Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1c152d95d | ||
|
|
dffac5deb8 | ||
|
|
8aa3bbfad3 | ||
|
|
5bb4006e55 | ||
|
|
e06a406682 | ||
|
|
a19719be4b | ||
|
|
35856a9f3d | ||
|
|
cf1d5d5ee2 | ||
|
|
f69a845e51 | ||
|
|
db67e93bb8 | ||
|
|
313f089222 | ||
|
|
458c08ded7 | ||
|
|
fe6cc7417f | ||
|
|
181c94a808 | ||
|
|
9ff6f02d8d | ||
|
|
3cf74516d9 | ||
|
|
31f5502c16 | ||
|
|
671d60bd72 | ||
|
|
294e866501 | ||
|
|
96789100e3 | ||
|
|
ea934fc7fb | ||
|
|
d529b7b340 | ||
|
|
092ca7f9f8 | ||
|
|
79f4bdb869 | ||
|
|
b0d2d0f7dc | ||
|
|
cdf58c1dfe | ||
|
|
4680de6022 | ||
|
|
e8f61181b6 | ||
|
|
75e9e007d0 | ||
|
|
3574ef7c0b | ||
|
|
86158036d2 | ||
|
|
e73beb47a3 | ||
|
|
0db5f1dd73 | ||
|
|
6dcdc2aa58 | ||
|
|
64601137e8 | ||
|
|
af7697b670 | ||
|
|
9e4f46088b | ||
|
|
91e71a9b9c | ||
|
|
1198ffb847 | ||
|
|
72116ecd0c | ||
|
|
04ebb7123d | ||
|
|
43096beafe | ||
|
|
2a08c62456 | ||
|
|
b62d74c3fa | ||
|
|
652fb5a6ad | ||
|
|
d1f25cc19f | ||
|
|
dc6a9d9761 | ||
|
|
838d725715 | ||
|
|
c45acb39ca | ||
|
|
957edf2a52 | ||
|
|
d969c6ed0e | ||
|
|
dd6fca961c | ||
|
|
e88a6df456 | ||
|
|
3a4530c999 | ||
|
|
b623dba40a | ||
|
|
e2fd945109 | ||
|
|
e6dc3cda31 | ||
|
|
18d8ff127a | ||
|
|
4a9526451f | ||
|
|
669d89c870 | ||
|
|
39161f2af5 | ||
|
|
be8c33e3fc | ||
|
|
7c1a27f9df | ||
|
|
342cc93dbf | ||
|
|
6f07cf439b | ||
|
|
1d4d00b69b | ||
|
|
54cb3e4d54 | ||
|
|
b0fc2caef9 | ||
|
|
1221509d24 | ||
|
|
17f5f60504 | ||
|
|
a8b0b488c6 | ||
|
|
6d922d5b70 | ||
|
|
1ab62a33e3 | ||
|
|
f9540a1e78 | ||
|
|
a720bf2c8b | ||
|
|
c143eab6fb | ||
|
|
1afb20af8b | ||
|
|
c6f27c839d | ||
|
|
742f1708c5 | ||
|
|
ac9601a48b | ||
|
|
4cd109a5fc | ||
|
|
078554a8b0 | ||
|
|
464e2f4f64 | ||
|
|
b84f55aa18 | ||
|
|
be2f780bfa | ||
|
|
262d8493a6 | ||
|
|
24e12e871b | ||
|
|
a2ee75339c | ||
|
|
9c28c4d63c | ||
|
|
7d57da1424 | ||
|
|
b77b20ba4d | ||
|
|
57ae58f093 | ||
|
|
b29c878688 | ||
|
|
5a5a6c76c1 | ||
|
|
f6a53f537c | ||
|
|
d86ed37208 | ||
|
|
92b945f28d | ||
|
|
cb164f0055 | ||
|
|
acfe37c1b5 | ||
|
|
44ded26df4 | ||
|
|
6dbd92a010 | ||
|
|
f729ca0ad2 | ||
|
|
065490c077 | ||
|
|
da6425dd93 | ||
|
|
250dcbc676 | ||
|
|
de4ca4a88f | ||
|
|
723a0c890e | ||
|
|
7085e7df20 | ||
|
|
09496d60f3 | ||
|
|
bf6bc26a5c | ||
|
|
a7217eae15 | ||
|
|
2a532ef9b7 | ||
|
|
c304ded924 | ||
|
|
4d8c47a505 | ||
|
|
d3e0ccbf34 | ||
|
|
3c82b46148 | ||
|
|
07d7f2cc17 | ||
|
|
54d4f09ab1 | ||
|
|
e8c698b5e8 | ||
|
|
b096906ffd | ||
|
|
5a9cec12fa | ||
|
|
a2ed71751f | ||
|
|
5a3a2df920 | ||
|
|
9d07e669ea | ||
|
|
aa18f1cc35 | ||
|
|
668d78ea64 | ||
|
|
6f62581d6e | ||
|
|
06686ada5c | ||
|
|
4380482606 | ||
|
|
d5112b3d32 | ||
|
|
ef67506640 | ||
|
|
c2ea4d2793 | ||
|
|
f0d8d479bd | ||
|
|
2d46d70c92 | ||
|
|
4320bb7cb4 | ||
|
|
398e8ed5ff | ||
|
|
4e8b804337 | ||
|
|
de7c664e0d | ||
|
|
c7600a2e3d | ||
|
|
8b34e82aab | ||
|
|
c96efe8802 | ||
|
|
0a1bfd3a2e | ||
|
|
755cf9ce43 | ||
|
|
c6aca74396 | ||
|
|
b5b7f3c16c | ||
|
|
293162b484 | ||
|
|
bd543b7b9d | ||
|
|
994fe9f482 | ||
|
|
f657bdb14c | ||
|
|
a4238e1191 | ||
|
|
daf08423b4 | ||
|
|
91367f3d3a | ||
|
|
9dbd52b638 | ||
|
|
22c6be2770 | ||
|
|
fb261c9f90 | ||
|
|
638e35bf90 | ||
|
|
25d13862ae | ||
|
|
1c8f483138 | ||
|
|
92ad215873 | ||
|
|
7ed3cfbb7c | ||
|
|
53e75a27be | ||
|
|
1b67819c3c | ||
|
|
07f8020f79 | ||
|
|
b12d0680d8 | ||
|
|
64247d913e | ||
|
|
3575cdae85 | ||
|
|
f71a542247 | ||
|
|
46cc59138b | ||
|
|
46bfa5112e | ||
|
|
fcdff51783 | ||
|
|
a4be4ae419 | ||
|
|
7083352b9a | ||
|
|
956a0f46c0 | ||
|
|
aea1864627 | ||
|
|
803b87111a | ||
|
|
f64a641b63 | ||
|
|
7f9bb354d7 | ||
|
|
4b7b6284f8 | ||
|
|
bbccd26415 | ||
|
|
bab7c737b3 | ||
|
|
3169787b41 | ||
|
|
f29bbc6657 | ||
|
|
f53cd5ae11 | ||
|
|
38d36b5ab5 | ||
|
|
8f8f596abc | ||
|
|
2afc8abdb2 | ||
|
|
38dd948306 | ||
|
|
e393cf0a10 | ||
|
|
4f451017e3 | ||
|
|
9cb31af48a | ||
|
|
2c17041b27 | ||
|
|
72c75a443e | ||
|
|
1ba6384647 | ||
|
|
e2edf9a37c | ||
|
|
cb0bfd095d | ||
|
|
ec03dd4755 | ||
|
|
de44f5ae42 | ||
|
|
5ee6796172 | ||
|
|
d034fca513 | ||
|
|
f65c3fb29a | ||
|
|
7192bc9d69 | ||
|
|
d7636a05f4 | ||
|
|
bd93301634 | ||
|
|
b0558ba0a2 | ||
|
|
251934bb4b | ||
|
|
f2c9ef5d30 | ||
|
|
15a072bbaa | ||
|
|
8f1c303862 | ||
|
|
c79d2e3e6e | ||
|
|
f45d8d860b | ||
|
|
88f31184a0 | ||
|
|
d5368baab4 | ||
|
|
10f645226c | ||
|
|
322c572cd6 | ||
|
|
fdfeb178b5 | ||
|
|
1c63cdba91 | ||
|
|
289d0db8e3 | ||
|
|
9adacca77f | ||
|
|
7dc6813274 | ||
|
|
77bf1cfbb9 | ||
|
|
8f28350579 | ||
|
|
2d06d0c1d3 | ||
|
|
7e8301afcd | ||
|
|
2600e8f856 | ||
|
|
914748e14e | ||
|
|
21a7b633e7 | ||
|
|
a77d4352e2 | ||
|
|
a7a21e0e51 | ||
|
|
a0a14cd285 | ||
|
|
44b7bedd53 | ||
|
|
409a0808c1 | ||
|
|
4214d02fad | ||
|
|
bc1abb1b58 | ||
|
|
2f2f53ba4d | ||
|
|
cbeb4c151d | ||
|
|
1d5ca70790 | ||
|
|
1f619ff4d3 | ||
|
|
f5acdebe60 | ||
|
|
bd59922204 | ||
|
|
c4d9482c79 | ||
|
|
8faef8f017 | ||
|
|
284161db77 | ||
|
|
78db2b66ef | ||
|
|
0d71643ecb | ||
|
|
9a7a105e7b | ||
|
|
f54d024d97 | ||
|
|
94338bd53f | ||
|
|
1a9ecc0313 | ||
|
|
4baeb7b945 | ||
|
|
baee479fb5 | ||
|
|
5ad4b2e9c4 | ||
|
|
26657cddfc | ||
|
|
947a922f8c | ||
|
|
a2be76184e | ||
|
|
8ddb733dc3 | ||
|
|
94e016129b | ||
|
|
5e653ded5d | ||
|
|
1bf8097c3f | ||
|
|
3d2f11ffd2 | ||
|
|
5e77a22d7a | ||
|
|
1dffcb1dc3 | ||
|
|
edc18cbafb | ||
|
|
2f23194432 | ||
|
|
20709472be | ||
|
|
7a8c6a0cec | ||
|
|
88558e40a7 | ||
|
|
79386b2b6b | ||
|
|
3aad8d3cb5 | ||
|
|
272a6f37ad | ||
|
|
e10081025d | ||
|
|
e03a6d8f11 | ||
|
|
98e7dc0321 | ||
|
|
74438c5d74 | ||
|
|
0c4e548565 | ||
|
|
cc70e01e96 | ||
|
|
7c635748af | ||
|
|
0c874ad56d | ||
|
|
2fb7f80b71 | ||
|
|
3f7c3414ce | ||
|
|
97165db44d | ||
|
|
82cdf04cf0 | ||
|
|
171d899609 | ||
|
|
6432278d5d | ||
|
|
ccaff12451 | ||
|
|
11347c080b | ||
|
|
553676c3f9 | ||
|
|
40b70e7418 | ||
|
|
0ea75019ae | ||
|
|
dbc092df77 | ||
|
|
68049f0dca | ||
|
|
9db5dfc175 | ||
|
|
039a316b3b | ||
|
|
f4b529f1ce | ||
|
|
9c6d606103 | ||
|
|
6cdc06aae7 | ||
|
|
2684654e7b | ||
|
|
25bcc33204 | ||
|
|
e5b3f6215b | ||
|
|
9306eadd90 | ||
|
|
a27fbb5b8b | ||
|
|
cd4c82b0b8 | ||
|
|
aa6179ee9e | ||
|
|
6594b8c2dc | ||
|
|
d4c2cdf003 | ||
|
|
2b529c37f5 | ||
|
|
8599cd925b | ||
|
|
89f37b4b9e | ||
|
|
f3136e85ee | ||
|
|
845743073c | ||
|
|
b2c7bacaf9 | ||
|
|
ed1673f032 | ||
|
|
bac9d443ea | ||
|
|
90e3868c08 | ||
|
|
c428572e01 | ||
|
|
884b5755ec | ||
|
|
32d7366795 | ||
|
|
21bf4a5450 | ||
|
|
8042b02cf5 | ||
|
|
ae7cdd3982 | ||
|
|
9ba945ad76 | ||
|
|
9cd04c3660 | ||
|
|
444bc19781 | ||
|
|
eb08c4ba04 | ||
|
|
5846e24a29 | ||
|
|
9c2c09e433 | ||
|
|
3c4329697b | ||
|
|
af9951d993 | ||
|
|
630f6df2f5 | ||
|
|
fcf07b8e7b | ||
|
|
4824c2f626 | ||
|
|
2b6b2e1b63 | ||
|
|
6926c1e57f | ||
|
|
6a00a7f1ca | ||
|
|
288c3d4c87 | ||
|
|
ac497bcc41 | ||
|
|
7b8d0d6845 | ||
|
|
a9a4589fae | ||
|
|
b8f972a774 | ||
|
|
305e388478 | ||
|
|
97e7848a78 | ||
|
|
d33058a924 | ||
|
|
eda3cc1f17 | ||
|
|
47043ad4ad | ||
|
|
acf20088cc | ||
|
|
374cb2766c | ||
|
|
31685b9ba5 | ||
|
|
722ff4ce52 | ||
|
|
161b375043 | ||
|
|
2c14191cde | ||
|
|
486546918f | ||
|
|
23d08e58a4 | ||
|
|
4c4c34ecaa | ||
|
|
bd3f2fd70a | ||
|
|
b80e42a635 | ||
|
|
9446a034d0 | ||
|
|
da0c6ab3fd | ||
|
|
c1adb27ee5 | ||
|
|
980d3027e3 | ||
|
|
53ba533f9b | ||
|
|
e40827da65 | ||
|
|
b9848b6a96 | ||
|
|
147593b11a | ||
|
|
21652f928b | ||
|
|
5b0352aff0 | ||
|
|
b2da2fa096 | ||
|
|
0cbb01a2a8 | ||
|
|
179840968d | ||
|
|
3b9c1649a1 | ||
|
|
5750c7dbe1 | ||
|
|
7ceca359be | ||
|
|
0f8152a6ed | ||
|
|
b3258bb020 | ||
|
|
4b2e705cbc | ||
|
|
31e41382f8 | ||
|
|
ecda7192dc | ||
|
|
9a48562b9f | ||
|
|
c181b58bc1 | ||
|
|
2624afa2ed | ||
|
|
3690af1e35 | ||
|
|
98966d3b38 | ||
|
|
41fdd16ce1 | ||
|
|
1bc94859c0 | ||
|
|
60f6f36437 | ||
|
|
82267ba16a | ||
|
|
14260deef8 | ||
|
|
8ba7342ccf | ||
|
|
f6b935d78e | ||
|
|
8cf3ae2091 | ||
|
|
e02b997521 | ||
|
|
229cef13c9 | ||
|
|
f503d6f896 | ||
|
|
d851166bb7 | ||
|
|
638a861350 | ||
|
|
20d7e4cbb0 | ||
|
|
36ccedd7c6 | ||
|
|
31854ba7a6 | ||
|
|
2639f07f5c | ||
|
|
b6e13f557f | ||
|
|
6333d90059 | ||
|
|
fea5d0ec56 | ||
|
|
c77f02d1fd | ||
|
|
c498965cae | ||
|
|
cdaa77071d | ||
|
|
6a72678c8d | ||
|
|
bb67fc622f | ||
|
|
5b50c02d93 | ||
|
|
ffec7e3266 | ||
|
|
039c1e7027 | ||
|
|
b27a21b3b8 | ||
|
|
2bcb5f617e | ||
|
|
7bf5294f9f | ||
|
|
c2fd1940f0 | ||
|
|
2d095ce6db | ||
|
|
e88bc36fca | ||
|
|
5613ea4f77 | ||
|
|
9c41599d60 | ||
|
|
5f8dd869e0 | ||
|
|
fb17df0b7e |
No files matched your search
@@ -1,28 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report a bug
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Server version**
|
||||
|
||||
|
||||
**Plugin version** ("latest" is not a version)
|
||||
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
|
||||
**How to reproduce**
|
||||
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
|
||||
**Actual behavior**
|
||||
|
||||
|
||||
**Additional info** (errors, screenshots, ...)
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Bug Report
|
||||
description: Unwanted behavior that does not match configuration.
|
||||
labels: ["Bug"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Server version
|
||||
description: Version and type of used server software where TAB is installed.
|
||||
placeholder: Spigot 1.18.1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: TAB version
|
||||
description: Full version in numbers, "latest" is not a version.
|
||||
placeholder: 3.0.2
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: How the plugin behaves
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: How the plugin is supposed to behave instead with your configuration.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: What to do in order to reproduce this issue
|
||||
placeholder: |
|
||||
Step 1: ..
|
||||
Step 2: ..
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Any other info you want to provide, such as screenshots.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Let's make sure this report is valid
|
||||
options:
|
||||
- label: I am running latest version of the plugin
|
||||
required: true
|
||||
- label: I have read the wiki to make sure it's not an issue with configuration
|
||||
required: true
|
||||
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
|
||||
required: true
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Error
|
||||
description: Error message with a stack trace in console or error file.
|
||||
labels: ["Error"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Server version
|
||||
description: Version and type of used server software where TAB is installed.
|
||||
placeholder: Spigot 1.18.1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: TAB version
|
||||
description: Full version in numbers, "latest" is not a version.
|
||||
placeholder: 3.0.2
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Stack trace
|
||||
description: The error message and stack trace you received in console or errors.log file.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce (if known)
|
||||
description: What to do in order to reproduce this issue
|
||||
placeholder: |
|
||||
Step 1: ..
|
||||
Step 2: ..
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Any other info you want to provide.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Let's make sure this report is valid
|
||||
options:
|
||||
- label: I am running latest version of the plugin
|
||||
required: true
|
||||
- label: I have included a paste of the error
|
||||
required: true
|
||||
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
|
||||
required: true
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Wiki change
|
||||
description: Change to text on a wiki page or adding a new section.
|
||||
labels: ["Wiki"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Page link
|
||||
description: Link to the page that should receive the change.
|
||||
placeholder: https://github.com/NEZNAMY/TAB/wiki/Installation
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current text (if applicable)
|
||||
description: Current version of the text (if modifying), ignore in case of new section.
|
||||
placeholder: |
|
||||
Installation on Bukkit/Spigot/Paper is as simple as it can be. Just put the plugin into plugins folder and restart the server. You can also run `/plugman load tab` to load the plugin while server is running.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: New text
|
||||
description: What the text should be changed to / how should new section look
|
||||
placeholder: |
|
||||
Put the plugin into plugins folder and restart the server. In case you don't want to restart your server, loading with plugins like PlugMan is supported.
|
||||
validations:
|
||||
required: true
|
||||
@@ -11,15 +11,16 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 11
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
@@ -37,3 +38,8 @@ jobs:
|
||||
env:
|
||||
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
|
||||
with:
|
||||
name: Jar
|
||||
path: jar/build/libs/TAB-**.jar
|
||||
@@ -1,201 +0,0 @@
|
||||
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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,18 +1,47 @@
|
||||
# About
|
||||
This is where you can download the plugin for free without needing to buy me a coffee or just grab some code inspiration for your plugin. God bless opensource.
|
||||
|
||||
# Compiling from source
|
||||
TAB aims to be a superior all-in-one minecraft plugin for displaying information that outperforms all
|
||||
similar plugins in terms of features, performance, compatibility and support.
|
||||
More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/Why-TAB%3F) wiki page.
|
||||
|
||||
# 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.
|
||||
|
||||
# Compiling
|
||||
Compilation requires JDK 8 and up.
|
||||
To compile the plugin, run `./gradlew build` from the terminal.
|
||||
Once the plugin compiles, you can grab the jar from `jar/build/libs/` folder.
|
||||
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
|
||||
|
||||
# Links
|
||||
See [releases](https://github.com/NEZNAMY/TAB/releases) for precompiled jar.
|
||||
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/
|
||||
|
||||
Has the plugin helped you in any way, and you wish to support the development?
|
||||
You can purchase the plugin on [MC-Market](https://www.mc-market.org/resources/20631/), [Polymart](https://polymart.org/resource/484) or just [send a donation](https://paypal.me/neznamy1).
|
||||
|
||||
Supporters have access to high-quality [discord support](https://discord.gg/EaSvdk6).
|
||||
Supporting my work:
|
||||
* MC-Market: https://www.mc-market.org/resources/14009/
|
||||
* Polymart: https://polymart.org/resource/484
|
||||
|
||||
Want to know more about the plugin's functions? Check out the [wiki](https://github.com/NEZNAMY/TAB/wiki).
|
||||
|
||||
Other links:
|
||||
* Discord support for buyers: 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.
|
||||
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.
|
||||
|
||||
# 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.
|
||||
@@ -2,81 +2,96 @@ package me.neznamy.tab.api;
|
||||
|
||||
public interface ArmorStand {
|
||||
|
||||
/**
|
||||
* Returns true if offset is static, false if not
|
||||
* @return true if static, false if dynamic
|
||||
*/
|
||||
boolean hasStaticOffset();
|
||||
|
||||
/**
|
||||
* Changes offset of the armor stand
|
||||
* @param offset - new offset
|
||||
*/
|
||||
void setOffset(double offset);
|
||||
/**
|
||||
* Returns true if offset is static, false if not
|
||||
*
|
||||
* @return true if static, false if dynamic
|
||||
*/
|
||||
boolean hasStaticOffset();
|
||||
|
||||
/**
|
||||
* Changes offset of the armor stand
|
||||
*
|
||||
* @param offset
|
||||
* new offset
|
||||
*/
|
||||
void setOffset(double offset);
|
||||
|
||||
/**
|
||||
* Returns offset of the armor stand
|
||||
* @return offset from base
|
||||
*/
|
||||
double getOffset();
|
||||
|
||||
/**
|
||||
* Returns property for armor stand's name
|
||||
* @return property for armor stand's name
|
||||
*/
|
||||
Property getProperty();
|
||||
|
||||
/**
|
||||
* Teleports armor stand to owner's current location for everyone in range
|
||||
*/
|
||||
void teleport();
|
||||
|
||||
/**
|
||||
* Teleports armor stand to owner's current location for specified player
|
||||
* @param viewer - player to send packet to
|
||||
*/
|
||||
void teleport(TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Changes sneaking flag and sends packets to everyone in range
|
||||
* @param sneaking
|
||||
* new sneaking status
|
||||
*/
|
||||
void sneak(boolean sneaking);
|
||||
|
||||
/**
|
||||
* DeSpawns armor stand for everyone
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* DeSpawns armor stand for specified player
|
||||
* @param viewer - player to send packet to
|
||||
*/
|
||||
void destroy(TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Updates armor stand's name if needed
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
/**
|
||||
* Updates visibility if needed
|
||||
* @param force - if refresh should be forced
|
||||
*/
|
||||
void updateVisibility(boolean force);
|
||||
|
||||
/**
|
||||
* Returns entity ID of this armor stand
|
||||
* @return entity ID of this armor stand
|
||||
*/
|
||||
int getEntityId();
|
||||
|
||||
/**
|
||||
* Spawns armor stand for specified player
|
||||
* @param viewer - player to spawn for
|
||||
*/
|
||||
void spawn(TabPlayer viewer);
|
||||
|
||||
void respawn(TabPlayer viewer);
|
||||
/**
|
||||
* Returns offset of the armor stand
|
||||
*
|
||||
* @return offset from base
|
||||
*/
|
||||
double getOffset();
|
||||
|
||||
/**
|
||||
* Returns property for armor stand's name
|
||||
*
|
||||
* @return property for armor stand's name
|
||||
*/
|
||||
Property getProperty();
|
||||
|
||||
/**
|
||||
* Teleports armor stand to owner's current location for everyone in range
|
||||
*/
|
||||
void teleport();
|
||||
|
||||
/**
|
||||
* Teleports armor stand to owner's current location for specified player
|
||||
*
|
||||
* @param viewer
|
||||
* player to send packet to
|
||||
*/
|
||||
void teleport(TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Changes sneaking flag and sends packets to everyone in range
|
||||
*
|
||||
* @param sneaking
|
||||
* new sneaking status
|
||||
*/
|
||||
void sneak(boolean sneaking);
|
||||
|
||||
/**
|
||||
* DeSpawns armor stand for everyone
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* DeSpawns armor stand for specified player
|
||||
*
|
||||
* @param viewer
|
||||
* player to send packet to
|
||||
*/
|
||||
void destroy(TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Updates armor stand's name if needed
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
/**
|
||||
* Updates visibility if needed
|
||||
*
|
||||
* @param force
|
||||
* if refresh should be forced
|
||||
*/
|
||||
void updateVisibility(boolean force);
|
||||
|
||||
/**
|
||||
* Returns entity ID of this armor stand
|
||||
*
|
||||
* @return entity ID of this armor stand
|
||||
*/
|
||||
int getEntityId();
|
||||
|
||||
/**
|
||||
* Spawns armor stand for specified player
|
||||
*
|
||||
* @param viewer
|
||||
* player to spawn for
|
||||
*/
|
||||
void spawn(TabPlayer viewer);
|
||||
|
||||
void respawn(TabPlayer viewer);
|
||||
}
|
||||
@@ -1,178 +1,14 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A helper class for easy management of armor stands of a player
|
||||
*/
|
||||
public class ArmorStandManager {
|
||||
public interface ArmorStandManager {
|
||||
|
||||
//map of registered armor stands
|
||||
private final Map<String, ArmorStand> armorStands = new LinkedHashMap<>();
|
||||
|
||||
//players in entity tracking range
|
||||
private final List<TabPlayer> nearbyPlayers = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
//array to iterate over to avoid concurrent modification and slightly boost performance & memory
|
||||
private ArmorStand[] armorStandArray = new ArmorStand[0];
|
||||
|
||||
private TabPlayer[] nearbyPlayerArray = new TabPlayer[0];
|
||||
|
||||
/**
|
||||
* Adds armor stand into list
|
||||
* @param name - key name of the armor stand
|
||||
* @param as - armor stand to add
|
||||
*/
|
||||
public synchronized void addArmorStand(String name, ArmorStand as) {
|
||||
armorStands.put(name, as);
|
||||
armorStandArray = armorStands.values().toArray(new ArmorStand[0]);
|
||||
for (TabPlayer p : nearbyPlayerArray) as.spawn(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns armor stand with given name or null if not present
|
||||
* @param name - name of armor stand registered with
|
||||
* @return armor stand with given name
|
||||
*/
|
||||
public ArmorStand getArmorStand(String name) {
|
||||
return armorStands.get(name);
|
||||
}
|
||||
/**
|
||||
* Sends destroy packet of all armor stands to everyone and clears nearby players list
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Removes armor stand from list. It is not automatically destroyed for players
|
||||
* @param name - name of armor stand to remove
|
||||
*/
|
||||
public void removeArmorStand(String name) {
|
||||
armorStands.get(name).destroy();
|
||||
armorStands.remove(name);
|
||||
armorStandArray = armorStands.values().toArray(new ArmorStand[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns all armor stands for specified viewer and adds them into nearby players
|
||||
* @param viewer - player to spawn armor stands for
|
||||
*/
|
||||
public void spawn(TabPlayer viewer) {
|
||||
synchronized (nearbyPlayers) {
|
||||
nearbyPlayers.add(viewer);
|
||||
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
|
||||
}
|
||||
if (viewer.getVersion().getMinorVersion() < 8) return;
|
||||
for (ArmorStand a : armorStandArray) a.spawn(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sneak value of armor stands to specified value
|
||||
* @param sneaking - new sneaking status
|
||||
*/
|
||||
public void sneak(boolean sneaking) {
|
||||
for (ArmorStand a : armorStandArray) a.sneak(sneaking);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports armor stands to player's current location for all nearby players
|
||||
*/
|
||||
public void teleport() {
|
||||
for (ArmorStand a : armorStandArray) a.teleport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports armor stands to player's current location for specified viewer
|
||||
* @param viewer - player to teleport armor stands for
|
||||
*/
|
||||
public void teleport(TabPlayer viewer) {
|
||||
for (ArmorStand a : armorStandArray) a.teleport(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes name of all armor stands for all nearby players
|
||||
*/
|
||||
public void refresh() {
|
||||
for (ArmorStand a : armorStandArray) a.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates armor stand visibility for all armor stands
|
||||
* @param force - true if packets should be sent despite seemingly not needed
|
||||
*/
|
||||
public void updateVisibility(boolean force) {
|
||||
for (ArmorStand a : armorStandArray) a.updateVisibility(force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes specified player from list of nearby players
|
||||
* @param viewer - player to remove
|
||||
*/
|
||||
public void unregisterPlayer(TabPlayer viewer) {
|
||||
synchronized (nearbyPlayers) {
|
||||
nearbyPlayers.remove(viewer);
|
||||
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends destroy packet of all armor stands to everyone and clears nearby players list
|
||||
*/
|
||||
public void destroy() {
|
||||
for (ArmorStand a : armorStandArray) a.destroy();
|
||||
synchronized (nearbyPlayers) {
|
||||
nearbyPlayers.clear();
|
||||
nearbyPlayerArray = new TabPlayer[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends destroy packet of all armor stands to specified player and removes them from nearby players list
|
||||
* @param viewer - player to destroy armor stands for
|
||||
*/
|
||||
public void destroy(TabPlayer viewer) {
|
||||
for (ArmorStand a : armorStandArray) a.destroy(viewer);
|
||||
unregisterPlayer(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if manager contains armor stand with specified entity id, false if not
|
||||
* @param entityId - entity id
|
||||
* @return true if armor stand with specified entity id exists, false if not
|
||||
*/
|
||||
public boolean hasArmorStandWithID(int entityId) {
|
||||
for (ArmorStand a : armorStandArray) {
|
||||
if (a.getEntityId() == entityId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of all registered armor stands
|
||||
* @return armor stand array
|
||||
*/
|
||||
public ArmorStand[] getArmorStands(){
|
||||
return armorStandArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of nearby players
|
||||
* @return array of nearby players
|
||||
*/
|
||||
public TabPlayer[] getNearbyPlayers(){
|
||||
return nearbyPlayerArray;
|
||||
}
|
||||
|
||||
public boolean isNearby(TabPlayer viewer) {
|
||||
return nearbyPlayers.contains(viewer);
|
||||
}
|
||||
|
||||
public void respawn() {
|
||||
for (ArmorStand a : armorStandArray) {
|
||||
for (TabPlayer viewer : nearbyPlayerArray) {
|
||||
a.respawn(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
void refresh(boolean force);
|
||||
}
|
||||
@@ -1,35 +1,44 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
/**
|
||||
* Feature manager allows registration and work with features.
|
||||
*/
|
||||
public interface FeatureManager {
|
||||
|
||||
/**
|
||||
* Registers a feature, which will start receiving events
|
||||
*
|
||||
* @param name - name of feature
|
||||
* @param feature - the handler
|
||||
*/
|
||||
void registerFeature(String name, TabFeature feature);
|
||||
|
||||
/**
|
||||
* Unregisters feature making it no longer receive events. This does not run unload method nor cancel
|
||||
* tasks created by the feature
|
||||
* @param name - feature name defined in registerFeature
|
||||
*/
|
||||
void unregisterFeature(String name);
|
||||
|
||||
/**
|
||||
* Returns whether a feature with said name is registered or not
|
||||
*
|
||||
* @param name - name of feature defined in registerFeature method
|
||||
* @return true if feature exists, false if not
|
||||
*/
|
||||
boolean isFeatureEnabled(String name);
|
||||
/**
|
||||
* Registers a feature, which will start receiving events
|
||||
*
|
||||
* @param name
|
||||
* name of feature
|
||||
* @param feature
|
||||
* the handler
|
||||
*/
|
||||
void registerFeature(String name, TabFeature feature);
|
||||
|
||||
/**
|
||||
* Unregisters feature making it no longer receive events. This does not run unload method nor cancel
|
||||
* tasks created by the feature
|
||||
*
|
||||
* @param name
|
||||
* feature name defined in registerFeature
|
||||
*/
|
||||
void unregisterFeature(String name);
|
||||
|
||||
/**
|
||||
* Returns whether a feature with said name is registered or not
|
||||
*
|
||||
* @param name
|
||||
* name of feature defined in registerFeature method
|
||||
* @return true if feature exists, false if not
|
||||
*/
|
||||
boolean isFeatureEnabled(String name);
|
||||
|
||||
/**
|
||||
* Returns feature handler by its name
|
||||
*
|
||||
* @param name - name of feature defined in registerFeature method
|
||||
* @return the feature or null if feature does not exist
|
||||
*/
|
||||
TabFeature getFeature(String name);
|
||||
/**
|
||||
* Returns feature handler by its name
|
||||
*
|
||||
* @param name
|
||||
* name of feature defined in registerFeature method
|
||||
* @return the feature or null if feature does not exist
|
||||
*/
|
||||
TabFeature getFeature(String name);
|
||||
}
|
||||
@@ -2,15 +2,15 @@ package me.neznamy.tab.api;
|
||||
|
||||
public interface HeaderFooterManager {
|
||||
|
||||
void setHeader(TabPlayer player, String header);
|
||||
|
||||
void setFooter(TabPlayer player, String footer);
|
||||
|
||||
void setHeaderAndFooter(TabPlayer player, String header, String footer);
|
||||
|
||||
void resetHeader(TabPlayer player);
|
||||
|
||||
void resetFooter(TabPlayer player);
|
||||
|
||||
void resetHeaderAndFooter(TabPlayer player);
|
||||
void setHeader(TabPlayer player, String header);
|
||||
|
||||
void setFooter(TabPlayer player, String footer);
|
||||
|
||||
void setHeaderAndFooter(TabPlayer player, String header, String footer);
|
||||
|
||||
void resetHeader(TabPlayer player);
|
||||
|
||||
void resetFooter(TabPlayer player);
|
||||
|
||||
void resetHeaderAndFooter(TabPlayer player);
|
||||
}
|
||||
@@ -1,60 +1,68 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
/**
|
||||
* An interface representing a property assigned to a player,
|
||||
* which may contain placeholders and can be changed using the API.
|
||||
*/
|
||||
public interface Property {
|
||||
|
||||
/**
|
||||
* Changes raw value to new one
|
||||
* @param rawValue - new value to be used
|
||||
*/
|
||||
void changeRawValue(String rawValue);
|
||||
/**
|
||||
* Returns original raw value ignoring API calls
|
||||
*
|
||||
* @return original raw value
|
||||
*/
|
||||
String getOriginalRawValue();
|
||||
|
||||
/**
|
||||
* Returns original raw value ignoring API calls
|
||||
* @return original raw value
|
||||
*/
|
||||
String getOriginalRawValue();
|
||||
/**
|
||||
* Temporarily overrides current raw value with an API call
|
||||
*
|
||||
* @param temporaryValue
|
||||
* temporary value to be assigned
|
||||
*/
|
||||
void setTemporaryValue(String temporaryValue);
|
||||
|
||||
/**
|
||||
* Temporarily overrides current raw value with an API call
|
||||
* @param temporaryValue - temporary value to be assigned
|
||||
*/
|
||||
void setTemporaryValue(String temporaryValue);
|
||||
|
||||
/**
|
||||
* Returns current temporary value
|
||||
* @return temporary value or null if not set
|
||||
*/
|
||||
String getTemporaryValue();
|
||||
/**
|
||||
* Returns current temporary value
|
||||
*
|
||||
* @return temporary value or null if not set
|
||||
*/
|
||||
String getTemporaryValue();
|
||||
|
||||
/**
|
||||
* Returns temporary value (via API) if present, raw value otherwise
|
||||
* @return current raw value
|
||||
*/
|
||||
String getCurrentRawValue();
|
||||
/**
|
||||
* Returns temporary value (via API) if present, raw value otherwise
|
||||
*
|
||||
* @return current raw value
|
||||
*/
|
||||
String getCurrentRawValue();
|
||||
|
||||
/**
|
||||
* Returns last known value
|
||||
* @return last known value
|
||||
*/
|
||||
String get();
|
||||
/**
|
||||
* Returns last known value
|
||||
*
|
||||
* @return last known value
|
||||
*/
|
||||
String get();
|
||||
|
||||
/**
|
||||
* Replaces all placeholders in current raw value, colorizes it, removes remove-strings and returns whether value changed or not
|
||||
* @return if updating changed value or not
|
||||
*/
|
||||
boolean update();
|
||||
|
||||
/**
|
||||
* Replaces all placeholders in current raw value, colorizes it, removes remove-strings and returns it.
|
||||
* Equal to calling update() and then get().
|
||||
* @return updated value
|
||||
*/
|
||||
String updateAndGet();
|
||||
/**
|
||||
* Replaces all placeholders in current raw value, colorizes it, removes remove-strings and returns whether value changed or not
|
||||
*
|
||||
* @return if updating changed value or not
|
||||
*/
|
||||
boolean update();
|
||||
|
||||
/**
|
||||
* Returns value for defined viewer by applying relational placeholders to last known value
|
||||
* @param viewer - the viewer
|
||||
* @return format for the viewer
|
||||
*/
|
||||
String getFormat(TabPlayer viewer);
|
||||
/**
|
||||
* Replaces all placeholders in current raw value, colorizes it, removes remove-strings and returns it.
|
||||
* Equal to calling update() and then get().
|
||||
*
|
||||
* @return updated value
|
||||
*/
|
||||
String updateAndGet();
|
||||
|
||||
/**
|
||||
* Returns value for defined viewer by applying relational placeholders to last known value
|
||||
*
|
||||
* @param viewer
|
||||
* the viewer
|
||||
* @return format for the viewer
|
||||
*/
|
||||
String getFormat(TabPlayer viewer);
|
||||
}
|
||||
@@ -1,10 +1,147 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Interface representing user or group configuration.
|
||||
* Implementation is either using file or MySQL, if it's enabled.
|
||||
*/
|
||||
public interface PropertyConfiguration {
|
||||
|
||||
void setProperty(String name, String property, String server, String world, String value);
|
||||
|
||||
String[] getProperty(String name, String property, String server, String world);
|
||||
|
||||
void remove(String name);
|
||||
/**
|
||||
* Sets property value of group or user to specified value. If {@code world} or
|
||||
* {@code server} is not {@code null}, value will be inserted as a per-world or
|
||||
* per-server setting. If both {@code world} and {@code server} are {@code null},
|
||||
* value is inserted as a global setting.
|
||||
*
|
||||
* @param groupOrUser
|
||||
* Name of group or user, depending on what this instance handles
|
||||
* @param property
|
||||
* Name of property to set
|
||||
* @param server
|
||||
* Name of server to apply setting to, {@code null} for global setting
|
||||
* @param world
|
||||
* Name of world to apply setting to, {@code null} for global setting
|
||||
* @param value
|
||||
* Value of the property
|
||||
*/
|
||||
void setProperty(String groupOrUser, String property, String server, String world, String value);
|
||||
|
||||
/**
|
||||
* Gets property of group or user. If {@code server} or {@code world}
|
||||
* are not {@code null}, per-server / per-world settings are checked first
|
||||
* to try to find a match. If no match is found, global value is returned.
|
||||
* Returned value is an array with 2 elements, with first argument being value
|
||||
* and second being source. If nothing is found, empty array is returned.
|
||||
*
|
||||
* @param groupOrUser
|
||||
* Name of group or user, depending on what this instance handles
|
||||
* @param property
|
||||
* Name of property to get
|
||||
* @param server
|
||||
* Server the player is currently in, to allow check for per-server settings
|
||||
* @param world
|
||||
* World the player is currently in, to allow check for per-world settings
|
||||
* @return Array with 2 elements with value being first, source second if found,
|
||||
* empty array if nothing was found.
|
||||
*/
|
||||
String[] getProperty(String groupOrUser, String property, String server, String world);
|
||||
|
||||
/**
|
||||
* Removes all data applied to specified group or user.
|
||||
*
|
||||
* @param groupOrUser
|
||||
* Name of group or user, depending on what this instance handles
|
||||
*/
|
||||
void remove(String groupOrUser);
|
||||
|
||||
/**
|
||||
* Returns map of global settings applied to specified group or user.
|
||||
* Map key is property name, value is property value.
|
||||
*
|
||||
* @param groupOrUser
|
||||
* Name of group or user, depending on what this instance handles
|
||||
* @return Map of global settings of specified group or user
|
||||
*/
|
||||
Map<String, Object> getGlobalSettings(String groupOrUser);
|
||||
|
||||
/**
|
||||
* Returns map of per-world settings of specified group or user.
|
||||
* Map key is world name, value is property name-value map in that world.
|
||||
*
|
||||
* @param groupOrUser
|
||||
* Name of group or user, depending on what this instance handles
|
||||
* @return Map of per-world settings of specified group or user
|
||||
*/
|
||||
Map<String, Map<String, Object>> getPerWorldSettings(String groupOrUser);
|
||||
|
||||
/**
|
||||
* Returns map of per-server settings of specified group or user.
|
||||
* Map key is server name, value is property name-value map in that server.
|
||||
*
|
||||
* @param groupOrUser
|
||||
* Name of group or user, depending on what this instance handles
|
||||
* @return Map of per-server settings of specified group or user
|
||||
*/
|
||||
Map<String, Map<String, Object>> getPerServerSettings(String groupOrUser);
|
||||
|
||||
/**
|
||||
* Returns set of all groups or users that have anything configured,
|
||||
* either globally or in some world or server.
|
||||
*
|
||||
* @return Set of all entries with something configured
|
||||
*/
|
||||
Set<String> getAllEntries();
|
||||
|
||||
/**
|
||||
* Converts per-world or per-server map into a per-world or per-server map
|
||||
* with values only belonging to entered group or user. In other words, extracts
|
||||
* settings belonging to group or user from the map.
|
||||
*
|
||||
* @param map
|
||||
* Original per-world or per-server setting map
|
||||
* @param groupOrUser
|
||||
* Name of group or user, depending on what this instance handles
|
||||
* @return Converted map only containing data of specified group or user
|
||||
*/
|
||||
default Map<String, Map<String, Object>> convertMap(Map<String, Map<String, Map<String, Object>>> map, String groupOrUser) {
|
||||
Map<String, Map<String, Object>> converted = new HashMap<>();
|
||||
for (Map.Entry<String, Map<String, Map<String, Object>>> entry : map.entrySet()) {
|
||||
converted.put(entry.getKey(), entry.getValue().get(groupOrUser));
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts object into string. For most objects, {@link Object#toString()} is called.
|
||||
* For lists, entries are connected with {@code "\n"}.
|
||||
*
|
||||
* @param obj
|
||||
* Object to convert
|
||||
* @return Converted string
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default String toString(Object obj) {
|
||||
if (obj instanceof List) {
|
||||
return ((List<Object>)obj).stream().map(Object::toString).collect(Collectors.joining("\n"));
|
||||
}
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string into object. If string contains {@code "\n"},
|
||||
* a list is created using {@code "\n"} as a separator. Otherwise,
|
||||
* inserted string is returned.
|
||||
*
|
||||
* @param string
|
||||
* String to convert
|
||||
* @return Converted object
|
||||
*/
|
||||
default Object fromString(String string) {
|
||||
if (string != null && string.contains("\n")) {
|
||||
return Arrays.asList(string.split("\n"));
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
@@ -5,137 +5,165 @@ package me.neznamy.tab.api;
|
||||
*/
|
||||
public enum ProtocolVersion {
|
||||
|
||||
UNKNOWN ("Unknown"),
|
||||
PROXY ("Proxy"),
|
||||
V1_18_1 (757),
|
||||
V1_18 (757),
|
||||
V1_17_1 (756),
|
||||
V1_17 (755),
|
||||
V1_16_5 (754),
|
||||
V1_16_4 (754),
|
||||
V1_16_3 (753),
|
||||
V1_16_2 (751),
|
||||
V1_16_1 (736),
|
||||
V1_16 (735),
|
||||
V1_15_2 (578),
|
||||
V1_15_1 (575),
|
||||
V1_15 (573),
|
||||
V1_14_4 (498),
|
||||
V1_14_3 (490),
|
||||
V1_14_2 (485),
|
||||
V1_14_1 (480),
|
||||
V1_14 (477),
|
||||
V1_13_2 (404),
|
||||
V1_13_1 (401),
|
||||
V1_13 (393),
|
||||
V1_12_2 (340),
|
||||
V1_12_1 (338),
|
||||
V1_12 (335),
|
||||
V1_11_2 (316),
|
||||
V1_11_1 (316),
|
||||
V1_11 (315),
|
||||
V1_10_2 (210),
|
||||
V1_10_1 (210),
|
||||
V1_10 (210),
|
||||
V1_9_4 (110),
|
||||
V1_9_3 (110),
|
||||
V1_9_2 (109),
|
||||
V1_9_1 (108),
|
||||
V1_9 (107),
|
||||
V1_8 (47),
|
||||
V1_7_10 (5),
|
||||
V1_7_9 (5),
|
||||
V1_7_8 (5),
|
||||
V1_7_7 (5),
|
||||
V1_7_6 (5),
|
||||
V1_7_5 (4),
|
||||
V1_7_4 (4),
|
||||
V1_7_2 (4),
|
||||
V1_6_4 (78),
|
||||
V1_6_2 (74),
|
||||
V1_6_1 (73),
|
||||
V1_5_2 (61),
|
||||
V1_5_1 (60),
|
||||
V1_5 (60),
|
||||
V1_4_7 (51),
|
||||
V1_4_6 (51);
|
||||
UNKNOWN_SERVER_VERSION ("Unknown"),
|
||||
UNKNOWN_CLIENT_VERSION ("Unknown"),
|
||||
PROXY ("Proxy"),
|
||||
V1_18_2 (758),
|
||||
V1_18_1 (757),
|
||||
V1_18 (757),
|
||||
V1_17_1 (756),
|
||||
V1_17 (755),
|
||||
V1_16_5 (754),
|
||||
V1_16_4 (754),
|
||||
V1_16_3 (753),
|
||||
V1_16_2 (751),
|
||||
V1_16_1 (736),
|
||||
V1_16 (735),
|
||||
V1_15_2 (578),
|
||||
V1_15_1 (575),
|
||||
V1_15 (573),
|
||||
V1_14_4 (498),
|
||||
V1_14_3 (490),
|
||||
V1_14_2 (485),
|
||||
V1_14_1 (480),
|
||||
V1_14 (477),
|
||||
V1_13_2 (404),
|
||||
V1_13_1 (401),
|
||||
V1_13 (393),
|
||||
V1_12_2 (340),
|
||||
V1_12_1 (338),
|
||||
V1_12 (335),
|
||||
V1_11_2 (316),
|
||||
V1_11_1 (316),
|
||||
V1_11 (315),
|
||||
V1_10_2 (210),
|
||||
V1_10_1 (210),
|
||||
V1_10 (210),
|
||||
V1_9_4 (110),
|
||||
V1_9_3 (110),
|
||||
V1_9_2 (109),
|
||||
V1_9_1 (108),
|
||||
V1_9 (107),
|
||||
V1_8 (47),
|
||||
V1_7_10 (5),
|
||||
V1_7_9 (5),
|
||||
V1_7_8 (5),
|
||||
V1_7_7 (5),
|
||||
V1_7_6 (5),
|
||||
V1_7_5 (4),
|
||||
V1_7_4 (4),
|
||||
V1_7_2 (4),
|
||||
V1_6_4 (78),
|
||||
V1_6_2 (74),
|
||||
V1_6_1 (73),
|
||||
V1_5_2 (61),
|
||||
V1_5_1 (60),
|
||||
V1_5 (60),
|
||||
V1_4_7 (51),
|
||||
V1_4_6 (51);
|
||||
|
||||
//version's network id found at https://wiki.vg/Protocol_version_numbers
|
||||
private final int networkId;
|
||||
|
||||
//minor version, such as 16
|
||||
private final int minorVersion;
|
||||
|
||||
//friendly name displayed in %player-version% placeholder
|
||||
private final String friendlyName;
|
||||
/** Value array to iterate over to avoid array creations on each call */
|
||||
public static final ProtocolVersion[] VALUES = values();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given network id
|
||||
* @param networkId - network id of this version
|
||||
*/
|
||||
ProtocolVersion(int networkId){
|
||||
this.networkId = networkId;
|
||||
this.minorVersion = Integer.parseInt(toString().split("_")[1]);
|
||||
this.friendlyName = toString().substring(1).replace("_", ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given friendly name
|
||||
* @param friendlyName - friendly name to display
|
||||
*/
|
||||
ProtocolVersion(String friendlyName){
|
||||
this.networkId = 999;
|
||||
this.minorVersion = 18;
|
||||
this.friendlyName = friendlyName;
|
||||
}
|
||||
/** Version's network id found at https://wiki.vg/Protocol_version_numbers */
|
||||
private final int networkId;
|
||||
|
||||
/**
|
||||
* Returns the version's network id
|
||||
* @return version's network id
|
||||
*/
|
||||
public int getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
/** Version's minor version, such as 16 for 1.16.x */
|
||||
private int minorVersion;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/** Version's friendly name displayed in %player-version% placeholder */
|
||||
private final String 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;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given network id
|
||||
*
|
||||
* @param networkId
|
||||
* network id of this version
|
||||
*/
|
||||
ProtocolVersion(int networkId){
|
||||
this.networkId = networkId;
|
||||
this.minorVersion = Integer.parseInt(toString().split("_")[1]);
|
||||
this.friendlyName = toString().substring(1).replace("_", ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum constant of entered version or UNKNOWN if unknown version
|
||||
* @param friendlyName - friendly name of the version
|
||||
* @return version or UNKNOWN if version is unknown
|
||||
*/
|
||||
public static ProtocolVersion fromFriendlyName(String friendlyName) {
|
||||
if (friendlyName.startsWith("1.8")) return V1_8;
|
||||
try {
|
||||
return valueOf("V" + friendlyName.replace(".", "_"));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given friendly name
|
||||
*
|
||||
* @param friendlyName
|
||||
* friendly name to display
|
||||
*/
|
||||
ProtocolVersion(String friendlyName){
|
||||
this.networkId = 999;
|
||||
this.minorVersion = 18;
|
||||
this.friendlyName = friendlyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns version from given network id
|
||||
* @param networkId - network id of protocol version
|
||||
* @return version from given network id
|
||||
*/
|
||||
public static ProtocolVersion fromNetworkId(int networkId) {
|
||||
for (ProtocolVersion v : values()) {
|
||||
if (networkId == v.getNetworkId()) return v;
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param friendlyName
|
||||
* friendly name of the version
|
||||
* @return version or UNKNOWN_SERVER_VERSION if version is unknown
|
||||
*/
|
||||
public static ProtocolVersion fromFriendlyName(String friendlyName) {
|
||||
if (friendlyName.startsWith("1.8")) return V1_8;
|
||||
try {
|
||||
return valueOf("V" + friendlyName.replace(".", "_"));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return UNKNOWN_SERVER_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns version from given network id
|
||||
*
|
||||
* @param networkId
|
||||
* network id of protocol version
|
||||
* @return version from given network id
|
||||
*/
|
||||
public static ProtocolVersion fromNetworkId(int networkId) {
|
||||
for (ProtocolVersion v : VALUES) {
|
||||
if (networkId == v.getNetworkId()) return v;
|
||||
}
|
||||
return UNKNOWN_CLIENT_VERSION;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.bossbar.BossBarManager;
|
||||
import me.neznamy.tab.api.config.ConfigurationFile;
|
||||
import me.neznamy.tab.api.event.EventBus;
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager;
|
||||
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
|
||||
import me.neznamy.tab.api.task.ThreadManager;
|
||||
@@ -14,94 +15,193 @@ import me.neznamy.tab.api.team.TeamManager;
|
||||
*/
|
||||
public abstract class TabAPI {
|
||||
|
||||
private static TabAPI instance;
|
||||
|
||||
public static TabAPI getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void setInstance(TabAPI instance) {
|
||||
TabAPI.instance = instance;
|
||||
}
|
||||
/** Instance of the API */
|
||||
private static TabAPI instance;
|
||||
|
||||
/**
|
||||
* Returns player object from given UUID
|
||||
* @return player object from given UUID
|
||||
* @param id - Player UUID
|
||||
*/
|
||||
public abstract TabPlayer getPlayer(UUID id);
|
||||
/**
|
||||
* Returns API instance. If instance was not set by the plugin, throws
|
||||
* {@code IllegalStateException}. This is usually caused by shading the API
|
||||
* into own project, which is not allowed. Another option is calling the method
|
||||
* before plugin was able to load.
|
||||
*
|
||||
* @return API instance
|
||||
* @throws IllegalStateException
|
||||
* If instance is {@code null}
|
||||
*/
|
||||
public static TabAPI getInstance() {
|
||||
if (instance == null) throw new IllegalStateException("API instance is null. This likely means you shaded TAB's API into your project" +
|
||||
" instead of only using it, which is not allowed.");
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns player object from given name
|
||||
* @return player object from given name
|
||||
* @param name - Player name
|
||||
*/
|
||||
public abstract TabPlayer getPlayer(String name);
|
||||
|
||||
/**
|
||||
* Returns collection of all online players. Will return empty list if plugin is disabled (due to a broken configuration file for example).
|
||||
* @return collection of online players
|
||||
*/
|
||||
public abstract TabPlayer[] getOnlinePlayers();
|
||||
/**
|
||||
* Instance setter for internal use by the plugin only.
|
||||
*
|
||||
* @param instance
|
||||
* API instance
|
||||
*/
|
||||
public static void setInstance(TabAPI instance) {
|
||||
TabAPI.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return BossBar manager instance if the feature is enabled. Returns null otherwise.
|
||||
* @return BossBar manager
|
||||
*/
|
||||
public abstract BossBarManager getBossBarManager();
|
||||
|
||||
/**
|
||||
* Returns scoreboard manager instance if the feature is enabled. Returns null otherwise.
|
||||
* @return scoreboard manager
|
||||
*/
|
||||
public abstract ScoreboardManager getScoreboardManager();
|
||||
|
||||
/**
|
||||
* Returns team manager instance if the feature is enabled, false otherwise
|
||||
* @return team manager
|
||||
*/
|
||||
public abstract TeamManager getTeamManager();
|
||||
|
||||
public abstract HeaderFooterManager getHeaderFooterManager();
|
||||
|
||||
/**
|
||||
* Returns PlaceholderManager instance
|
||||
* @return PlaceholderManager instance
|
||||
*/
|
||||
public abstract PlaceholderManager getPlaceholderManager();
|
||||
/**
|
||||
* Returns player object from given UUID
|
||||
*
|
||||
* @param id
|
||||
* Player UUID
|
||||
* @return player object from given UUID
|
||||
*/
|
||||
public abstract TabPlayer getPlayer(UUID id);
|
||||
|
||||
/**
|
||||
* Returns feature manager instance
|
||||
* @return feature manager instance
|
||||
*/
|
||||
public abstract FeatureManager getFeatureManager();
|
||||
|
||||
public abstract TablistFormatManager getTablistFormatManager();
|
||||
/**
|
||||
* Returns player object from given name
|
||||
*
|
||||
* @param name
|
||||
* Player name
|
||||
* @return player object from given name
|
||||
*/
|
||||
public abstract TabPlayer getPlayer(String name);
|
||||
|
||||
/**
|
||||
* Returns server version. On proxy installation returns PROXY.
|
||||
* @return server version
|
||||
*/
|
||||
public abstract ProtocolVersion getServerVersion();
|
||||
|
||||
/**
|
||||
* Prints message into console
|
||||
* @param message - message to print
|
||||
* @param translateColors - true if colors should be translated, false if not
|
||||
*/
|
||||
public abstract void sendConsoleMessage(String message, boolean translateColors);
|
||||
|
||||
public abstract ThreadManager getThreadManager();
|
||||
|
||||
public abstract ConfigurationFile getPlayerCache();
|
||||
|
||||
public abstract ConfigurationFile getConfig();
|
||||
|
||||
public abstract PropertyConfiguration getGroups();
|
||||
|
||||
public abstract PropertyConfiguration getUsers();
|
||||
|
||||
public abstract void debug(String message);
|
||||
|
||||
public abstract void logError(String message, Throwable t);
|
||||
}
|
||||
/**
|
||||
* Returns collection of all online players. Will return empty list if plugin is disabled (due to a broken configuration file for example).
|
||||
*
|
||||
* @return collection of online players
|
||||
*/
|
||||
public abstract TabPlayer[] getOnlinePlayers();
|
||||
|
||||
/**
|
||||
* Return BossBar manager instance if the feature is enabled. If not, returns {@code null}.
|
||||
*
|
||||
* @return BossBar manager
|
||||
*/
|
||||
public abstract BossBarManager getBossBarManager();
|
||||
|
||||
/**
|
||||
* Returns scoreboard manager instance if the feature is enabled. If not, returns {@code null}.
|
||||
*
|
||||
* @return scoreboard manager
|
||||
*/
|
||||
public abstract ScoreboardManager getScoreboardManager();
|
||||
|
||||
/**
|
||||
* Returns team manager instance if the feature is enabled. If not, returns {@code null}.
|
||||
*
|
||||
* @return team manager
|
||||
*/
|
||||
public abstract TeamManager getTeamManager();
|
||||
|
||||
/**
|
||||
* Returns header/footer manager instance if the feature is enabled. If not, returns {@code null}.
|
||||
*
|
||||
* @return Header/footer manager
|
||||
*/
|
||||
public abstract HeaderFooterManager getHeaderFooterManager();
|
||||
|
||||
/**
|
||||
* Returns PlaceholderManager instance
|
||||
*
|
||||
* @return PlaceholderManager instance
|
||||
*/
|
||||
public abstract PlaceholderManager getPlaceholderManager();
|
||||
|
||||
/**
|
||||
* Returns feature manager instance
|
||||
*
|
||||
* @return feature manager instance
|
||||
*/
|
||||
public abstract FeatureManager getFeatureManager();
|
||||
|
||||
/**
|
||||
* Returns Tablist name format manager instance if the feature is enabled. If not, returns {@code null}.
|
||||
*
|
||||
* @return Tablist name format manager
|
||||
*/
|
||||
public abstract TablistFormatManager getTablistFormatManager();
|
||||
|
||||
/**
|
||||
* Gets the event bus for registering listeners for TAB events.
|
||||
*
|
||||
* @return the event bus
|
||||
*/
|
||||
public abstract EventBus getEventBus();
|
||||
|
||||
/**
|
||||
* Returns server version. On proxy installation returns PROXY.
|
||||
*
|
||||
* @return server version
|
||||
*/
|
||||
public abstract ProtocolVersion getServerVersion();
|
||||
|
||||
/**
|
||||
* Prints message into console
|
||||
*
|
||||
* @param message
|
||||
* message to print
|
||||
* @param translateColors
|
||||
* {@code true} if colors should be translated, {@code false} if not
|
||||
*/
|
||||
public abstract void sendConsoleMessage(String message, boolean translateColors);
|
||||
|
||||
/**
|
||||
* Returns TAB's Thread manager, which allows task submitting
|
||||
*
|
||||
* @return ThreadManager instance
|
||||
*/
|
||||
public abstract ThreadManager getThreadManager();
|
||||
|
||||
/**
|
||||
* Returns TAB's cache file used to store player toggle data
|
||||
*
|
||||
* @return TAB's player cache file
|
||||
*/
|
||||
public abstract ConfigurationFile getPlayerCache();
|
||||
|
||||
/**
|
||||
* Returns TAB's config.yml file
|
||||
*
|
||||
* @return config.yml file
|
||||
*/
|
||||
public abstract ConfigurationFile getConfig();
|
||||
|
||||
/**
|
||||
* Returns TAB's group configuration
|
||||
*
|
||||
* @return TAB's group configuration
|
||||
*/
|
||||
public abstract PropertyConfiguration getGroups();
|
||||
|
||||
/**
|
||||
* Returns TAB's user configuration
|
||||
*
|
||||
* @return TAB's user configuration
|
||||
*/
|
||||
public abstract PropertyConfiguration getUsers();
|
||||
|
||||
/**
|
||||
* Sends a debug message into console if the option
|
||||
* is enabled in config.
|
||||
*
|
||||
* @param message
|
||||
* Message to send
|
||||
*/
|
||||
public abstract void debug(String message);
|
||||
|
||||
/**
|
||||
* Logs an error into errors.log file
|
||||
*
|
||||
* @param message
|
||||
* Error message
|
||||
* @param t
|
||||
* Thrown error
|
||||
*/
|
||||
public abstract void logError(String message, Throwable t);
|
||||
|
||||
/**
|
||||
* Sets name of file with syntax error, which prevented
|
||||
* the plugin from enabling. Internal use only.
|
||||
*
|
||||
* @param file
|
||||
* Name of file with syntax error
|
||||
*/
|
||||
public abstract void setBrokenFile(String file);
|
||||
}
|
||||
@@ -1,262 +1,404 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
|
||||
/**
|
||||
* Abstract class representing a core feature of the plugin.
|
||||
* <p>
|
||||
* It receives all kinds of events and can react to them.
|
||||
*/
|
||||
public abstract class TabFeature {
|
||||
|
||||
private final String featureName;
|
||||
private final String refreshDisplayName;
|
||||
protected final String[] disabledServers;
|
||||
private final boolean serverWhitelistMode;
|
||||
protected final String[] disabledWorlds;
|
||||
private final boolean worldWhitelistMode;
|
||||
private final List<TabPlayer> disabledPlayers = new ArrayList<>();
|
||||
private final List<String> methodOverrides = new ArrayList<>();
|
||||
|
||||
protected TabFeature(String featureName, String refreshDisplayName) {
|
||||
this(featureName, refreshDisplayName, null, null);
|
||||
}
|
||||
|
||||
protected TabFeature(String featureName, String refreshDisplayName, List<String> disabledServers, List<String> disabledWorlds) {
|
||||
this.featureName = featureName;
|
||||
this.refreshDisplayName = refreshDisplayName;
|
||||
if (disabledServers != null) {
|
||||
this.disabledServers = disabledServers.toArray(new String[0]);
|
||||
serverWhitelistMode = disabledServers.contains("WHITELIST");
|
||||
} else {
|
||||
this.disabledServers = new String[0];
|
||||
serverWhitelistMode = false;
|
||||
}
|
||||
if (disabledWorlds != null) {
|
||||
this.disabledWorlds = disabledWorlds.toArray(new String[0]);
|
||||
worldWhitelistMode = disabledWorlds.contains("WHITELIST");
|
||||
} else {
|
||||
this.disabledWorlds = new String[0];
|
||||
worldWhitelistMode = false;
|
||||
}
|
||||
try {
|
||||
if (getClass().getMethod("onCommand", TabPlayer.class, String.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onCommand");
|
||||
if (getClass().getMethod("onJoin", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onJoin");
|
||||
if (getClass().getMethod("onQuit", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onQuit");
|
||||
if (getClass().getMethod("onWorldChange", TabPlayer.class, String.class, String.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onWorldChange");
|
||||
if (getClass().getMethod("onDisplayObjective", TabPlayer.class, PacketPlayOutScoreboardDisplayObjective.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onDisplayObjective");
|
||||
if (getClass().getMethod("onLoginPacket", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onLoginPacket");
|
||||
if (getClass().getMethod("onObjective", TabPlayer.class, PacketPlayOutScoreboardObjective.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onObjective");
|
||||
if (getClass().getMethod("onPlayerInfo", TabPlayer.class, PacketPlayOutPlayerInfo.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onPlayerInfo");
|
||||
if (getClass().getMethod("onPacketReceive", TabPlayer.class, Object.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onPacketReceive");
|
||||
if (getClass().getMethod("onPacketSend", TabPlayer.class, Object.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onPacketSend");
|
||||
if (getClass().getMethod("refresh", TabPlayer.class, boolean.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("refresh");
|
||||
} catch (NoSuchMethodException e) {
|
||||
//this will never happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all players and sends packets
|
||||
*/
|
||||
public void load() {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads all players and sends clear packets
|
||||
*/
|
||||
public void unload() {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes command from player
|
||||
* @param sender - command sender
|
||||
* @param message - command line
|
||||
* @return true if event should be cancelled, false if not
|
||||
*/
|
||||
public boolean onCommand(TabPlayer sender, String message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes join event
|
||||
* @param connectedPlayer - player who connected
|
||||
*/
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes quit event
|
||||
* @param disconnectedPlayer - player who disconnected
|
||||
*/
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
disabledPlayers.remove(disconnectedPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes world switch
|
||||
* @param changed - player who switched world
|
||||
* @param from - world player changed from
|
||||
* @param to - world player changed to
|
||||
*/
|
||||
public void onWorldChange(TabPlayer changed, String from, String to) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes server switch
|
||||
* @param changed - player who switched server
|
||||
* @param from - server player changed from
|
||||
* @param to - server player changed to
|
||||
*/
|
||||
public void onServerChange(TabPlayer changed, String from, String to) {
|
||||
onWorldChange(changed, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the packet send and returns true if packet should be cancelled
|
||||
* @param receiver - player receiving packet
|
||||
* @param packet - received packet
|
||||
*/
|
||||
public void onDisplayObjective(TabPlayer receiver, PacketPlayOutScoreboardDisplayObjective packet) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes login packet, only available on BungeeCord
|
||||
* @param packetReceiver - player receiving client reset packet
|
||||
*/
|
||||
public void onLoginPacket(TabPlayer packetReceiver) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the packet send
|
||||
* @param receiver - player receiving packet
|
||||
* @param packet - received packet
|
||||
*/
|
||||
public void onObjective(TabPlayer receiver, PacketPlayOutScoreboardObjective packet) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the packet send and possibly modifies it
|
||||
* @param receiver - player receiving packet
|
||||
* @param info - received packet
|
||||
*/
|
||||
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes raw packet sent by client
|
||||
* @param sender - packet sender
|
||||
* @param packet - packet received
|
||||
* @return true if false should be cancelled, false if not
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes raw packet sent to client
|
||||
* @param receiver - packet receiver
|
||||
* @param packet - the packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
//empty by default
|
||||
}
|
||||
/** Feature's name displayed in /tab cpu */
|
||||
private final String featureName;
|
||||
|
||||
/**
|
||||
* Performs refresh of specified player
|
||||
* @param refreshed - player to refresh
|
||||
* @param force - if refresh should be forced despite refresh seemingly not needed
|
||||
*/
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
//empty by default
|
||||
}
|
||||
/** Feature's function name displayed in place of refreshing in /tab cpu */
|
||||
private final String refreshDisplayName;
|
||||
|
||||
/**
|
||||
* Registers this feature as one using specified placeholders
|
||||
* @param placeholders - placeholders to add as used in this feature
|
||||
*/
|
||||
public void addUsedPlaceholders(Collection<String> placeholders) {
|
||||
if (placeholders.isEmpty()) return;
|
||||
placeholders.forEach(p -> TabAPI.getInstance().getPlaceholderManager().addUsedPlaceholder(p, this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if world or server is disabled, false if not
|
||||
* @param world - world to check
|
||||
* @param server - server to check
|
||||
* @return true if feature should be disabled, false if not
|
||||
*/
|
||||
public boolean isDisabled(String server, String world) {
|
||||
boolean contains = contains(disabledWorlds, world);
|
||||
if (worldWhitelistMode) contains = !contains;
|
||||
if (contains) return true;
|
||||
contains = contains(disabledServers, server);
|
||||
if (serverWhitelistMode) contains = !contains;
|
||||
return contains;
|
||||
}
|
||||
|
||||
protected boolean contains(String[] list, String element) {
|
||||
if (element == null) return false;
|
||||
for (String s : list) {
|
||||
if (s.endsWith("*")) {
|
||||
if (element.toLowerCase().startsWith(s.substring(0, s.length()-1).toLowerCase())) return true;
|
||||
} else {
|
||||
if (element.equalsIgnoreCase(s)) return true;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
/** Servers where the feature is disabled (or enabled if using whitelist mode) */
|
||||
protected String[] disabledServers = new String[0];
|
||||
|
||||
public boolean overridesMethod(String method) {
|
||||
return methodOverrides.contains(method);
|
||||
}
|
||||
/** Flag tracking whether disabled server list is a whitelist or blacklist */
|
||||
private boolean serverWhitelistMode = false;
|
||||
|
||||
public boolean isDisabledPlayer(TabPlayer p) {
|
||||
return disabledPlayers.contains(p);
|
||||
}
|
||||
|
||||
public void addDisabledPlayer(TabPlayer p) {
|
||||
if (disabledPlayers.contains(p)) return;
|
||||
disabledPlayers.add(p);
|
||||
}
|
||||
|
||||
public void removeDisabledPlayer(TabPlayer p) {
|
||||
disabledPlayers.remove(p);
|
||||
}
|
||||
/** Worlds where the feature is disabled (or enabled if using whitelist mode) */
|
||||
protected String[] disabledWorlds = new String[0];
|
||||
|
||||
public String getRefreshDisplayName() {
|
||||
return refreshDisplayName;
|
||||
}
|
||||
/** Flag tracking whether disabled world list is a whitelist or blacklist */
|
||||
private boolean worldWhitelistMode = false;
|
||||
|
||||
/** Players located in currently disabled worlds / servers */
|
||||
private final Set<TabPlayer> disabledPlayers = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
|
||||
/**
|
||||
* List of methods overridden (implemented) by each feature.
|
||||
* If a method is not overridden, it is not called at all.
|
||||
* This avoids a massive spam for every single method
|
||||
* in every single feature in /tab cpu output
|
||||
*/
|
||||
private final List<String> methodOverrides = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads method overrides
|
||||
*
|
||||
* @param featureName
|
||||
* Feature's name in /tab cpu
|
||||
* @param refreshDisplayName
|
||||
* "refreshing" cpu display type name of the feature
|
||||
*/
|
||||
protected TabFeature(String featureName, String refreshDisplayName) {
|
||||
this.featureName = featureName;
|
||||
this.refreshDisplayName = refreshDisplayName;
|
||||
try {
|
||||
if (getClass().getMethod("onCommand", TabPlayer.class, String.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onCommand");
|
||||
if (getClass().getMethod("onJoin", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onJoin");
|
||||
if (getClass().getMethod("onQuit", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onQuit");
|
||||
if (getClass().getMethod("onWorldChange", TabPlayer.class, String.class, String.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onWorldChange");
|
||||
if (getClass().getMethod("onServerChange", TabPlayer.class, String.class, String.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onServerChange");
|
||||
if (getClass().getMethod("onDisplayObjective", TabPlayer.class, PacketPlayOutScoreboardDisplayObjective.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onDisplayObjective");
|
||||
if (getClass().getMethod("onLoginPacket", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onLoginPacket");
|
||||
if (getClass().getMethod("onObjective", TabPlayer.class, PacketPlayOutScoreboardObjective.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onObjective");
|
||||
if (getClass().getMethod("onPlayerInfo", TabPlayer.class, PacketPlayOutPlayerInfo.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onPlayerInfo");
|
||||
if (getClass().getMethod("onPacketReceive", TabPlayer.class, Object.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onPacketReceive");
|
||||
if (getClass().getMethod("onPacketSend", TabPlayer.class, Object.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onPacketSend");
|
||||
if (getClass().getMethod("refresh", TabPlayer.class, boolean.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("refresh");
|
||||
if (getClass().getMethod("onVanishStatusChange", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onVanishStatusChange");
|
||||
} catch (NoSuchMethodException e) {
|
||||
//this will never happen
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads method overrides.
|
||||
* Also loads lists of disabled worlds and servers of this feature with config
|
||||
* section path specified with {@code configSection} parameter.
|
||||
*
|
||||
* @param featureName
|
||||
* Feature's name in /tab cpu
|
||||
* @param refreshDisplayName
|
||||
* "refreshing" cpu display type name of the feature
|
||||
* @param configSection
|
||||
* Configuration section of the feature to load disabled
|
||||
* servers / worlds from
|
||||
*/
|
||||
protected TabFeature(String featureName, String refreshDisplayName, String configSection) {
|
||||
this(featureName, refreshDisplayName);
|
||||
List<String> disabledServers = TabAPI.getInstance().getConfig().getStringList(configSection + ".disable-in-servers");
|
||||
List<String> disabledWorlds = TabAPI.getInstance().getConfig().getStringList(configSection + ".disable-in-worlds");
|
||||
if (disabledServers != null) {
|
||||
this.disabledServers = disabledServers.toArray(new String[0]);
|
||||
serverWhitelistMode = disabledServers.contains("WHITELIST");
|
||||
}
|
||||
if (disabledWorlds != null) {
|
||||
this.disabledWorlds = disabledWorlds.toArray(new String[0]);
|
||||
worldWhitelistMode = disabledWorlds.contains("WHITELIST");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all online players and sends packets
|
||||
*/
|
||||
public void load() {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads all players and sends clear packets
|
||||
*/
|
||||
public void unload() {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes command from player. This is typically a toggle command.
|
||||
*
|
||||
* @param sender
|
||||
* command sender
|
||||
* @param message
|
||||
* command line
|
||||
* @return {@code true} if event should be cancelled, {@code true} if not
|
||||
*/
|
||||
public boolean onCommand(TabPlayer sender, String message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes join event
|
||||
*
|
||||
* @param connectedPlayer
|
||||
* player who connected
|
||||
*/
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes quit event
|
||||
*
|
||||
* @param disconnectedPlayer
|
||||
* player who disconnected
|
||||
*/
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes world switch
|
||||
*
|
||||
* @param changed
|
||||
* player who switched world
|
||||
* @param from
|
||||
* world player changed from
|
||||
* @param to
|
||||
* world player changed to
|
||||
*/
|
||||
public void onWorldChange(TabPlayer changed, String from, String to) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes server switch
|
||||
*
|
||||
* @param changed
|
||||
* player who switched server
|
||||
* @param from
|
||||
* server player changed from
|
||||
* @param to
|
||||
* server player changed to
|
||||
*/
|
||||
public void onServerChange(TabPlayer changed, String from, String to) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the packet send and returns true if packet should be cancelled
|
||||
*
|
||||
* @param receiver
|
||||
* player receiving packet
|
||||
* @param packet
|
||||
* received packet
|
||||
*/
|
||||
public void onDisplayObjective(TabPlayer receiver, PacketPlayOutScoreboardDisplayObjective packet) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes login packet, only available on BungeeCord
|
||||
*
|
||||
* @param packetReceiver
|
||||
* player receiving client reset packet
|
||||
*/
|
||||
public void onLoginPacket(TabPlayer packetReceiver) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the packet send
|
||||
*
|
||||
* @param receiver
|
||||
* player receiving packet
|
||||
* @param packet
|
||||
* received packet
|
||||
*/
|
||||
public void onObjective(TabPlayer receiver, PacketPlayOutScoreboardObjective packet) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the packet send and possibly modifies it
|
||||
*
|
||||
* @param receiver
|
||||
* player receiving packet
|
||||
* @param info
|
||||
* received packet
|
||||
*/
|
||||
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes raw packet sent by client
|
||||
*
|
||||
* @param sender
|
||||
* packet sender
|
||||
* @param packet
|
||||
* packet received
|
||||
* @return true if false should be cancelled, false if not
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes raw packet sent to client
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
* @param packet
|
||||
* the packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs refresh of specified player
|
||||
*
|
||||
* @param refreshed
|
||||
* player to refresh
|
||||
* @param force
|
||||
* if refresh should be forced despite refresh seemingly not needed
|
||||
*/
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes vanish status change of player
|
||||
*
|
||||
* @param player
|
||||
* Player who changed vanish status
|
||||
*/
|
||||
public void onVanishStatusChange(TabPlayer player){
|
||||
//empty by default
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this feature as one using specified placeholders
|
||||
*
|
||||
* @param placeholders
|
||||
* placeholders to add as used in this feature
|
||||
*/
|
||||
public void addUsedPlaceholders(Collection<String> placeholders) {
|
||||
if (placeholders.isEmpty()) return;
|
||||
placeholders.forEach(p -> TabAPI.getInstance().getPlaceholderManager().addUsedPlaceholder(p, this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if world or server is disabled, {@code false} if not
|
||||
*
|
||||
* @param server
|
||||
* server to check
|
||||
* @param world
|
||||
* world to check
|
||||
* @return {@code true} if feature should be disabled, {@code false} if not
|
||||
*/
|
||||
public boolean isDisabled(String server, String world) {
|
||||
boolean contains = contains(disabledWorlds, world);
|
||||
if (worldWhitelistMode) contains = !contains;
|
||||
if (contains) return true;
|
||||
contains = contains(disabledServers, server);
|
||||
if (serverWhitelistMode) contains = !contains;
|
||||
return contains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if list contains the specified element or element
|
||||
* ends with {@code "*"} and element meeting that requirement is present,
|
||||
* {@code false} otherwise.
|
||||
*
|
||||
* @param list
|
||||
* List to check
|
||||
* @param element
|
||||
* Element to find
|
||||
* @return {@code true} if element was found, {@code false} if not
|
||||
*/
|
||||
protected boolean contains(String[] list, String element) {
|
||||
if (element == null) return false;
|
||||
for (String s : list) {
|
||||
if (s.endsWith("*")) {
|
||||
if (element.toLowerCase().startsWith(s.substring(0, s.length()-1).toLowerCase())) return true;
|
||||
} else {
|
||||
if (element.equalsIgnoreCase(s)) return true;
|
||||
}
|
||||
}
|
||||
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
|
||||
*
|
||||
* @param method
|
||||
* Method to check
|
||||
* @return {@code true} if overridden, {@code false} if not
|
||||
*/
|
||||
public boolean overridesMethod(String method) {
|
||||
return methodOverrides.contains(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if player is currently located in a server
|
||||
* or world, which is marked as disabled, {@code false} if not
|
||||
*
|
||||
* @param p
|
||||
* Player to check
|
||||
* @return {@code true} if player is in disabled server / world,
|
||||
* {@code false} if not
|
||||
*/
|
||||
public boolean isDisabledPlayer(TabPlayer p) {
|
||||
return disabledPlayers.contains(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds specified player into list of players in disabled
|
||||
* servers / worlds.
|
||||
*
|
||||
* @param p
|
||||
* Player to add
|
||||
*/
|
||||
public void addDisabledPlayer(TabPlayer p) {
|
||||
if (disabledPlayers.contains(p)) return;
|
||||
disabledPlayers.add(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes specified player from list of players in disabled
|
||||
* servers / worlds
|
||||
*
|
||||
* @param p
|
||||
* Player to check
|
||||
*/
|
||||
public void removeDisabledPlayer(TabPlayer p) {
|
||||
disabledPlayers.remove(p);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.util.UUID;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
|
||||
/**
|
||||
@@ -11,251 +12,302 @@ import me.neznamy.tab.api.protocol.TabPacket;
|
||||
*/
|
||||
public interface TabPlayer {
|
||||
|
||||
/**
|
||||
* Refreshes all visuals on the player
|
||||
*/
|
||||
void forceRefresh();
|
||||
/**
|
||||
* Refreshes all visuals on the player
|
||||
*/
|
||||
void forceRefresh();
|
||||
|
||||
/**
|
||||
* Returns player's name
|
||||
* @return Player's name
|
||||
*/
|
||||
String getName();
|
||||
/**
|
||||
* Returns player's name
|
||||
*
|
||||
* @return Player's name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns player's UUID
|
||||
* @return Player's UUID
|
||||
*/
|
||||
UUID getUniqueId();
|
||||
/**
|
||||
* Returns player's UUID
|
||||
*
|
||||
* @return Player's UUID
|
||||
*/
|
||||
UUID getUniqueId();
|
||||
|
||||
/**
|
||||
* Returns player's uuid used in TabList. This may only be different from real uuid if
|
||||
* TAB is installed on velocity with some specific velocity setup
|
||||
* @return player's uuid in TabList
|
||||
*/
|
||||
UUID getTablistUUID();
|
||||
/**
|
||||
* Returns player's uuid used in TabList. This may only be different from real uuid if
|
||||
* TAB is installed on velocity with some specific velocity setup
|
||||
*
|
||||
* @return player's uuid in TabList
|
||||
*/
|
||||
UUID getTablistUUID();
|
||||
|
||||
/**
|
||||
* Returns player's protocol version
|
||||
* @return player's protocol version
|
||||
*/
|
||||
ProtocolVersion getVersion();
|
||||
/**
|
||||
* Returns player's protocol version
|
||||
*
|
||||
* @return player's protocol version
|
||||
*/
|
||||
ProtocolVersion getVersion();
|
||||
|
||||
/**
|
||||
* Returns platform-specific entity
|
||||
* @return an instance of bukkit/bungee player depending on platform
|
||||
*/
|
||||
Object getPlayer();
|
||||
/**
|
||||
* Returns platform-specific entity
|
||||
*
|
||||
* @return an instance of bukkit/bungee player depending on platform
|
||||
*/
|
||||
Object getPlayer();
|
||||
|
||||
/**
|
||||
* Returns player's current world name (on BungeeCord this requires bridge installed)
|
||||
* @return name of world where player is currently in, null on BungeeCord if bridge is not installed
|
||||
*/
|
||||
String getWorld();
|
||||
|
||||
/**
|
||||
* Returns player's current server name on BungeeCord (on bukkit this returns null)
|
||||
* @return name of server where player is currently in, null on bukkit
|
||||
*/
|
||||
String getServer();
|
||||
/**
|
||||
* Returns player's current world name (on BungeeCord this requires bridge installed)
|
||||
*
|
||||
* @return name of world where player is currently in, "N/A" on BungeeCord if bridge is not installed
|
||||
*/
|
||||
String getWorld();
|
||||
|
||||
/**
|
||||
* Returns player's current server name on BungeeCord (on bukkit this returns "N/A")
|
||||
*
|
||||
* @return name of server where player is currently in, "N/A" on bukkit
|
||||
*/
|
||||
String getServer();
|
||||
|
||||
/**
|
||||
* Performs platform-specific API call to check for permission and returns the result
|
||||
* @param permission - the permission to check for
|
||||
* @return - true if player has permission, false if not
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
/**
|
||||
* Performs platform-specific API call to check for permission and returns the result
|
||||
*
|
||||
* @param permission
|
||||
* the permission to check for
|
||||
* @return true if player has permission, false if not
|
||||
*/
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
/**
|
||||
* Sends the player a custom universal packet
|
||||
* @param packet - packet to send
|
||||
*/
|
||||
void sendCustomPacket(TabPacket packet);
|
||||
/**
|
||||
* Sends the player a custom universal packet
|
||||
*
|
||||
* @param packet
|
||||
* packet to send
|
||||
*/
|
||||
void sendCustomPacket(TabPacket packet);
|
||||
|
||||
/**
|
||||
* Sends the player a custom universal packet and adds that packet into counter that
|
||||
* is displayed in /tab cpu
|
||||
* @param packet - packet to send
|
||||
* @param feature - feature to increment sent packet counter of
|
||||
*/
|
||||
void sendCustomPacket(TabPacket packet, TabFeature feature);
|
||||
|
||||
/**
|
||||
* Sends the player a custom universal packet and adds that packet into counter that
|
||||
* is displayed in /tab cpu
|
||||
* @param packet - packet to send
|
||||
* @param feature - feature to increment sent packet counter of
|
||||
*/
|
||||
void sendCustomPacket(TabPacket packet, String feature);
|
||||
/**
|
||||
* Sends the player a custom universal packet and adds that packet into counter that
|
||||
* is displayed in /tab cpu
|
||||
*
|
||||
* @param packet
|
||||
* packet to send
|
||||
* @param feature
|
||||
* feature to increment sent packet counter of
|
||||
*/
|
||||
void sendCustomPacket(TabPacket packet, TabFeature feature);
|
||||
|
||||
/**
|
||||
* Sends the player a custom universal packet and adds that packet into counter that
|
||||
* is displayed in /tab cpu
|
||||
*
|
||||
* @param packet
|
||||
* packet to send
|
||||
* @param feature
|
||||
* feature to increment sent packet counter of
|
||||
*/
|
||||
void sendCustomPacket(TabPacket packet, String feature);
|
||||
|
||||
/**
|
||||
* Sends the player a platform-specific packet
|
||||
* @param packet - an instance of packet depending on platform
|
||||
*/
|
||||
void sendPacket(Object packet);
|
||||
/**
|
||||
* Sends the player a platform-specific packet
|
||||
*
|
||||
* @param packet
|
||||
* an instance of packet depending on platform
|
||||
*/
|
||||
void sendPacket(Object packet);
|
||||
|
||||
/**
|
||||
* Sends the player a platform-specific packet and adds that packet into counter that
|
||||
* is displayed in /tab cpu
|
||||
* @param packet - an instance of packet depending on platform
|
||||
* @param feature - feature to increment sent packet counter of
|
||||
*/
|
||||
void sendPacket(Object packet, TabFeature feature);
|
||||
/**
|
||||
* Returns player's property by name
|
||||
*
|
||||
* @param name
|
||||
* name of property
|
||||
* @return the property or {@code null} if not found
|
||||
*/
|
||||
Property getProperty(String name);
|
||||
|
||||
/**
|
||||
* Sends the player a platform-specific packet and adds that packet into counter that
|
||||
* is displayed in /tab cpu
|
||||
* @param packet - an instance of packet depending on platform
|
||||
* @param feature - feature to increment sent packet counter of
|
||||
*/
|
||||
void sendPacket(Object packet, String feature);
|
||||
/**
|
||||
* Sends a message to the player
|
||||
*
|
||||
* @param message
|
||||
* message to be sent
|
||||
* @param translateColors
|
||||
* whether colors should be translated or not
|
||||
*/
|
||||
void sendMessage(String message, boolean translateColors);
|
||||
|
||||
/**
|
||||
* Returns player's property by name
|
||||
* @param name - name of property
|
||||
* @return the property or null if not found
|
||||
*/
|
||||
Property getProperty(String name);
|
||||
/**
|
||||
* Sends specified component as a chat message
|
||||
*
|
||||
* @param message
|
||||
* message to send
|
||||
*/
|
||||
void sendMessage(IChatBaseComponent message);
|
||||
|
||||
/**
|
||||
* Sends a message to the player
|
||||
* @param message - message to be sent
|
||||
* @param translateColors - whether colors should be translated or not
|
||||
*/
|
||||
void sendMessage(String message, boolean translateColors);
|
||||
/**
|
||||
* Returns player's netty channel. On servers running version 1.7 or older returns {@code null}.
|
||||
*
|
||||
* @return player's channel
|
||||
*/
|
||||
Channel getChannel();
|
||||
|
||||
/**
|
||||
* Sends specified component as a chat message
|
||||
* @param message - message to send
|
||||
*/
|
||||
void sendMessage(IChatBaseComponent message);
|
||||
/**
|
||||
* Returns player's ping calculated by server
|
||||
*
|
||||
* @return player's ping
|
||||
*/
|
||||
int getPing();
|
||||
|
||||
/**
|
||||
* Returns player's primary permission group
|
||||
* @return player's group depending on configuration
|
||||
*/
|
||||
String getGroup();
|
||||
/**
|
||||
* Returns player's skin data
|
||||
*
|
||||
* @return player's skin
|
||||
*/
|
||||
Skin getSkin();
|
||||
|
||||
/**
|
||||
* Toggles armor stands preview mode for the player
|
||||
*/
|
||||
void toggleNametagPreview();
|
||||
/**
|
||||
* Returns true once the player is successfully loaded (onJoin method ran through all methods)
|
||||
*
|
||||
* @return {@code true} if player is fully loaded, {@code false} otherwise
|
||||
*/
|
||||
boolean isLoaded();
|
||||
|
||||
/**
|
||||
* Returns whether player is previewing NameTag or not
|
||||
* @return true if previewing, false if not
|
||||
*/
|
||||
boolean isPreviewingNametag();
|
||||
/**
|
||||
* Sets property with specified name to new value. If property did not exist before, it is
|
||||
* created and {@code true} is returned. If it existed, it is overridden and {@code true} is returned.
|
||||
* {@code false} is returned otherwise.
|
||||
*
|
||||
* @param feature
|
||||
* feature using this property to get placeholders registered
|
||||
* @param identifier
|
||||
* property name
|
||||
* @param rawValue
|
||||
* new raw value
|
||||
* @return {@code true} if value changed / did not exist, {@code false} if value did not change
|
||||
*/
|
||||
boolean setProperty(TabFeature feature, String identifier, String rawValue);
|
||||
|
||||
/**
|
||||
* Returns player's channel or null if server is 1.7 or older
|
||||
* @return player's channel
|
||||
*/
|
||||
Channel getChannel();
|
||||
/**
|
||||
* Loads property from config using standard property loading algorithm
|
||||
*
|
||||
* @param property
|
||||
* property name to load
|
||||
* @return {@code true} if value did not exist or changed, {@code false} otherwise
|
||||
*/
|
||||
boolean loadPropertyFromConfig(TabFeature feature, String property);
|
||||
|
||||
/**
|
||||
* Returns player's ping calculated by server
|
||||
* @return player's ping
|
||||
*/
|
||||
int getPing();
|
||||
/**
|
||||
* Loads property from config using standard property loading algorithm. If the property is
|
||||
* not set in config, {@code ifNotSet} value is used.
|
||||
*
|
||||
* @param property
|
||||
* property name to load
|
||||
* @param ifNotSet
|
||||
* value to use if property is not defined in config
|
||||
* @return {@code true} if value did not exist or changed, {@code false} otherwise
|
||||
*/
|
||||
boolean loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
|
||||
|
||||
/**
|
||||
* Returns player's platform-specific skin data
|
||||
* @return player's skin
|
||||
*/
|
||||
Object getSkin();
|
||||
/**
|
||||
* Returns name of player's scoreboard team or {@code null} if NameTag feature is disabled
|
||||
*
|
||||
* @return name of player's team
|
||||
*/
|
||||
String getTeamName();
|
||||
|
||||
/**
|
||||
* Returns true once the player is successfully loaded (onJoin method ran through all methods)
|
||||
* @return true if player is fully loaded, false otherwise
|
||||
*/
|
||||
boolean isLoaded();
|
||||
/**
|
||||
* Returns user-friendly explanation of team name
|
||||
*
|
||||
* @return explanation behind team name
|
||||
*/
|
||||
String getTeamNameNote();
|
||||
|
||||
/**
|
||||
* Sets property with specified name to new value. If property did not exist before, it is
|
||||
* created and true is returned. If it existed, it is overridden and true is returned. False is returned otherwise.
|
||||
* @param feature - feature using this property to get placeholders registered
|
||||
* @param identifier - property name
|
||||
* @param rawValue - new raw value
|
||||
* @return true if value changed / did not exist, false if value did not change
|
||||
*/
|
||||
boolean setProperty(TabFeature feature, String identifier, String rawValue);
|
||||
/**
|
||||
* Returns {@code true} if player is disguised using LibsDisguises, {@code false} if not
|
||||
*
|
||||
* @return {@code true} if player is disguised, {@code false} if not
|
||||
*/
|
||||
boolean isDisguised();
|
||||
|
||||
/**
|
||||
* Loads property from config using standard property loading algorithm
|
||||
* @param property - property name to load
|
||||
*/
|
||||
void loadPropertyFromConfig(TabFeature feature, String property);
|
||||
/**
|
||||
* Returns {@code true} if player has invisibility potion, {@code false} if not.
|
||||
* For bukkit, API is used, for BungeeCord, bridge is used.
|
||||
*
|
||||
* @return {@code true} if player has invisibility potion, {@code false} if not
|
||||
*/
|
||||
boolean hasInvisibilityPotion();
|
||||
|
||||
/**
|
||||
* Loads property from config using standard property loading algorithm. If the property is
|
||||
* not set in config, sets it to ifNotSet value
|
||||
* @param property - property name to load
|
||||
* @param ifNotSet - value to use if property is not defined in config
|
||||
*/
|
||||
void loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
|
||||
/**
|
||||
* Checks if player is vanished using any supported vanish plugin and returns the result.
|
||||
*
|
||||
* @return {@code true} if player is vanished, {@code false} if not
|
||||
*/
|
||||
boolean isVanished();
|
||||
|
||||
/**
|
||||
* Returns name of player's scoreboard team or null if NameTag feature is disabled
|
||||
* @return name of player's team
|
||||
*/
|
||||
String getTeamName();
|
||||
/**
|
||||
* Calls platform-specific method and returns the result
|
||||
*
|
||||
* @return {@code true} if player is online, {@code false} if not
|
||||
*/
|
||||
boolean isOnline();
|
||||
|
||||
/**
|
||||
* Returns GameMode of the player (0 for survival, 1 creative, 2 adventure, 3 spectator)
|
||||
*
|
||||
* @return GameMode of the player
|
||||
*/
|
||||
int getGamemode();
|
||||
|
||||
/**
|
||||
* Returns user-friendly explanation of team name
|
||||
* @return explanation behind team name
|
||||
*/
|
||||
String getTeamNameNote();
|
||||
/**
|
||||
* Returns {@code true} if this is a bedrock player, {@code false} if not
|
||||
*
|
||||
* @return {@code true} if player is a bedrock player, {@code false} if not
|
||||
*/
|
||||
boolean isBedrockPlayer();
|
||||
|
||||
/**
|
||||
* Returns true if player is disguised using LibsDisguises
|
||||
* @return true if player is disguised, false if not
|
||||
*/
|
||||
boolean isDisguised();
|
||||
/**
|
||||
* Returns player's primary permission group. If group has been changed using
|
||||
* {@link #setTemporaryGroup(String)}, returns that value. Otherwise, returns group
|
||||
* detected by standard group assign logic
|
||||
*
|
||||
* @return player's primary permission group
|
||||
*/
|
||||
String getGroup();
|
||||
|
||||
/**
|
||||
* Returns true if player has invisibility potion, false if not. For bukkit, API is used, for BungeeCord
|
||||
* bukkit bridge is used
|
||||
* @return true if player has invisibility potion, false if not
|
||||
*/
|
||||
boolean hasInvisibilityPotion();
|
||||
/**
|
||||
* Temporarily overrides player's group and applies all changes coming from new group.
|
||||
* This includes all properties and sorting, if used.
|
||||
*
|
||||
* @param group
|
||||
* New group to use
|
||||
* @see #hasTemporaryGroup()
|
||||
* @see #resetTemporaryGroup()
|
||||
*/
|
||||
void setTemporaryGroup(String group);
|
||||
|
||||
/**
|
||||
* Currently only PV hook on BungeeCord for global PlayerList function. Looking to expand this
|
||||
* in the future
|
||||
* @return true if vanished with PV on BungeeCord
|
||||
*/
|
||||
boolean isVanished();
|
||||
/**
|
||||
* Returns temporary group applied to the player using {@link #setTemporaryGroup(String)}.
|
||||
* If no group was set, returns {@code null}
|
||||
*
|
||||
* @return Temporary group assigned to player or {@code null} if not set
|
||||
* @see #setTemporaryGroup(String)
|
||||
* @see #resetTemporaryGroup()
|
||||
*/
|
||||
boolean hasTemporaryGroup();
|
||||
|
||||
/**
|
||||
* Returns true if player is online according to server
|
||||
* @return true if online, false if not
|
||||
*/
|
||||
boolean isOnline();
|
||||
/**
|
||||
* Resets temporary group assigned using {@link #setTemporaryGroup(String)}.
|
||||
* If no temporary group is set, doesn't do anything.
|
||||
*
|
||||
* @see #setTemporaryGroup(String)
|
||||
* @see #hasTemporaryGroup()
|
||||
*/
|
||||
void resetTemporaryGroup();
|
||||
|
||||
/**
|
||||
* Returns player's armor stand manager if unlimited GameMode mode is enabled, null if disabled
|
||||
* @return player's armor stand manager
|
||||
*/
|
||||
ArmorStandManager getArmorStandManager();
|
||||
|
||||
/**
|
||||
* Sets player's armor stands manager to new instance
|
||||
* @param armorStandManager - new instance
|
||||
*/
|
||||
void setArmorStandManager(ArmorStandManager armorStandManager);
|
||||
|
||||
/**
|
||||
* Returns GameMode of the player (0 for survival, 1 creative, 2 adventure, 3 spectator)
|
||||
* @return GameMode of the player
|
||||
*/
|
||||
int getGamemode();
|
||||
|
||||
boolean isBedrockPlayer();
|
||||
|
||||
void setTemporaryGroup(String group);
|
||||
|
||||
boolean hasTemporaryGroup();
|
||||
|
||||
void resetTemporaryGroup();
|
||||
/**
|
||||
* Returns player's game profile name as seen by other players. By default,
|
||||
* this returns player's name. If using a nickname / disguise plugin that changes
|
||||
* game profile name for other players and change was successfully detected, returns
|
||||
* that value.
|
||||
*
|
||||
* @return player's game profile name as seen by other players
|
||||
*/
|
||||
String getNickname();
|
||||
}
|
||||
@@ -1,29 +1,168 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
/**
|
||||
* An interface allowing to modify tablist display names
|
||||
* of players.
|
||||
* <p>
|
||||
* Instance can be obtained using {@link TabAPI#getTablistFormatManager()}.
|
||||
* This requires the feature to be enabled in config, otherwise the method will
|
||||
* return {@code null}.
|
||||
*/
|
||||
public interface TablistFormatManager {
|
||||
|
||||
void setPrefix(TabPlayer player, String prefix);
|
||||
|
||||
void setName(TabPlayer player, String customName);
|
||||
|
||||
void setSuffix(TabPlayer player, String suffix);
|
||||
|
||||
void resetPrefix(TabPlayer player);
|
||||
|
||||
void resetName(TabPlayer player);
|
||||
|
||||
void resetSuffix(TabPlayer player);
|
||||
|
||||
String getCustomPrefix(TabPlayer player);
|
||||
|
||||
String getCustomName(TabPlayer player);
|
||||
|
||||
String getCustomSuffix(TabPlayer player);
|
||||
|
||||
String getOriginalPrefix(TabPlayer player);
|
||||
|
||||
String getOriginalName(TabPlayer player);
|
||||
|
||||
String getOriginalSuffix(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Changes player's prefix to provided value. Supports placeholders,
|
||||
* as well as any supported RGB formats.
|
||||
*
|
||||
* @param player
|
||||
* player to change prefix of
|
||||
* @param prefix
|
||||
* new prefix value
|
||||
* @see #resetPrefix(TabPlayer)
|
||||
* @see #getCustomPrefix(TabPlayer)
|
||||
* @see #getOriginalPrefix(TabPlayer)
|
||||
*/
|
||||
void setPrefix(TabPlayer player, String prefix);
|
||||
|
||||
/**
|
||||
* Changes player's name to provided value. Supports placeholders,
|
||||
* as well as any supported RGB formats.
|
||||
*
|
||||
* @param player
|
||||
* player to change prefix of
|
||||
* @param customName
|
||||
* new customName value
|
||||
* @see #resetName(TabPlayer)
|
||||
* @see #getCustomName(TabPlayer)
|
||||
* @see #getOriginalName(TabPlayer)
|
||||
*/
|
||||
void setName(TabPlayer player, String customName);
|
||||
|
||||
/**
|
||||
* Changes player's suffix to provided value. Supports placeholders,
|
||||
* as well as any supported RGB formats.
|
||||
*
|
||||
* @param player
|
||||
* player to change prefix of
|
||||
* @param suffix
|
||||
* new suffix value
|
||||
* @see #resetSuffix(TabPlayer)
|
||||
* @see #getCustomSuffix(TabPlayer)
|
||||
* @see #getOriginalSuffix(TabPlayer)
|
||||
*/
|
||||
void setSuffix(TabPlayer player, String suffix);
|
||||
|
||||
/**
|
||||
* Resets player's prefix previously assigned using {@link #setPrefix(TabPlayer, String)}
|
||||
* method. If nothing was set, this method will not do anything.
|
||||
*
|
||||
* @param player
|
||||
* player to reset prefix of
|
||||
* @see #setPrefix(TabPlayer, String)
|
||||
* @see #getCustomPrefix(TabPlayer)
|
||||
* @see #getOriginalPrefix(TabPlayer)
|
||||
*/
|
||||
void resetPrefix(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Resets player's custom name previously assigned using {@link #setName(TabPlayer, String)}
|
||||
* method. If nothing was set, this method will not do anything.
|
||||
*
|
||||
* @param player
|
||||
* player to reset name of
|
||||
* @see #setName(TabPlayer, String)
|
||||
* @see #getCustomName(TabPlayer)
|
||||
* @see #getOriginalName(TabPlayer)
|
||||
*/
|
||||
void resetName(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Resets player's suffix previously assigned using {@link #setSuffix(TabPlayer, String)}
|
||||
* method. If nothing was set, this method will not do anything.
|
||||
*
|
||||
* @param player
|
||||
* player to reset suffix of
|
||||
* @see #setSuffix(TabPlayer, String)
|
||||
* @see #getCustomSuffix(TabPlayer)
|
||||
* @see #getOriginalSuffix(TabPlayer)
|
||||
*/
|
||||
void resetSuffix(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns prefix assigned using {@link #setPrefix(TabPlayer, String)} method.
|
||||
* If the method was not used, {@code null} is returned.
|
||||
*
|
||||
* @param player
|
||||
* player to get custom prefix of
|
||||
* @return player's custom prefix or {@code null} if not defined
|
||||
* @see #setPrefix(TabPlayer, String)
|
||||
* @see #resetPrefix(TabPlayer)
|
||||
* @see #getOriginalPrefix(TabPlayer)
|
||||
*/
|
||||
String getCustomPrefix(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns custom name assigned using {@link #setName(TabPlayer, String)} method.
|
||||
* If the method was not used, {@code null} is returned.
|
||||
*
|
||||
* @param player
|
||||
* player to get custom name of
|
||||
* @return player's custom name or {@code null} if not defined
|
||||
* @see #setName(TabPlayer, String)
|
||||
* @see #resetName(TabPlayer)
|
||||
* @see #getOriginalName(TabPlayer)
|
||||
*/
|
||||
String getCustomName(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns suffix assigned using {@link #setSuffix(TabPlayer, String)} method.
|
||||
* If the method was not used, {@code null} is returned.
|
||||
*
|
||||
* @param player
|
||||
* player to get custom suffix of
|
||||
* @return player's custom suffix or {@code null} if not defined
|
||||
* @see #setSuffix(TabPlayer, String)
|
||||
* @see #resetSuffix(TabPlayer)
|
||||
* @see #getOriginalSuffix(TabPlayer)
|
||||
*/
|
||||
String getCustomSuffix(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns player's original prefix applied using plugin's internal logic.
|
||||
* This value is not affected by API calls in any way.
|
||||
*
|
||||
* @param player
|
||||
* player to get original prefix of
|
||||
* @return player's original prefix
|
||||
* @see #setPrefix(TabPlayer, String)
|
||||
* @see #resetPrefix(TabPlayer)
|
||||
* @see #getCustomPrefix(TabPlayer)
|
||||
*/
|
||||
String getOriginalPrefix(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns player's original name applied using plugin's internal logic.
|
||||
* This value is not affected by API calls in any way.
|
||||
*
|
||||
* @param player
|
||||
* player to get original name of
|
||||
* @return player's original name
|
||||
* @see #setName(TabPlayer, String)
|
||||
* @see #resetName(TabPlayer)
|
||||
* @see #getCustomName(TabPlayer)
|
||||
*/
|
||||
String getOriginalName(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns player's original suffix applied using plugin's internal logic.
|
||||
* This value is not affected by API calls in any way.
|
||||
*
|
||||
* @param player
|
||||
* player to get original suffix of
|
||||
* @return player's original suffix
|
||||
* @see #setSuffix(TabPlayer, String)
|
||||
* @see #resetSuffix(TabPlayer)
|
||||
* @see #getCustomSuffix(TabPlayer)
|
||||
*/
|
||||
String getOriginalSuffix(TabPlayer player);
|
||||
}
|
||||
@@ -6,11 +6,11 @@ package me.neznamy.tab.api.bossbar;
|
||||
*/
|
||||
public enum BarColor {
|
||||
|
||||
PINK,
|
||||
BLUE,
|
||||
RED,
|
||||
GREEN,
|
||||
YELLOW,
|
||||
PURPLE,
|
||||
WHITE
|
||||
PINK,
|
||||
BLUE,
|
||||
RED,
|
||||
GREEN,
|
||||
YELLOW,
|
||||
PURPLE,
|
||||
WHITE
|
||||
}
|
||||
@@ -7,30 +7,30 @@ package me.neznamy.tab.api.bossbar;
|
||||
*/
|
||||
public enum BarStyle {
|
||||
|
||||
PROGRESS("SOLID"),
|
||||
NOTCHED_6("SEGMENTED_6"),
|
||||
NOTCHED_10("SEGMENTED_10"),
|
||||
NOTCHED_12("SEGMENTED_12"),
|
||||
NOTCHED_20("SEGMENTED_20");
|
||||
PROGRESS("SOLID"),
|
||||
NOTCHED_6("SEGMENTED_6"),
|
||||
NOTCHED_10("SEGMENTED_10"),
|
||||
NOTCHED_12("SEGMENTED_12"),
|
||||
NOTCHED_20("SEGMENTED_20");
|
||||
|
||||
/** Style's name used in Bukkit API */
|
||||
private final String bukkitName;
|
||||
/** 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* Returns name of this style in Bukkit API
|
||||
* @return name of this style in Bukkit API
|
||||
*/
|
||||
public String getBukkitName() {
|
||||
return bukkitName;
|
||||
}
|
||||
}
|
||||
@@ -19,167 +19,184 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
*/
|
||||
public interface BossBar {
|
||||
|
||||
/**
|
||||
* Returns name of this BossBar. If it was defined in config, returns
|
||||
* the name specified in config. If it was made using the API, returns
|
||||
* randomly generated ID given to this BossBar when creating.
|
||||
* @return name of BossBar
|
||||
*/
|
||||
String getName();
|
||||
/**
|
||||
* Returns name of this BossBar. If it was defined in config, returns
|
||||
* the name specified in config. If it was made using the API, returns
|
||||
* randomly generated ID given to this BossBar when creating.
|
||||
*
|
||||
* @return name of BossBar
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns randomly generated uuid of this BossBar used internally to match
|
||||
* the BossBar in client with packets or with Bukkit API BossBar instances.
|
||||
* @return uuid of this BossBar
|
||||
*/
|
||||
UUID getUniqueId();
|
||||
/**
|
||||
* Returns randomly generated uuid of this BossBar used internally to match
|
||||
* the BossBar in client with packets or with Bukkit API BossBar instances.
|
||||
*
|
||||
* @return uuid of this BossBar
|
||||
*/
|
||||
UUID getUniqueId();
|
||||
|
||||
/**
|
||||
* Changes BossBar title to specified string, supporting placeholders.
|
||||
* <p>
|
||||
* If title contains new placeholders not used before, they are registered using
|
||||
* standard registration logic and refreshed periodically. No need to call
|
||||
* this method to try to keep placeholder values up to date.
|
||||
* <p>
|
||||
* If specified title is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param title
|
||||
* New title to display in this BossBar
|
||||
*/
|
||||
void setTitle(String title);
|
||||
/**
|
||||
* Changes BossBar title to specified string, supporting placeholders.
|
||||
* <p>
|
||||
* If title contains new placeholders not used before, they are registered using
|
||||
* standard registration logic and refreshed periodically. No need to call
|
||||
* this method to try to keep placeholder values up to date.
|
||||
* <p>
|
||||
* If specified title is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param title
|
||||
* New title to display in this BossBar
|
||||
*/
|
||||
void setTitle(String title);
|
||||
|
||||
/**
|
||||
* Changes BossBar progress to specified string, supporting placeholders.
|
||||
* The string must be a string version of a float value, or a placeholder that
|
||||
* outputs a float value with range 0-100.
|
||||
* <p>
|
||||
* If progress contains new placeholders not used before, they are registered using
|
||||
* standard registration logic and refreshed periodically. No need to call
|
||||
* this method to try to keep placeholder values up to date.
|
||||
* <p>
|
||||
* If specified progress is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param progress
|
||||
* New progress to use in this BossBar
|
||||
*/
|
||||
void setProgress(String progress);
|
||||
/**
|
||||
* Changes BossBar progress to specified string, supporting placeholders.
|
||||
* The string must be a string version of a float value, or a placeholder that
|
||||
* outputs a float value with range 0-100.
|
||||
* <p>
|
||||
* If progress contains new placeholders not used before, they are registered using
|
||||
* standard registration logic and refreshed periodically. No need to call
|
||||
* this method to try to keep placeholder values up to date.
|
||||
* <p>
|
||||
* If specified progress is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param progress
|
||||
* New progress to use in this BossBar
|
||||
*/
|
||||
void setProgress(String progress);
|
||||
|
||||
/**
|
||||
* Changes BossBar progress to specified value ranging from 0 to 100.
|
||||
* <p>
|
||||
* If specified progress is equal to the current one, nothing happens.
|
||||
* @param progress
|
||||
* New progress to use in this BossBar
|
||||
*/
|
||||
void setProgress(float progress);
|
||||
/**
|
||||
* Changes BossBar progress to specified value ranging from 0 to 100.
|
||||
* <p>
|
||||
* If specified progress is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param progress
|
||||
* New progress to use in this BossBar
|
||||
*/
|
||||
void setProgress(float progress);
|
||||
|
||||
/**
|
||||
* Changes BossBar color to specified string, supporting placeholders.
|
||||
* The string must be a string version of one of the supported values,
|
||||
* or a placeholder that outputs one of them.
|
||||
* <p>
|
||||
* If color contains new placeholders not used before, they are registered using
|
||||
* standard registration logic and refreshed periodically. No need to call
|
||||
* this method to try to keep placeholder values up to date.
|
||||
* <p>
|
||||
* If specified color is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param color
|
||||
* New color to use in this BossBar
|
||||
*/
|
||||
void setColor(String color);
|
||||
/**
|
||||
* Changes BossBar color to specified string, supporting placeholders.
|
||||
* The string must be a string version of one of the supported values,
|
||||
* or a placeholder that outputs one of them.
|
||||
* <p>
|
||||
* If color contains new placeholders not used before, they are registered using
|
||||
* standard registration logic and refreshed periodically. No need to call
|
||||
* this method to try to keep placeholder values up to date.
|
||||
* <p>
|
||||
* If specified color is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param color
|
||||
* New color to use in this BossBar
|
||||
*/
|
||||
void setColor(String color);
|
||||
|
||||
/**
|
||||
* Changes BossBar color to specified enum constant.
|
||||
* <p>
|
||||
* If specified color is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param color
|
||||
* New color to use in this BossBar
|
||||
*/
|
||||
void setColor(BarColor color);
|
||||
/**
|
||||
* Changes BossBar color to specified enum constant.
|
||||
* <p>
|
||||
* If specified color is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param color
|
||||
* New color to use in this BossBar
|
||||
*/
|
||||
void setColor(BarColor color);
|
||||
|
||||
/**
|
||||
* Changes BossBar style to specified string, supporting placeholders.
|
||||
* The string must be a string version of one of the supported values,
|
||||
* or a placeholder that outputs one of them.
|
||||
* <p>
|
||||
* If style contains new placeholders not used before, they are registered using
|
||||
* standard registration logic and refreshed periodically. No need to call
|
||||
* this method to try to keep placeholder values up to date.
|
||||
* <p>
|
||||
* If specified style is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param style
|
||||
* New style to use in this BossBar
|
||||
*/
|
||||
void setStyle(String style);
|
||||
/**
|
||||
* Changes BossBar style to specified string, supporting placeholders.
|
||||
* The string must be a string version of one of the supported values,
|
||||
* or a placeholder that outputs one of them.
|
||||
* <p>
|
||||
* If style contains new placeholders not used before, they are registered using
|
||||
* standard registration logic and refreshed periodically. No need to call
|
||||
* this method to try to keep placeholder values up to date.
|
||||
* <p>
|
||||
* If specified style is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param style
|
||||
* New style to use in this BossBar
|
||||
*/
|
||||
void setStyle(String style);
|
||||
|
||||
/**
|
||||
* Changes BossBar style to specified enum constant.
|
||||
* <p>
|
||||
* If specified style is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param style
|
||||
* New style to use in this BossBar
|
||||
*/
|
||||
void setStyle(BarStyle style);
|
||||
/**
|
||||
* Changes BossBar style to specified enum constant.
|
||||
* <p>
|
||||
* If specified style is equal to the current one, nothing happens.
|
||||
*
|
||||
* @param style
|
||||
* New style to use in this BossBar
|
||||
*/
|
||||
void setStyle(BarStyle style);
|
||||
|
||||
/**
|
||||
* Returns current title of the BossBar in raw format. If it contains placeholders,
|
||||
* their raw identifiers are used in the result.
|
||||
* @return title of the BossBar
|
||||
*/
|
||||
String getTitle();
|
||||
/**
|
||||
* Returns current title of the BossBar in raw format. If it contains placeholders,
|
||||
* their raw identifiers are used in the result.
|
||||
*
|
||||
* @return title of the BossBar
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* Returns progress of the BossBar as a string, which is either entered string
|
||||
* containing placeholders or entered number converted to string
|
||||
* @return entered progress as a string
|
||||
*/
|
||||
String getProgress();
|
||||
/**
|
||||
* Returns progress of the BossBar as a string, which is either entered string
|
||||
* containing placeholders or entered number converted to string
|
||||
*
|
||||
* @return entered progress as a string
|
||||
*/
|
||||
String getProgress();
|
||||
|
||||
/**
|
||||
* Returns color of the BossBar as a string, which is either entered string
|
||||
* containing placeholders or entered enum value converted to string
|
||||
* @return entered color as a string
|
||||
*/
|
||||
String getColor();
|
||||
/**
|
||||
* Returns color of the BossBar as a string, which is either entered string
|
||||
* containing placeholders or entered enum value converted to string
|
||||
*
|
||||
* @return entered color as a string
|
||||
*/
|
||||
String getColor();
|
||||
|
||||
/**
|
||||
* Returns style of the BossBar as a string, which is either entered string
|
||||
* containing placeholders or entered enum value converted to string
|
||||
* @return entered style as a string
|
||||
*/
|
||||
String getStyle();
|
||||
/**
|
||||
* Returns style of the BossBar as a string, which is either entered string
|
||||
* containing placeholders or entered enum value converted to string
|
||||
*
|
||||
* @return entered style as a string
|
||||
*/
|
||||
String getStyle();
|
||||
|
||||
/**
|
||||
* Registers this BossBar to specified player.
|
||||
* <p>
|
||||
* If the player already sees this BossBar, nothing happens.
|
||||
*
|
||||
* @param player
|
||||
* Player to register this BossBar to
|
||||
*/
|
||||
void addPlayer(TabPlayer player);
|
||||
/**
|
||||
* Registers this BossBar to specified player.
|
||||
* <p>
|
||||
* If the player already sees this BossBar, nothing happens.
|
||||
*
|
||||
* @param player
|
||||
* Player to register this BossBar to
|
||||
*/
|
||||
void addPlayer(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Unregisters this BossBar from specified player.
|
||||
* <p>
|
||||
* If the player does not see this BossBar, nothing happens.
|
||||
*
|
||||
* @param player
|
||||
* Player to unregister this BossBar from
|
||||
*/
|
||||
void removePlayer(TabPlayer player);
|
||||
/**
|
||||
* Unregisters this BossBar from specified player.
|
||||
* <p>
|
||||
* If the player does not see this BossBar, nothing happens.
|
||||
*
|
||||
* @param player
|
||||
* Player to unregister this BossBar from
|
||||
*/
|
||||
void removePlayer(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns list of players who can see this BossBar.
|
||||
* <p>
|
||||
* The returned list can only be used to read. Writing
|
||||
* will not work properly. For adding/removing players see {@link #addPlayer(TabPlayer)}
|
||||
* and {@link #removePlayer(TabPlayer)}.
|
||||
* @return List of players seeing this BossBar
|
||||
*/
|
||||
List<TabPlayer> getPlayers();
|
||||
/**
|
||||
* Returns list of players who can see this BossBar.
|
||||
* <p>
|
||||
* The returned list can only be used to read. Writing
|
||||
* will not work properly. For adding/removing players see {@link #addPlayer(TabPlayer)}
|
||||
* and {@link #removePlayer(TabPlayer)}.
|
||||
*
|
||||
* @return List of players seeing this BossBar
|
||||
*/
|
||||
List<TabPlayer> getPlayers();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if specified player can see this BossBar, {@code false} if not
|
||||
*
|
||||
* @param player
|
||||
* Player to check for
|
||||
* @return {@code true} if contains, {@code false} if not
|
||||
*/
|
||||
boolean containsPlayer(TabPlayer player);
|
||||
}
|
||||
@@ -4,164 +4,162 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
/**
|
||||
* An interface allowing work with BossBars such as creating, sending
|
||||
* and toggling.
|
||||
* <p>
|
||||
* Instance can be obtained using {@link me.neznamy.tab.api.TabAPI#getBossBarManager()}.
|
||||
* Instance can be obtained using {@link TabAPI#getBossBarManager()}.
|
||||
* This requires the BossBar feature to be enabled in config, otherwise the method will
|
||||
* return null.
|
||||
* return {@code null}.
|
||||
*/
|
||||
public interface BossBarManager {
|
||||
|
||||
/**
|
||||
* Creates BossBar with specified parameters and registers it to BossBar manager,
|
||||
* allowing to retrieve it later.
|
||||
*
|
||||
* @param title
|
||||
* Title to display, supports placeholders
|
||||
*
|
||||
* @param progress
|
||||
* Progress to use. Must be between 0 and 100
|
||||
*
|
||||
* @param color
|
||||
* Color to use
|
||||
*
|
||||
* @param style
|
||||
* Style to use
|
||||
* @return The newly created BossBar
|
||||
*/
|
||||
BossBar createBossBar(String title, float progress, BarColor color, BarStyle style);
|
||||
/**
|
||||
* Creates BossBar with specified parameters and registers it to BossBar manager,
|
||||
* allowing to retrieve it later.
|
||||
*
|
||||
* @param title
|
||||
* Title to display, supports placeholders
|
||||
* @param progress
|
||||
* Progress to use. Must be between 0 and 100
|
||||
* @param color
|
||||
* Color to use
|
||||
* @param style
|
||||
* Style to use
|
||||
* @return The newly created BossBar
|
||||
*/
|
||||
BossBar createBossBar(String title, float progress, BarColor color, BarStyle style);
|
||||
|
||||
/**
|
||||
* Creates BossBar with specified parameters and registers it to BossBar manager,
|
||||
* allowing to retrieve it later.
|
||||
*
|
||||
* @param title
|
||||
* Title to display, supports placeholders
|
||||
*
|
||||
* @param progress
|
||||
* Progress to use. Must be a string version of a number, or
|
||||
* a placeholder that returns a number
|
||||
*
|
||||
* @param color
|
||||
* Color to use. Must be a string version of one of enum constants, or
|
||||
* a placeholder that returns one of them
|
||||
*
|
||||
* @param style
|
||||
* Style to use. Must be a string version of one of enum constants, or
|
||||
* a placeholder that returns one of them
|
||||
* @return The newly created BossBar
|
||||
*/
|
||||
BossBar createBossBar(String title, String progress, String color, String style);
|
||||
/**
|
||||
* Creates BossBar with specified parameters and registers it to BossBar manager,
|
||||
* allowing to retrieve it later.
|
||||
*
|
||||
* @param title
|
||||
* Title to display, supports placeholders
|
||||
* @param progress
|
||||
* Progress to use. Must be a string version of a number, or
|
||||
* a placeholder that returns a number
|
||||
* @param color
|
||||
* Color to use. Must be a string version of one of enum constants, or
|
||||
* a placeholder that returns one of them
|
||||
* @param style
|
||||
* Style to use. Must be a string version of one of enum constants, or
|
||||
* a placeholder that returns one of them
|
||||
* @return The newly created BossBar
|
||||
*/
|
||||
BossBar createBossBar(String title, String progress, String color, String style);
|
||||
|
||||
/**
|
||||
* Returns registered BossBar with given name. For BossBars from config it is the name
|
||||
* they were created with, for API BossBars their randomly generated name, which can be
|
||||
* retrieved using {@link me.neznamy.tab.api.bossbar.BossBar#getName()}.
|
||||
* Returns null if no such BossBar exists.
|
||||
*
|
||||
* @param name
|
||||
* Name of registered BossBar
|
||||
* @return BossBar with specified name
|
||||
*/
|
||||
BossBar getBossBar(String name);
|
||||
/**
|
||||
* Returns registered BossBar with given name. For BossBars from config it is the name
|
||||
* they were created with, for API BossBars their randomly generated name, which can be
|
||||
* retrieved using {@link me.neznamy.tab.api.bossbar.BossBar#getName()}.
|
||||
* Returns null if no such BossBar exists.
|
||||
*
|
||||
* @param name
|
||||
* Name of registered BossBar
|
||||
* @return BossBar with specified name
|
||||
*/
|
||||
BossBar getBossBar(String name);
|
||||
|
||||
/**
|
||||
* Returns registered BossBar with specified UUID. This UUID is randomly generated when
|
||||
* BossBar is created and used in packets / to match which the one from Bukkit API.
|
||||
* Returns null if no such BossBar exists.
|
||||
*
|
||||
* @param id
|
||||
* UUID to return BossBar by
|
||||
* @return BossBar with specified uuid
|
||||
*/
|
||||
BossBar getBossBar(UUID id);
|
||||
/**
|
||||
* Returns registered BossBar with specified UUID. This UUID is randomly generated when
|
||||
* BossBar is created and used in packets / to match which the one from Bukkit API.
|
||||
* Returns null if no such BossBar exists.
|
||||
*
|
||||
* @param id
|
||||
* UUID to return BossBar by
|
||||
* @return BossBar with specified uuid
|
||||
*/
|
||||
BossBar getBossBar(UUID id);
|
||||
|
||||
/**
|
||||
* Returns a map of all registered BossBars. This includes both BossBars from config
|
||||
* and those registered via API.
|
||||
* <p>
|
||||
* Map key is BossBar's name available in {@link me.neznamy.tab.api.bossbar.BossBar#getName()},
|
||||
* map value is the BossBar interface.
|
||||
* @return all registered BossBars
|
||||
*/
|
||||
Map<String, BossBar> getRegisteredBossBars();
|
||||
/**
|
||||
* Returns a map of all registered BossBars. This includes both BossBars from config
|
||||
* and those registered via API.
|
||||
* <p>
|
||||
* Map key is BossBar's name available in {@link me.neznamy.tab.api.bossbar.BossBar#getName()},
|
||||
* map value is the BossBar interface.
|
||||
*
|
||||
* @return all registered BossBars
|
||||
*/
|
||||
Map<String, BossBar> getRegisteredBossBars();
|
||||
|
||||
/**
|
||||
* Toggles BossBar for this player and sends toggle message if {@code sendToggleMessage} is true.
|
||||
*
|
||||
* @param player
|
||||
* Player to toggle BossBar for
|
||||
*
|
||||
* @param sendToggleMessage
|
||||
* {@code true} if toggle message should be sent, {@code false} if not
|
||||
* @see #hasBossBarVisible(TabPlayer)
|
||||
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
|
||||
*/
|
||||
void toggleBossBar(TabPlayer player, boolean sendToggleMessage);
|
||||
/**
|
||||
* Toggles BossBar for this player and sends toggle message if {@code sendToggleMessage} is true.
|
||||
*
|
||||
* @param player
|
||||
* Player to toggle BossBar for
|
||||
* @param sendToggleMessage
|
||||
* {@code true} if toggle message should be sent, {@code false} if not
|
||||
* @see #hasBossBarVisible(TabPlayer)
|
||||
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
|
||||
*/
|
||||
void toggleBossBar(TabPlayer player, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Returns true if player can see BossBars, false if toggled via command or API.
|
||||
*
|
||||
* @param player
|
||||
* Player to check toggle status of
|
||||
* @return {@code true} if visible, {@code false} if disabled (toggled)
|
||||
* @see #toggleBossBar(TabPlayer, boolean)
|
||||
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
|
||||
*/
|
||||
boolean hasBossBarVisible(TabPlayer player);
|
||||
/**
|
||||
* Returns true if player can see BossBars, false if toggled via command or API.
|
||||
*
|
||||
* @param player
|
||||
* Player to check toggle status of
|
||||
* @return {@code true} if visible, {@code false} if disabled (toggled)
|
||||
* @see #toggleBossBar(TabPlayer, boolean)
|
||||
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
|
||||
*/
|
||||
boolean hasBossBarVisible(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Sets BossBar visibility of player to set value. If value did not change, nothing happens.
|
||||
* If visibility changed, toggle message is sent if {@code sendToggleMessage} is {@code true}.
|
||||
*
|
||||
* @param player
|
||||
* Player to set BossBar visibility of
|
||||
* @param visible
|
||||
* {@code true} if BossBar should be visible, {@code false} if not
|
||||
* @param sendToggleMessage
|
||||
* {@code true} if toggle message should be sent if value changed, {@code false} if not
|
||||
* @see #toggleBossBar(TabPlayer, boolean)
|
||||
* @see #hasBossBarVisible(TabPlayer)
|
||||
*/
|
||||
void setBossBarVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
|
||||
/**
|
||||
* Sets BossBar visibility of player to set value. If value did not change, nothing happens.
|
||||
* If visibility changed, toggle message is sent if {@code sendToggleMessage} is {@code true}.
|
||||
*
|
||||
* @param player
|
||||
* Player to set BossBar visibility of
|
||||
* @param visible
|
||||
* {@code true} if BossBar should be visible, {@code false} if not
|
||||
* @param sendToggleMessage
|
||||
* {@code true} if toggle message should be sent if value changed, {@code false} if not
|
||||
* @see #toggleBossBar(TabPlayer, boolean)
|
||||
* @see #hasBossBarVisible(TabPlayer)
|
||||
*/
|
||||
void setBossBarVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Temporarily displays registered BossBar to player for specified amount of time in milliseconds.
|
||||
* @param player
|
||||
* Player to show BossBar to
|
||||
* @param bossBar
|
||||
* Name of registered BossBar to show
|
||||
* @param duration
|
||||
* In milliseconds for how long should BossBar be displayed
|
||||
* @throws IllegalArgumentException
|
||||
* if no BossBar with specified name exists
|
||||
* @see #announceBossBar(String, int)
|
||||
* @see #getAnnouncedBossBars()
|
||||
*/
|
||||
void sendBossBarTemporarily(TabPlayer player, String bossBar, int duration);
|
||||
/**
|
||||
* Temporarily displays registered BossBar to player for specified amount of time in milliseconds.
|
||||
*
|
||||
* @param player
|
||||
* Player to show BossBar to
|
||||
* @param bossBar
|
||||
* Name of registered BossBar to show
|
||||
* @param duration
|
||||
* In milliseconds for how long should BossBar be displayed
|
||||
* @throws IllegalArgumentException
|
||||
* if no BossBar with specified name exists
|
||||
* @see #announceBossBar(String, int)
|
||||
* @see #getAnnouncedBossBars()
|
||||
*/
|
||||
void sendBossBarTemporarily(TabPlayer player, String bossBar, int duration);
|
||||
|
||||
/**
|
||||
* Temporarily displays registered BossBar to all players for specified amount of time in milliseconds.
|
||||
* @param bossBar
|
||||
* Name of registered BossBar to show
|
||||
* @param duration
|
||||
* In milliseconds for how long should BossBar be displayed
|
||||
* @throws IllegalArgumentException
|
||||
* if no BossBar with specified name exists
|
||||
* @see #sendBossBarTemporarily(TabPlayer, String, int)
|
||||
* @see #getAnnouncedBossBars()
|
||||
*/
|
||||
void announceBossBar(String bossBar, int duration);
|
||||
/**
|
||||
* Temporarily displays registered BossBar to all players for specified amount of time in milliseconds.
|
||||
*
|
||||
* @param bossBar
|
||||
* Name of registered BossBar to show
|
||||
* @param duration
|
||||
* In milliseconds for how long should BossBar be displayed
|
||||
* @throws IllegalArgumentException
|
||||
* if no BossBar with specified name exists
|
||||
* @see #sendBossBarTemporarily(TabPlayer, String, int)
|
||||
* @see #getAnnouncedBossBars()
|
||||
*/
|
||||
void announceBossBar(String bossBar, int duration);
|
||||
|
||||
/**
|
||||
* Returns list of BossBars which are currently being announced.
|
||||
* @return list of currently active BossBar announcements
|
||||
* @see #sendBossBarTemporarily(TabPlayer, String, int)
|
||||
* @see #announceBossBar(String, int)
|
||||
*/
|
||||
List<BossBar> getAnnouncedBossBars();
|
||||
/**
|
||||
* Returns list of BossBars which are currently being announced.
|
||||
*
|
||||
* @return list of currently active BossBar announcements
|
||||
* @see #sendBossBarTemporarily(TabPlayer, String, int)
|
||||
* @see #announceBossBar(String, int)
|
||||
*/
|
||||
List<BossBar> getAnnouncedBossBars();
|
||||
}
|
||||
@@ -7,53 +7,56 @@ import me.neznamy.tab.api.util.Preconditions;
|
||||
*/
|
||||
public class ChatClickable {
|
||||
|
||||
/** Click action */
|
||||
private final EnumClickAction action;
|
||||
/** Click action */
|
||||
private final EnumClickAction action;
|
||||
|
||||
/** Click value */
|
||||
private final String value;
|
||||
/** 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 cannot be null");
|
||||
Preconditions.checkNotNull(value, "hover value cannot be null");
|
||||
this.action = action;
|
||||
this.value = 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 EnumClickAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns click action defined in constructor
|
||||
* @return click action
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Returns click action defined in constructor
|
||||
*
|
||||
* @return click action
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum for all possible click actions
|
||||
*/
|
||||
public enum EnumClickAction {
|
||||
|
||||
OPEN_URL,
|
||||
RUN_COMMAND,
|
||||
CHANGE_PAGE, //since 1.8
|
||||
SUGGEST_COMMAND,
|
||||
COPY_TO_CLIPBOARD //since 1.15
|
||||
}
|
||||
/**
|
||||
* Enum for all possible click actions
|
||||
*/
|
||||
public enum EnumClickAction {
|
||||
|
||||
OPEN_URL,
|
||||
RUN_COMMAND,
|
||||
CHANGE_PAGE, //since 1.8
|
||||
SUGGEST_COMMAND,
|
||||
COPY_TO_CLIPBOARD //since 1.15
|
||||
}
|
||||
}
|
||||
@@ -3,36 +3,39 @@ package me.neznamy.tab.api.chat;
|
||||
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) {
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toRawText() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
@Override
|
||||
public String toRawText() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -7,51 +7,54 @@ import me.neznamy.tab.api.util.Preconditions;
|
||||
*/
|
||||
public class ChatHoverable {
|
||||
|
||||
/** Hover action */
|
||||
private final EnumHoverAction action;
|
||||
/** Hover action */
|
||||
private final EnumHoverAction action;
|
||||
|
||||
/** Hover value */
|
||||
private final IChatBaseComponent value;
|
||||
/** 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 cannot be null");
|
||||
Preconditions.checkNotNull(value, "hover value cannot be null");
|
||||
this.action = action;
|
||||
this.value = 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 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;
|
||||
}
|
||||
/**
|
||||
* Returns hover value defined in constructor
|
||||
*
|
||||
* @return hover value
|
||||
*/
|
||||
public IChatBaseComponent getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum for all possible hover actions
|
||||
*/
|
||||
public enum EnumHoverAction {
|
||||
/**
|
||||
* Enum for all possible hover actions
|
||||
*/
|
||||
public enum EnumHoverAction {
|
||||
|
||||
SHOW_TEXT,
|
||||
SHOW_ITEM,
|
||||
SHOW_ENTITY
|
||||
}
|
||||
SHOW_TEXT,
|
||||
SHOW_ITEM,
|
||||
SHOW_ENTITY
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package me.neznamy.tab.api.chat;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
@@ -11,271 +12,315 @@ import me.neznamy.tab.api.chat.ChatHoverable.EnumHoverAction;
|
||||
|
||||
public class ChatModifier {
|
||||
|
||||
private TextColor color;
|
||||
private TextColor color;
|
||||
|
||||
private Boolean bold;
|
||||
private Boolean italic;
|
||||
private Boolean underlined;
|
||||
private Boolean strikethrough;
|
||||
private Boolean obfuscated;
|
||||
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) {
|
||||
this.color = modifier.color == null ? null : new TextColor(modifier.color);
|
||||
this.bold = modifier.bold;
|
||||
this.italic = modifier.italic;
|
||||
this.underlined = modifier.underlined;
|
||||
this.strikethrough = modifier.strikethrough;
|
||||
this.obfuscated = modifier.obfuscated;
|
||||
this.clickEvent = modifier.clickEvent == null ? null : new ChatClickable(modifier.clickEvent.getAction(), modifier.clickEvent.getValue());
|
||||
this.hoverEvent = modifier.hoverEvent == null ? null : new ChatHoverable(modifier.hoverEvent.getAction(), modifier.hoverEvent.getValue());
|
||||
this.font = modifier.font;
|
||||
this.targetVersion = modifier.targetVersion;
|
||||
}
|
||||
private ChatClickable clickEvent;
|
||||
private ChatHoverable hoverEvent;
|
||||
|
||||
public TextColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(TextColor color) {
|
||||
this.color = color;
|
||||
}
|
||||
private String font;
|
||||
|
||||
public Boolean getBold() {
|
||||
return bold;
|
||||
}
|
||||
private ProtocolVersion targetVersion;
|
||||
|
||||
public void setBold(Boolean bold) {
|
||||
this.bold = bold;
|
||||
}
|
||||
public ChatModifier() {
|
||||
}
|
||||
|
||||
public Boolean getItalic() {
|
||||
return italic;
|
||||
}
|
||||
public ChatModifier(ChatModifier modifier) {
|
||||
Preconditions.checkNotNull(modifier, "modifier");
|
||||
this.color = modifier.color == null ? null : new TextColor(modifier.color);
|
||||
this.bold = modifier.bold;
|
||||
this.italic = modifier.italic;
|
||||
this.underlined = modifier.underlined;
|
||||
this.strikethrough = modifier.strikethrough;
|
||||
this.obfuscated = modifier.obfuscated;
|
||||
this.clickEvent = modifier.clickEvent == null ? null : new ChatClickable(modifier.clickEvent.getAction(), modifier.clickEvent.getValue());
|
||||
this.hoverEvent = modifier.hoverEvent == null ? null : new ChatHoverable(modifier.hoverEvent.getAction(), modifier.hoverEvent.getValue());
|
||||
this.font = modifier.font;
|
||||
this.targetVersion = modifier.targetVersion;
|
||||
}
|
||||
|
||||
public void setItalic(Boolean italic) {
|
||||
this.italic = italic;
|
||||
}
|
||||
public TextColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public Boolean getUnderlined() {
|
||||
return underlined;
|
||||
}
|
||||
public void setColor(TextColor color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public void setUnderlined(Boolean underlined) {
|
||||
this.underlined = underlined;
|
||||
}
|
||||
public Boolean getBold() {
|
||||
return bold;
|
||||
}
|
||||
|
||||
public Boolean getStrikethrough() {
|
||||
return strikethrough;
|
||||
}
|
||||
public void setBold(Boolean bold) {
|
||||
this.bold = bold;
|
||||
}
|
||||
|
||||
public void setStrikethrough(Boolean strikethrough) {
|
||||
this.strikethrough = strikethrough;
|
||||
}
|
||||
public Boolean getItalic() {
|
||||
return italic;
|
||||
}
|
||||
|
||||
public Boolean getObfuscated() {
|
||||
return obfuscated;
|
||||
}
|
||||
public void setItalic(Boolean italic) {
|
||||
this.italic = italic;
|
||||
}
|
||||
|
||||
public void setObfuscated(Boolean obfuscated) {
|
||||
this.obfuscated = obfuscated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if bold is defined and set to true, false otherwise
|
||||
* @return true if bold is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isBold(){
|
||||
return Boolean.TRUE.equals(bold);
|
||||
}
|
||||
public Boolean getUnderlined() {
|
||||
return underlined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if italic is defined and set to true, false otherwise
|
||||
* @return true if italic is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isItalic(){
|
||||
return Boolean.TRUE.equals(italic);
|
||||
}
|
||||
public void setUnderlined(Boolean underlined) {
|
||||
this.underlined = underlined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if underlined is defined and set to true, false otherwise
|
||||
* @return true if underlined is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isUnderlined(){
|
||||
return Boolean.TRUE.equals(underlined);
|
||||
}
|
||||
public Boolean getStrikethrough() {
|
||||
return strikethrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if strikethrough is defined and set to true, false otherwise
|
||||
* @return true if strikethrough is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isStrikethrough(){
|
||||
return Boolean.TRUE.equals(strikethrough);
|
||||
}
|
||||
public void setStrikethrough(Boolean strikethrough) {
|
||||
this.strikethrough = strikethrough;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if obfuscation is defined and set to true, false otherwise
|
||||
* @return true if obfuscation is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isObfuscated(){
|
||||
return Boolean.TRUE.equals(obfuscated);
|
||||
}
|
||||
public Boolean getObfuscated() {
|
||||
return obfuscated;
|
||||
}
|
||||
|
||||
public ChatClickable getClickEvent() {
|
||||
return clickEvent;
|
||||
}
|
||||
public void setObfuscated(Boolean obfuscated) {
|
||||
this.obfuscated = obfuscated;
|
||||
}
|
||||
|
||||
public void setClickEvent(ChatClickable clickEvent) {
|
||||
this.clickEvent = clickEvent;
|
||||
}
|
||||
/**
|
||||
* Returns true if bold is defined and set to true, false otherwise
|
||||
*
|
||||
* @return true if bold is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isBold(){
|
||||
return Boolean.TRUE.equals(bold);
|
||||
}
|
||||
|
||||
public ChatHoverable getHoverEvent() {
|
||||
return hoverEvent;
|
||||
}
|
||||
/**
|
||||
* Returns true if italic is defined and set to true, false otherwise
|
||||
*
|
||||
* @return true if italic is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isItalic(){
|
||||
return Boolean.TRUE.equals(italic);
|
||||
}
|
||||
|
||||
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) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.OPEN_URL, url);
|
||||
}
|
||||
/**
|
||||
* Returns true if underlined is defined and set to true, false otherwise
|
||||
*
|
||||
* @return true if underlined is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isUnderlined(){
|
||||
return Boolean.TRUE.equals(underlined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click action to RUN_COMMAND and command to given value
|
||||
* @param command - command to perform, might be without / to send a chat message
|
||||
*/
|
||||
public void onClickRunCommand(String command) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.RUN_COMMAND, command);
|
||||
}
|
||||
/**
|
||||
* Returns true if strikethrough is defined and set to true, false otherwise
|
||||
*
|
||||
* @return true if strikethrough is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isStrikethrough(){
|
||||
return Boolean.TRUE.equals(strikethrough);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click action to SUGGEST_COMMAND and command to given value
|
||||
* @param command - command to suggest
|
||||
*/
|
||||
public void onClickSuggestCommand(String command) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.SUGGEST_COMMAND, command);
|
||||
}
|
||||
/**
|
||||
* Returns true if obfuscation is defined and set to true, false otherwise
|
||||
*
|
||||
* @return true if obfuscation is defined and set to true, false otherwise
|
||||
*/
|
||||
public boolean isObfuscated(){
|
||||
return Boolean.TRUE.equals(obfuscated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click action to CHANGE_PAGE and page id to given value
|
||||
* @param newPage - id of new page
|
||||
*/
|
||||
public void onClickChangePage(int newPage) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() < 8) throw new UnsupportedOperationException("change_page click action is not supported on <1.8");
|
||||
clickEvent = new ChatClickable(EnumClickAction.CHANGE_PAGE, String.valueOf(newPage));
|
||||
}
|
||||
|
||||
public void onClick(EnumClickAction action, String value) {
|
||||
clickEvent = new ChatClickable(action, value);
|
||||
}
|
||||
public ChatClickable getClickEvent() {
|
||||
return clickEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets hover action to SHOW_TEXT and text to given value
|
||||
* @param text - text to show
|
||||
*/
|
||||
public void onHoverShowText(IChatBaseComponent text) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_TEXT, text);
|
||||
}
|
||||
public void setClickEvent(ChatClickable clickEvent) {
|
||||
this.clickEvent = clickEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets hover action to SHOW_ITEM and item to given value
|
||||
* @param serializedItem - item to show
|
||||
*/
|
||||
public void onHoverShowItem(String serializedItem) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ITEM, new IChatBaseComponent(serializedItem));
|
||||
}
|
||||
public ChatHoverable getHoverEvent() {
|
||||
return hoverEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets hover action to SHOW_ENTITY and entity data to given values
|
||||
* @param type - entity type
|
||||
* @param id - entity uuid
|
||||
* @param name - entity custom name
|
||||
*/
|
||||
public void onHoverShowEntity(String type, UUID id, String name) {
|
||||
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 onHover(EnumHoverAction action, IChatBaseComponent value) {
|
||||
hoverEvent = new ChatHoverable(action, value);
|
||||
}
|
||||
public void setHoverEvent(ChatHoverable hoverEvent) {
|
||||
this.hoverEvent = hoverEvent;
|
||||
}
|
||||
|
||||
public String getFont() {
|
||||
return font;
|
||||
}
|
||||
/**
|
||||
* 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");
|
||||
clickEvent = new ChatClickable(EnumClickAction.OPEN_URL, url);
|
||||
}
|
||||
|
||||
public void setFont(String font) {
|
||||
this.font = font;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONObject serialize() {
|
||||
JSONObject json = new JSONObject();
|
||||
if (color != null) json.put("color", targetVersion.getMinorVersion() >= 16 ? color.toString() : color.getLegacyColor().toString().toLowerCase());
|
||||
if (bold != null) json.put("bold", bold);
|
||||
if (italic != null) json.put("italic", italic);
|
||||
if (underlined != null) json.put("underlined", underlined);
|
||||
if (strikethrough != null) json.put("strikethrough", strikethrough);
|
||||
if (obfuscated != null) json.put("obfuscated", obfuscated);
|
||||
if (clickEvent != null) {
|
||||
JSONObject click = new JSONObject();
|
||||
click.put("action", clickEvent.getAction().toString().toLowerCase());
|
||||
click.put("value", clickEvent.getValue());
|
||||
json.put("clickEvent", click);
|
||||
}
|
||||
if (hoverEvent != null) {
|
||||
JSONObject hover = new JSONObject();
|
||||
hover.put("action", hoverEvent.getAction().toString().toLowerCase());
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) {
|
||||
hover.put("contents", hoverEvent.getValue());
|
||||
} else {
|
||||
hover.put("value", TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 ?
|
||||
hoverEvent.getValue() : hoverEvent.getValue().toRawText());
|
||||
}
|
||||
json.put("hoverEvent", hover);
|
||||
}
|
||||
if (font != null) json.put("font", font);
|
||||
return json;
|
||||
}
|
||||
|
||||
public String getMagicCodes() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (isBold()) builder.append(EnumChatFormat.BOLD.getFormat());
|
||||
if (isItalic()) builder.append(EnumChatFormat.ITALIC.getFormat());
|
||||
if (isUnderlined()) builder.append(EnumChatFormat.UNDERLINE.getFormat());
|
||||
if (isStrikethrough()) builder.append(EnumChatFormat.STRIKETHROUGH.getFormat());
|
||||
if (isObfuscated()) builder.append(EnumChatFormat.OBFUSCATED.getFormat());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void setTargetVersion(ProtocolVersion targetVersion) {
|
||||
this.targetVersion = targetVersion;
|
||||
}
|
||||
|
||||
public ProtocolVersion getTargetVersion() {
|
||||
return targetVersion;
|
||||
}
|
||||
/**
|
||||
* Sets click action to RUN_COMMAND and command to given value
|
||||
*
|
||||
* @param command
|
||||
* command to perform, might be without / to send a chat message
|
||||
*/
|
||||
public void onClickRunCommand(String command) {
|
||||
Preconditions.checkNotNull(command, "command");
|
||||
clickEvent = new ChatClickable(EnumClickAction.RUN_COMMAND, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click action to SUGGEST_COMMAND and command to given value
|
||||
*
|
||||
* @param command
|
||||
* command to suggest
|
||||
*/
|
||||
public void onClickSuggestCommand(String command) {
|
||||
Preconditions.checkNotNull(command, "command");
|
||||
clickEvent = new ChatClickable(EnumClickAction.SUGGEST_COMMAND, command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click action to CHANGE_PAGE and page id to given value
|
||||
*
|
||||
* @param newPage
|
||||
* id of new page
|
||||
*/
|
||||
public void onClickChangePage(int newPage) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() < 8) throw new UnsupportedOperationException("change_page click action is not supported on <1.8");
|
||||
clickEvent = new ChatClickable(EnumClickAction.CHANGE_PAGE, String.valueOf(newPage));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click action to COPY_TO_CLIPBOARD and text to provided value
|
||||
*
|
||||
* @param text
|
||||
* text to copy to clipboard on click
|
||||
*/
|
||||
public void onClickCopyToClipBoard(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
clickEvent = new ChatClickable(EnumClickAction.COPY_TO_CLIPBOARD, text);
|
||||
}
|
||||
|
||||
public void onClick(EnumClickAction action, String value) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
clickEvent = new ChatClickable(action, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets hover action to SHOW_TEXT and text to given value
|
||||
*
|
||||
* @param text
|
||||
* text to show
|
||||
*/
|
||||
public void onHoverShowText(IChatBaseComponent text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_TEXT, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets hover action to SHOW_ITEM and item to given value
|
||||
*
|
||||
* @param serializedItem
|
||||
* item to show
|
||||
*/
|
||||
public void onHoverShowItem(String serializedItem) {
|
||||
Preconditions.checkNotNull(serializedItem, "item");
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ITEM, new IChatBaseComponent(serializedItem));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets hover action to SHOW_ENTITY and entity data to given values
|
||||
*
|
||||
* @param type
|
||||
* entity type
|
||||
* @param id
|
||||
* entity uuid
|
||||
* @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 onHover(EnumHoverAction action, IChatBaseComponent value) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(value, "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();
|
||||
if (color != null) json.put("color", targetVersion.getMinorVersion() >= 16 ? color.toString() : color.getLegacyColor().toString().toLowerCase());
|
||||
if (bold != null) json.put("bold", bold);
|
||||
if (italic != null) json.put("italic", italic);
|
||||
if (underlined != null) json.put("underlined", underlined);
|
||||
if (strikethrough != null) json.put("strikethrough", strikethrough);
|
||||
if (obfuscated != null) json.put("obfuscated", obfuscated);
|
||||
if (clickEvent != null) {
|
||||
JSONObject click = new JSONObject();
|
||||
click.put("action", clickEvent.getAction().toString().toLowerCase());
|
||||
click.put("value", clickEvent.getValue());
|
||||
json.put("clickEvent", click);
|
||||
}
|
||||
if (hoverEvent != null) {
|
||||
JSONObject hover = new JSONObject();
|
||||
hover.put("action", hoverEvent.getAction().toString().toLowerCase());
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) {
|
||||
hover.put("contents", hoverEvent.getValue());
|
||||
} else {
|
||||
hover.put("value", TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 ?
|
||||
hoverEvent.getValue() : hoverEvent.getValue().toRawText());
|
||||
}
|
||||
json.put("hoverEvent", hover);
|
||||
}
|
||||
if (font != null) json.put("font", font);
|
||||
return json;
|
||||
}
|
||||
|
||||
public String getMagicCodes() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (isBold()) builder.append(EnumChatFormat.BOLD.getFormat());
|
||||
if (isItalic()) builder.append(EnumChatFormat.ITALIC.getFormat());
|
||||
if (isUnderlined()) builder.append(EnumChatFormat.UNDERLINE.getFormat());
|
||||
if (isStrikethrough()) builder.append(EnumChatFormat.STRIKETHROUGH.getFormat());
|
||||
if (isObfuscated()) builder.append(EnumChatFormat.OBFUSCATED.getFormat());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void setTargetVersion(ProtocolVersion targetVersion) {
|
||||
this.targetVersion = targetVersion;
|
||||
}
|
||||
|
||||
public ProtocolVersion getTargetVersion() {
|
||||
return targetVersion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class representing a chat component that was serialized before,
|
||||
* but deserialize was requested in PacketPlayOutPlayerInfo packet.
|
||||
* This is used to read and modify the packet and then rewrite it.
|
||||
* However, display name components must be deserialized to properly
|
||||
* forward them in the new packet, but its value is not actually
|
||||
* used anywhere.
|
||||
* <p>
|
||||
* To avoid this heavy and completely unnecessary operation, the original
|
||||
* string is saved and returned during serialization. If any read/write
|
||||
* operation is performed, the string is fully deserialized and this object
|
||||
* will act like a normal chat component.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
|
||||
/** The original serialized component string */
|
||||
private final String json;
|
||||
|
||||
/**
|
||||
* Flag tracking whether this component was fully deserialized
|
||||
* in case of a read operation to avoid repeating the deserialization
|
||||
* process on every request.
|
||||
*/
|
||||
private boolean deserialized;
|
||||
|
||||
/**
|
||||
* Flag tracking whether this component was modified, which means
|
||||
* a new string has to be created instead of returning the input one.
|
||||
*/
|
||||
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();
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(ProtocolVersion clientVersion) {
|
||||
if (modified) return super.toString(clientVersion);
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IChatBaseComponent> getExtra(){
|
||||
if (!deserialized) deserialize();
|
||||
return super.getExtra();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
if (!deserialized) deserialize();
|
||||
return super.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatModifier getModifier() {
|
||||
if (!deserialized) deserialize();
|
||||
return super.getModifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
|
||||
if (!deserialized) deserialize();
|
||||
modified = true;
|
||||
return super.setExtra(components);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
if (!deserialized) deserialize();
|
||||
modified = true;
|
||||
super.addExtra(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
if (!deserialized) deserialize();
|
||||
modified = true;
|
||||
super.setModifier(modifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a full deserialization process on this component.
|
||||
*/
|
||||
void deserialize() {
|
||||
deserialized = true;
|
||||
if (json.startsWith("\"") && json.endsWith("\"") && json.length() > 1) {
|
||||
//simple component with only text used, minecraft serializer outputs the text in quotes instead of full json
|
||||
setText(json.substring(1, json.length()-1));
|
||||
return;
|
||||
}
|
||||
JSONObject jsonObject;
|
||||
try {
|
||||
jsonObject = (JSONObject) new JSONParser().parse(json);
|
||||
} catch (ParseException e) {
|
||||
TabAPI.getInstance().logError("Failed to deserialize json component " + json, e);
|
||||
return;
|
||||
}
|
||||
/* if (jsonObject.containsKey("type")) {
|
||||
return new ChatComponentEntity((String) jsonObject.get("type"), UUID.fromString((String) jsonObject.get("id")), IChatBaseComponent.deserialize(jsonObject.get("name").toString()).toFlatText());
|
||||
}*/
|
||||
setText((String) jsonObject.get("text"));
|
||||
getModifier().setBold(getBoolean(jsonObject, "bold"));
|
||||
getModifier().setItalic(getBoolean(jsonObject, "italic"));
|
||||
getModifier().setUnderlined(getBoolean(jsonObject, "underlined"));
|
||||
getModifier().setStrikethrough(getBoolean(jsonObject, "strikethrough"));
|
||||
getModifier().setObfuscated(getBoolean(jsonObject, "obfuscated"));
|
||||
getModifier().setColor(TextColor.fromString(((String) jsonObject.get("color"))));
|
||||
if (jsonObject.containsKey("clickEvent")) {
|
||||
JSONObject clickEvent = (JSONObject) jsonObject.get("clickEvent");
|
||||
String action = (String) clickEvent.get("action");
|
||||
String value = clickEvent.get("value").toString();
|
||||
getModifier().onClick(ChatClickable.EnumClickAction.valueOf(action.toUpperCase()), value);
|
||||
}
|
||||
if (jsonObject.containsKey("hoverEvent")) {
|
||||
JSONObject hoverEvent = (JSONObject) jsonObject.get("hoverEvent");
|
||||
String action = (String) hoverEvent.get("action");
|
||||
String value = (String) hoverEvent.get("value");
|
||||
getModifier().onHover(ChatHoverable.EnumHoverAction.valueOf(action.toUpperCase()), deserialize(value));
|
||||
}
|
||||
if (jsonObject.containsKey("extra")) {
|
||||
List<Object> list = (List<Object>) jsonObject.get("extra");
|
||||
for (Object extra : list) {
|
||||
String string = extra.toString();
|
||||
//reverting .toString() removing "" for simple text
|
||||
if (!string.startsWith("{")) string = "\"" + string + "\"";
|
||||
addExtra(IChatBaseComponent.deserialize(string));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean value of requested key from map
|
||||
*
|
||||
* @param jsonObject
|
||||
* map to get value from
|
||||
* @param key
|
||||
* 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");
|
||||
String value = String.valueOf(jsonObject.getOrDefault(key, null));
|
||||
return "null".equals(value) ? null : Boolean.parseBoolean(value);
|
||||
}
|
||||
}
|
||||
@@ -7,236 +7,254 @@ import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
*/
|
||||
public enum EnumChatFormat {
|
||||
|
||||
BLACK('0', "000000"),
|
||||
DARK_BLUE('1', "0000AA"),
|
||||
DARK_GREEN('2', "00AA00"),
|
||||
DARK_AQUA('3', "00AAAA"),
|
||||
DARK_RED('4', "AA0000"),
|
||||
DARK_PURPLE('5', "AA00AA"),
|
||||
GOLD('6', "FFAA00"),
|
||||
GRAY('7', "AAAAAA"),
|
||||
DARK_GRAY('8', "555555"),
|
||||
BLUE('9', "5555FF"),
|
||||
GREEN('a', "55FF55"),
|
||||
AQUA('b', "55FFFF"),
|
||||
RED('c', "FF5555"),
|
||||
LIGHT_PURPLE('d', "FF55FF"),
|
||||
YELLOW('e', "FFFF55"),
|
||||
WHITE('f', "FFFFFF"),
|
||||
OBFUSCATED('k'),
|
||||
BOLD('l'),
|
||||
STRIKETHROUGH('m'),
|
||||
UNDERLINE('n'),
|
||||
ITALIC('o'),
|
||||
RESET('r');
|
||||
BLACK('0', "000000"),
|
||||
DARK_BLUE('1', "0000AA"),
|
||||
DARK_GREEN('2', "00AA00"),
|
||||
DARK_AQUA('3', "00AAAA"),
|
||||
DARK_RED('4', "AA0000"),
|
||||
DARK_PURPLE('5', "AA00AA"),
|
||||
GOLD('6', "FFAA00"),
|
||||
GRAY('7', "AAAAAA"),
|
||||
DARK_GRAY('8', "555555"),
|
||||
BLUE('9', "5555FF"),
|
||||
GREEN('a', "55FF55"),
|
||||
AQUA('b', "55FFFF"),
|
||||
RED('c', "FF5555"),
|
||||
LIGHT_PURPLE('d', "FF55FF"),
|
||||
YELLOW('e', "FFFF55"),
|
||||
WHITE('f', "FFFFFF"),
|
||||
OBFUSCATED('k'),
|
||||
BOLD('l'),
|
||||
STRIKETHROUGH('m'),
|
||||
UNDERLINE('n'),
|
||||
ITALIC('o'),
|
||||
RESET('r');
|
||||
|
||||
/** The symbol minecraft uses to colorize text */
|
||||
public static final char COLOR_CHAR = 0x00a7;
|
||||
/** Creating a constant to avoid memory allocations on each request */
|
||||
public static final EnumChatFormat[] VALUES = values();
|
||||
|
||||
/** The color symbol in form of a string */
|
||||
public static final String COLOR_STRING = String.valueOf(COLOR_CHAR);
|
||||
/** The symbol minecraft uses to colorize text */
|
||||
public static final char COLOR_CHAR = 0x00a7;
|
||||
|
||||
/** Character representing the color or magic code */
|
||||
private final char character;
|
||||
|
||||
/** Red value of this constant, 0 for magic codes */
|
||||
private final short red;
|
||||
/** The color symbol in form of a string */
|
||||
public static final String COLOR_STRING = String.valueOf(COLOR_CHAR);
|
||||
|
||||
/** Green value of this constant, 0 for magic codes */
|
||||
private final short green;
|
||||
/** Character representing the color or magic code */
|
||||
private final char character;
|
||||
|
||||
/** Blue value of this constant, 0 for magic codes */
|
||||
private final short blue;
|
||||
|
||||
/** Color as 6-digit hex code, null for magic codes */
|
||||
private final String hexCode;
|
||||
|
||||
/** Color symbol followed by constant's character */
|
||||
private final String chatFormat;
|
||||
/** Red value of this constant, 0 for magic codes */
|
||||
private final short red;
|
||||
|
||||
/**
|
||||
* Constructs new color instance with given character and hex code
|
||||
* @param character
|
||||
* character which the color goes by
|
||||
* @param hexCode
|
||||
* 6-digit hex code of the color
|
||||
*/
|
||||
EnumChatFormat(char character, String hexCode) {
|
||||
this.character = character;
|
||||
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
|
||||
this.hexCode = hexCode;
|
||||
int hexColor = Integer.parseInt(hexCode, 16);
|
||||
red = (short) ((hexColor >> 16) & 0xFF);
|
||||
green = (short) ((hexColor >> 8) & 0xFF);
|
||||
blue = (short) (hexColor & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new magic code instance with given character
|
||||
* @param character
|
||||
* character representing the magic code
|
||||
*/
|
||||
EnumChatFormat(char character) {
|
||||
this.character = character;
|
||||
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
|
||||
red = 0;
|
||||
green = 0;
|
||||
blue = 0;
|
||||
hexCode = null;
|
||||
}
|
||||
/** Green value of this constant, 0 for magic codes */
|
||||
private final short green;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param c
|
||||
* color code character (0-9, a-f, k-o, r)
|
||||
* @return instance from the character or null if character is not valid
|
||||
*/
|
||||
public static EnumChatFormat getByChar(char c) {
|
||||
for (EnumChatFormat format : values()) {
|
||||
if (format.character == c) return format;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum value of last colors used in given string.
|
||||
* If it's null, empty or does not contain color codes, WHITE is returned.
|
||||
* @param string
|
||||
* string to check last colors of
|
||||
* @return last used color code in given string or WHITE if nothing is found
|
||||
*/
|
||||
public static EnumChatFormat lastColorsOf(String string) {
|
||||
if (string == null || string.length() == 0) return EnumChatFormat.WHITE;
|
||||
String legacyText = RGBUtils.getInstance().convertRGBtoLegacy(string);
|
||||
String last = getLastColors(legacyText);
|
||||
if (last.length() > 0) {
|
||||
char c = last.toCharArray()[1];
|
||||
for (EnumChatFormat e : values()) {
|
||||
if (e.character == c) return e;
|
||||
}
|
||||
}
|
||||
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
|
||||
* @param red
|
||||
* exact red value
|
||||
* @param green
|
||||
* exact green value
|
||||
* @param blue
|
||||
* exact blue value
|
||||
* @return enum value or null if no such combination exists
|
||||
*/
|
||||
public static EnumChatFormat fromRGBExact(int red, int green, int blue){
|
||||
for (EnumChatFormat format : values()) {
|
||||
if (format.red == red && format.green == green && format.blue == blue) return format;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/** Blue value of this constant, 0 for magic codes */
|
||||
private final short blue;
|
||||
|
||||
/**
|
||||
* Color translation method taken from bukkit, which converts '&' symbol into
|
||||
* the actual color character if followed by a valid color character.
|
||||
* @param textToTranslate
|
||||
* text to replace color symbol in
|
||||
* @return colorized string from provided text
|
||||
*/
|
||||
public static String color(String textToTranslate){
|
||||
if (textToTranslate == null) return null;
|
||||
if (!textToTranslate.contains("&")) return textToTranslate;
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
for (int i = 0; i < b.length - 1; i++) {
|
||||
if ((b[i] == '&') && ("0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx".indexOf(b[(i + 1)]) > -1)){
|
||||
b[i] = COLOR_CHAR;
|
||||
b[(i + 1)] = Character.toLowerCase(b[(i + 1)]);
|
||||
}
|
||||
}
|
||||
return new String(b);
|
||||
}
|
||||
/** Color as 6-digit hex code, null for magic codes */
|
||||
private final String hexCode;
|
||||
|
||||
/**
|
||||
* Turns back the color symbol into '&' symbol in provided text.
|
||||
* @param text
|
||||
* text to revert colors in
|
||||
* @return reverted text
|
||||
*/
|
||||
public static String decolor(String text) {
|
||||
if (text == null) return null;
|
||||
if (!text.contains(COLOR_STRING)) return text;
|
||||
return text.replace(COLOR_CHAR, '&');
|
||||
}
|
||||
/** Color symbol followed by constant's character */
|
||||
private final String chatFormat;
|
||||
|
||||
/**
|
||||
* Code taken from bukkit, which returns last color codes used in provided text.
|
||||
* @param input
|
||||
* text to get last colors from
|
||||
* @return last colors used in provided text or empty string if nothing was found
|
||||
*/
|
||||
public static String getLastColors(String input) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
int length = input.length();
|
||||
for (int index = length - 1; index > -1; index--){
|
||||
char section = input.charAt(index);
|
||||
if ((section == COLOR_CHAR || section == '&') && (index < length - 1)){
|
||||
char c = input.charAt(index + 1);
|
||||
if ("0123456789AaBbCcDdEeFfKkLlMmNnOoRr".contains(String.valueOf(c))) {
|
||||
result.insert(0, COLOR_CHAR);
|
||||
result.insert(1, c);
|
||||
if ("0123456789AaBbCcDdEeFfRr".contains(String.valueOf(c))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
/**
|
||||
* Constructs new color instance with given character and hex code
|
||||
*
|
||||
* @param character
|
||||
* character which the color goes by
|
||||
* @param hexCode
|
||||
* 6-digit hex code of the color
|
||||
*/
|
||||
EnumChatFormat(char character, String hexCode) {
|
||||
this.character = character;
|
||||
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
|
||||
this.hexCode = hexCode;
|
||||
int hexColor = Integer.parseInt(hexCode, 16);
|
||||
red = (short) ((hexColor >> 16) & 0xFF);
|
||||
green = (short) ((hexColor >> 8) & 0xFF);
|
||||
blue = (short) (hexColor & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new magic code instance with given character
|
||||
*
|
||||
* @param character
|
||||
* character representing the magic code
|
||||
*/
|
||||
EnumChatFormat(char character) {
|
||||
this.character = character;
|
||||
this.chatFormat = 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
|
||||
*
|
||||
* @param c
|
||||
* color code character (0-9, a-f, k-o, r)
|
||||
* @return instance from the character or null if character is not valid
|
||||
*/
|
||||
public static EnumChatFormat getByChar(char c) {
|
||||
for (EnumChatFormat format : VALUES) {
|
||||
if (format.character == c) return format;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum value of last colors used in given string.
|
||||
* If it's null, empty or does not contain color codes, WHITE is returned.
|
||||
*
|
||||
* @param string
|
||||
* string to check last colors of
|
||||
* @return last used color code in given string or WHITE if nothing is found
|
||||
*/
|
||||
public static EnumChatFormat lastColorsOf(String string) {
|
||||
if (string == null || string.length() == 0) return EnumChatFormat.WHITE;
|
||||
String legacyText = RGBUtils.getInstance().convertRGBtoLegacy(string);
|
||||
String last = getLastColors(legacyText);
|
||||
if (last.length() > 0) {
|
||||
char c = last.toCharArray()[1];
|
||||
for (EnumChatFormat e : VALUES) {
|
||||
if (e.character == c) return e;
|
||||
}
|
||||
}
|
||||
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
|
||||
*
|
||||
* @param red
|
||||
* exact red value
|
||||
* @param green
|
||||
* exact green value
|
||||
* @param blue
|
||||
* exact blue value
|
||||
* @return enum value or null if no such combination exists
|
||||
*/
|
||||
public static EnumChatFormat fromRGBExact(int red, int green, int blue){
|
||||
for (EnumChatFormat format : VALUES) {
|
||||
if (format.red == red && format.green == green && format.blue == blue) return format;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Color translation method taken from bukkit, which converts '&' symbol into
|
||||
* the actual color character if followed by a valid color character.
|
||||
*
|
||||
* @param textToTranslate
|
||||
* text to replace color symbol in
|
||||
* @return colorized string from provided text
|
||||
*/
|
||||
public static String color(String textToTranslate){
|
||||
if (textToTranslate == null) return null;
|
||||
if (!textToTranslate.contains("&")) return textToTranslate;
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
for (int i = 0; i < b.length - 1; i++) {
|
||||
if ((b[i] == '&') && ("0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx".indexOf(b[(i + 1)]) > -1)){
|
||||
b[i] = COLOR_CHAR;
|
||||
b[(i + 1)] = Character.toLowerCase(b[(i + 1)]);
|
||||
}
|
||||
}
|
||||
return new String(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns back the color symbol into '&' symbol in provided text.
|
||||
*
|
||||
* @param text
|
||||
* text to revert colors in
|
||||
* @return reverted text
|
||||
*/
|
||||
public static String decolor(String text) {
|
||||
if (text == null) return null;
|
||||
if (!text.contains(COLOR_STRING)) return text;
|
||||
return text.replace(COLOR_CHAR, '&');
|
||||
}
|
||||
|
||||
/**
|
||||
* Code taken from bukkit, which returns last color codes used in provided text.
|
||||
*
|
||||
* @param input
|
||||
* text to get last colors from
|
||||
* @return last colors used in provided text or empty string if nothing was found
|
||||
*/
|
||||
public static String getLastColors(String input) {
|
||||
if (input == null) return "";
|
||||
StringBuilder result = new StringBuilder();
|
||||
int length = input.length();
|
||||
for (int index = length - 1; index > -1; index--){
|
||||
char section = input.charAt(index);
|
||||
if ((section == COLOR_CHAR || section == '&') && (index < length - 1)){
|
||||
char c = input.charAt(index + 1);
|
||||
if ("0123456789AaBbCcDdEeFfKkLlMmNnOoRr".contains(String.valueOf(c))) {
|
||||
result.insert(0, COLOR_CHAR);
|
||||
result.insert(1, c);
|
||||
if ("0123456789AaBbCcDdEeFfRr".contains(String.valueOf(c))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,12 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
|
||||
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 me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.IChatBaseComponent class to make work with it much easier
|
||||
@@ -21,421 +14,425 @@ import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
@SuppressWarnings("unchecked")
|
||||
public class IChatBaseComponent {
|
||||
|
||||
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<>();
|
||||
|
||||
//constants
|
||||
private static final String EMPTY_TRANSLATABLE = "{\"translate\":\"\"}";
|
||||
private static final String EMPTY_TEXT = "{\"text\":\"\"}";
|
||||
private static final List<IChatBaseComponent> EMPTY_LIST = new ArrayList<>(0);
|
||||
/**
|
||||
* 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<>();
|
||||
|
||||
//component text
|
||||
private String text;
|
||||
|
||||
private ChatModifier modifier = new ChatModifier();
|
||||
/** Text of the component */
|
||||
private String text;
|
||||
|
||||
//extra components
|
||||
private List<IChatBaseComponent> extra;
|
||||
/** Chat modifier containing color, magic codes, hover and click event */
|
||||
private ChatModifier modifier = new ChatModifier();
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.text = component.text;
|
||||
this.modifier = new ChatModifier(component.modifier);
|
||||
for (IChatBaseComponent child : component.getExtra()) {
|
||||
addExtra(new IChatBaseComponent(child));
|
||||
}
|
||||
}
|
||||
/** Extra components used in "extra" field */
|
||||
private List<IChatBaseComponent> extra;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given text
|
||||
* @param text - text to display
|
||||
*/
|
||||
public IChatBaseComponent(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
/**
|
||||
* 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");
|
||||
this.text = component.text;
|
||||
this.modifier = new ChatModifier(component.modifier);
|
||||
for (IChatBaseComponent child : component.getExtra()) {
|
||||
addExtra(new IChatBaseComponent(child));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of extra components or null if none are defined
|
||||
* @return list of extras
|
||||
*/
|
||||
public List<IChatBaseComponent> getExtra(){
|
||||
if (extra == null) return EMPTY_LIST;
|
||||
return extra;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given text
|
||||
*
|
||||
* @param text
|
||||
* text to display
|
||||
*/
|
||||
public IChatBaseComponent(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets full list of extra components to given list
|
||||
* @param components - components to use as extra
|
||||
* @return self
|
||||
*/
|
||||
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
|
||||
if (components.isEmpty()) throw new IllegalArgumentException("Unexpected empty array of components"); //exception taken from minecraft
|
||||
this.extra = components;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Returns list of extra components. If no extra components are defined, returns empty list.
|
||||
*
|
||||
* @return list of extra components
|
||||
*/
|
||||
public List<IChatBaseComponent> getExtra(){
|
||||
if (extra == null) return Collections.emptyList();
|
||||
return extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends provided component as extra component
|
||||
* @param child - component to append
|
||||
* @return self
|
||||
*/
|
||||
public IChatBaseComponent addExtra(IChatBaseComponent child) {
|
||||
if (extra == null) extra = new ArrayList<>();
|
||||
extra.add(child);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Sets full list of extra components to given list. Does not allow empty list.
|
||||
*
|
||||
* @param components
|
||||
* components to use as extra
|
||||
* @return self
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code components} is an empty list
|
||||
*/
|
||||
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
|
||||
if (components.isEmpty()) throw new IllegalArgumentException("Unexpected empty array of components"); //exception taken from minecraft
|
||||
this.extra = components;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text of this component
|
||||
* @return text of this component
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
/**
|
||||
* Appends provided component as extra component
|
||||
*
|
||||
* @param child
|
||||
* component to append
|
||||
*/
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
Preconditions.checkNotNull(child, "extra");
|
||||
if (extra == null) extra = new ArrayList<>();
|
||||
extra.add(child);
|
||||
}
|
||||
|
||||
public ChatModifier getModifier() {
|
||||
return modifier;
|
||||
}
|
||||
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
this.modifier = modifier;
|
||||
}
|
||||
/**
|
||||
* Returns text of this component
|
||||
*
|
||||
* @return text of this component
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text of this component
|
||||
* @param text - text to show
|
||||
* @return self
|
||||
*/
|
||||
public IChatBaseComponent setText(String text) {
|
||||
this.text = text;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Returns chat modifier of this component
|
||||
*
|
||||
* @return chat modifier of this component
|
||||
*/
|
||||
public ChatModifier getModifier() {
|
||||
return modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes string and returns created component
|
||||
* @param json - serialized string
|
||||
* @return Deserialized component
|
||||
*/
|
||||
public static IChatBaseComponent deserialize(String json) {
|
||||
try {
|
||||
if (json == null) return null;
|
||||
if (json.startsWith("\"") && json.endsWith("\"") && json.length() > 1) {
|
||||
//simple component with only text used, minecraft serializer outputs the text in quotes instead of full json
|
||||
return new IChatBaseComponent(json.substring(1, json.length()-1));
|
||||
}
|
||||
JSONObject jsonObject = (JSONObject) new JSONParser().parse(json);
|
||||
IChatBaseComponent component;
|
||||
if (jsonObject.containsKey("type")) {
|
||||
return new ChatComponentEntity((String) jsonObject.get("type"), UUID.fromString((String) jsonObject.get("id")), IChatBaseComponent.deserialize(jsonObject.get("name").toString()).toFlatText());
|
||||
}
|
||||
component = new IChatBaseComponent();
|
||||
component.setText((String) jsonObject.get("text"));
|
||||
component.modifier.setBold(getBoolean(jsonObject, "bold"));
|
||||
component.modifier.setItalic(getBoolean(jsonObject, "italic"));
|
||||
component.modifier.setUnderlined(getBoolean(jsonObject, "underlined"));
|
||||
component.modifier.setStrikethrough(getBoolean(jsonObject, "strikethrough"));
|
||||
component.modifier.setObfuscated(getBoolean(jsonObject, "obfuscated"));
|
||||
component.modifier.setColor(TextColor.fromString(((String) jsonObject.get("color"))));
|
||||
if (jsonObject.containsKey("clickEvent")) {
|
||||
JSONObject clickEvent = (JSONObject) jsonObject.get("clickEvent");
|
||||
String action = (String) clickEvent.get("action");
|
||||
String value = clickEvent.get("value").toString();
|
||||
component.modifier.onClick(EnumClickAction.valueOf(action.toUpperCase()), value);
|
||||
}
|
||||
if (jsonObject.containsKey("hoverEvent")) {
|
||||
JSONObject hoverEvent = (JSONObject) jsonObject.get("hoverEvent");
|
||||
String action = (String) hoverEvent.get("action");
|
||||
String value = (String) hoverEvent.get("value");
|
||||
component.modifier.onHover(EnumHoverAction.valueOf(action.toUpperCase()), deserialize(value));
|
||||
}
|
||||
if (jsonObject.containsKey("extra")) {
|
||||
List<Object> list = (List<Object>) jsonObject.get("extra");
|
||||
for (Object extra : list) {
|
||||
String string = extra.toString();
|
||||
//reverting .toString() removing "" for simple text
|
||||
if (!string.startsWith("{")) string = "\"" + string + "\"";
|
||||
component.addExtra(deserialize(string));
|
||||
}
|
||||
}
|
||||
return component;
|
||||
} catch (Exception e) {
|
||||
TabAPI.getInstance().logError("Failed to deserialize json component " + json, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns boolean value of requested key
|
||||
* @param jsonObject - object to get value from
|
||||
* @param key - name of key
|
||||
* @return value from json object or null if not present
|
||||
*/
|
||||
private static Boolean getBoolean(JSONObject jsonObject, String key) {
|
||||
String value = String.valueOf(jsonObject.getOrDefault(key, null));
|
||||
return "null".equals(value) ? null : Boolean.parseBoolean(value);
|
||||
}
|
||||
/**
|
||||
* Changes text of this component
|
||||
*
|
||||
* @param text
|
||||
* text to show
|
||||
*/
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
JSONObject json = new JSONObject();
|
||||
if (text != null) json.put("text", text);
|
||||
if (modifier.getTargetVersion() == null) modifier.setTargetVersion(TabAPI.getInstance().getServerVersion()); //packet.toString() was called as a part of a debug message
|
||||
json.putAll(modifier.serialize());
|
||||
if (extra != null) json.put("extra", extra);
|
||||
return json.toString();
|
||||
}
|
||||
/**
|
||||
* Deserializes string and returns created component. If provided string is null, returns null.
|
||||
*
|
||||
* @param json
|
||||
* serialized string
|
||||
* @return Deserialized component or null if input is null
|
||||
*/
|
||||
public static IChatBaseComponent deserialize(String json) {
|
||||
if (json == null) return null;
|
||||
return new DeserializedChatComponent(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this component with colors based on client version
|
||||
* @param clientVersion - client version
|
||||
* @return Serialized string
|
||||
*/
|
||||
public String toString(ProtocolVersion clientVersion) {
|
||||
return toString(clientVersion, false);
|
||||
}
|
||||
/**
|
||||
* Converts the component to a string representing the serialized component.
|
||||
* This method is only used internally by json library since it's missing
|
||||
* protocol version field used by the method.
|
||||
*
|
||||
* @return serialized component in string form
|
||||
* @see #toString(ProtocolVersion)
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
JSONObject json = new JSONObject();
|
||||
if (text != null) json.put("text", text);
|
||||
if (modifier.getTargetVersion() == null) modifier.setTargetVersion(TabAPI.getInstance().getServerVersion()); //packet.toString() was called as a part of a debug message
|
||||
json.putAll(modifier.serialize());
|
||||
if (extra != null) json.put("extra", extra);
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this component with colors based on client version
|
||||
* @param clientVersion - client version
|
||||
* @param sendTranslatableIfEmpty - if empty translatable should be sent if text is empty or not
|
||||
* @return Serialized string
|
||||
*/
|
||||
public String toString(ProtocolVersion clientVersion, boolean sendTranslatableIfEmpty) {
|
||||
if (extra == null) {
|
||||
if (text == null) return null;
|
||||
if (text.length() == 0) {
|
||||
if (sendTranslatableIfEmpty) {
|
||||
return EMPTY_TRANSLATABLE;
|
||||
} else {
|
||||
return EMPTY_TEXT;
|
||||
}
|
||||
}
|
||||
}
|
||||
modifier.setTargetVersion(clientVersion);
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* Serializes this component with colors based on client version.
|
||||
* If client version is <1.16, HEX colors will be converted to legacy colors.
|
||||
*
|
||||
* @param clientVersion
|
||||
* client version to adapt component for
|
||||
* @return serialized string
|
||||
*/
|
||||
public String toString(ProtocolVersion clientVersion) {
|
||||
return toString(clientVersion, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns organized component from colored text
|
||||
* @param originalText - text to convert
|
||||
* @return organized component from colored text
|
||||
*/
|
||||
public static IChatBaseComponent fromColoredText(String originalText){
|
||||
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText), false);
|
||||
List<IChatBaseComponent> components = new ArrayList<>();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
IChatBaseComponent component = new IChatBaseComponent();
|
||||
for (int i = 0; i < text.length(); i++){
|
||||
char c = text.charAt(i);
|
||||
if (c == EnumChatFormat.COLOR_CHAR){
|
||||
i++;
|
||||
if (i >= text.length()) {
|
||||
break;
|
||||
}
|
||||
c = text.charAt(i);
|
||||
if ((c >= 'A') && (c <= 'Z')) {
|
||||
c = (char)(c + ' ');
|
||||
}
|
||||
EnumChatFormat format = EnumChatFormat.getByChar(c);
|
||||
if (format != null){
|
||||
if (builder.length() > 0) {
|
||||
component.setText(builder.toString());
|
||||
components.add(component);
|
||||
component = new IChatBaseComponent(component);
|
||||
component.text = null;
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
switch (format){
|
||||
case BOLD:
|
||||
component.modifier.setBold(true);
|
||||
break;
|
||||
case ITALIC:
|
||||
component.modifier.setItalic(true);
|
||||
break;
|
||||
case UNDERLINE:
|
||||
component.modifier.setUnderlined(true);
|
||||
break;
|
||||
case STRIKETHROUGH:
|
||||
component.modifier.setStrikethrough(true);
|
||||
break;
|
||||
case OBFUSCATED:
|
||||
component.modifier.setObfuscated(true);
|
||||
break;
|
||||
case RESET:
|
||||
component = new IChatBaseComponent();
|
||||
component.modifier.setColor(new TextColor(EnumChatFormat.WHITE));
|
||||
break;
|
||||
default:
|
||||
component = new IChatBaseComponent();
|
||||
component.modifier.setColor(new TextColor(format));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (c == '#' && text.length() > i+6){
|
||||
String hex = text.substring(i+1, i+7);
|
||||
if (RGBUtils.getInstance().isHexCode(hex)) {
|
||||
TextColor color;
|
||||
if (containsLegacyCode(text, i)) {
|
||||
color = new TextColor(hex, EnumChatFormat.getByChar(text.charAt(i+8)));
|
||||
i += 8;
|
||||
} else {
|
||||
color = new TextColor(hex);
|
||||
i += 6;
|
||||
}
|
||||
if (builder.length() > 0){
|
||||
component.setText(builder.toString());
|
||||
components.add(component);
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
component = new IChatBaseComponent();
|
||||
component.modifier.setColor(color);
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
component.setText(builder.toString());
|
||||
components.add(component);
|
||||
return new IChatBaseComponent("").setExtra(components);
|
||||
}
|
||||
/**
|
||||
* Serializes this component with colors based on client version.
|
||||
* If client version is <1.16, HEX colors will be converted to legacy colors.
|
||||
*
|
||||
* @param clientVersion
|
||||
* client version to adapt component for
|
||||
* @param sendTranslatableIfEmpty
|
||||
* whether empty translatable should be sent if text is empty or not
|
||||
* @return serialized string
|
||||
*/
|
||||
public String toString(ProtocolVersion clientVersion, boolean sendTranslatableIfEmpty) {
|
||||
if (extra == null) {
|
||||
if (text == null) return null;
|
||||
if (text.length() == 0) {
|
||||
if (sendTranslatableIfEmpty) {
|
||||
return "{\"translate\":\"\"}";
|
||||
} else {
|
||||
return "{\"text\":\"\"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
modifier.setTargetVersion(clientVersion);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if text contains legacy color request at defined RGB index start
|
||||
* @param text - text to check
|
||||
* @param i - current index start
|
||||
* @return true if legacy color is defined, false if not
|
||||
*/
|
||||
private static boolean containsLegacyCode(String text, int i) {
|
||||
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
|
||||
return EnumChatFormat.getByChar(text.charAt(i+8)) != null;
|
||||
}
|
||||
/**
|
||||
* Returns organized component from colored text
|
||||
*
|
||||
* @param originalText
|
||||
* text to convert
|
||||
* @return organized component from colored text
|
||||
*/
|
||||
public static IChatBaseComponent fromColoredText(String originalText){
|
||||
Preconditions.checkNotNull(originalText, "text");
|
||||
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText));
|
||||
List<IChatBaseComponent> components = new ArrayList<>();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
IChatBaseComponent component = new IChatBaseComponent();
|
||||
for (int i = 0; i < text.length(); i++){
|
||||
char c = text.charAt(i);
|
||||
if (c == EnumChatFormat.COLOR_CHAR){
|
||||
i++;
|
||||
if (i >= text.length()) {
|
||||
break;
|
||||
}
|
||||
c = text.charAt(i);
|
||||
if ((c >= 'A') && (c <= 'Z')) {
|
||||
c = (char)(c + ' ');
|
||||
}
|
||||
EnumChatFormat format = EnumChatFormat.getByChar(c);
|
||||
if (format != null){
|
||||
if (builder.length() > 0) {
|
||||
component.setText(builder.toString());
|
||||
components.add(component);
|
||||
component = new IChatBaseComponent(component);
|
||||
component.text = null;
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
switch (format){
|
||||
case BOLD:
|
||||
component.modifier.setBold(true);
|
||||
break;
|
||||
case ITALIC:
|
||||
component.modifier.setItalic(true);
|
||||
break;
|
||||
case UNDERLINE:
|
||||
component.modifier.setUnderlined(true);
|
||||
break;
|
||||
case STRIKETHROUGH:
|
||||
component.modifier.setStrikethrough(true);
|
||||
break;
|
||||
case OBFUSCATED:
|
||||
component.modifier.setObfuscated(true);
|
||||
break;
|
||||
case RESET:
|
||||
component = new IChatBaseComponent();
|
||||
component.modifier.setColor(new TextColor(EnumChatFormat.WHITE));
|
||||
break;
|
||||
default:
|
||||
component = new IChatBaseComponent();
|
||||
component.modifier.setColor(new TextColor(format));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (c == '#' && text.length() > i+6){
|
||||
String hex = text.substring(i+1, i+7);
|
||||
if (RGBUtils.getInstance().isHexCode(hex)) {
|
||||
TextColor color;
|
||||
if (containsLegacyCode(text, i)) {
|
||||
color = new TextColor(hex, EnumChatFormat.getByChar(text.charAt(i+8)));
|
||||
i += 8;
|
||||
} else {
|
||||
color = new TextColor(hex);
|
||||
i += 6;
|
||||
}
|
||||
if (builder.length() > 0){
|
||||
component.setText(builder.toString());
|
||||
components.add(component);
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
component = new IChatBaseComponent();
|
||||
component.modifier.setColor(color);
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
component.setText(builder.toString());
|
||||
components.add(component);
|
||||
return new IChatBaseComponent("").setExtra(components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this component into a simple text with legacy colors (the closest match if color is set to RGB)
|
||||
* @return The simple text format
|
||||
*/
|
||||
public String toLegacyText() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
append(builder, "");
|
||||
return builder.toString();
|
||||
}
|
||||
/**
|
||||
* Returns true if text contains legacy color request at defined RGB index start
|
||||
*
|
||||
* @param text
|
||||
* text to check
|
||||
* @param i
|
||||
* current index start
|
||||
* @return true if legacy color is defined, false if not
|
||||
*/
|
||||
private static boolean containsLegacyCode(String text, int i) {
|
||||
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
|
||||
return EnumChatFormat.getByChar(text.charAt(i+8)) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends text to string builder, might also add color and magic codes if different
|
||||
* from previous component in chain
|
||||
* @param builder - builder to append text to
|
||||
* @param previousFormatting - colors and magic codes in previous component
|
||||
* @return New formatting, might be identical to previous one
|
||||
*/
|
||||
private String append(StringBuilder builder, String previousFormatting) {
|
||||
String formatting = previousFormatting;
|
||||
if (text != null) {
|
||||
formatting = getFormatting();
|
||||
if (!formatting.equals(previousFormatting)) {
|
||||
builder.append(formatting);
|
||||
}
|
||||
builder.append(text);
|
||||
/**
|
||||
* Converts this component into a simple text with legacy colors (the closest match if color is set to RGB)
|
||||
*
|
||||
* @return The simple text format
|
||||
*/
|
||||
public String toLegacyText() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
append(builder, "");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
for (IChatBaseComponent component : getExtra()) {
|
||||
formatting = component.append(builder, formatting);
|
||||
}
|
||||
return formatting;
|
||||
}
|
||||
/**
|
||||
* Appends text to string builder, might also add color and magic codes if they are different
|
||||
* from previous component in chain.
|
||||
*
|
||||
* @param builder
|
||||
* builder to append text to
|
||||
* @param previousFormatting
|
||||
* colors and magic codes in previous component
|
||||
* @return new formatting, might be identical to previous one
|
||||
*/
|
||||
private String append(StringBuilder builder, String previousFormatting) {
|
||||
String formatting = previousFormatting;
|
||||
if (text != null) {
|
||||
formatting = getFormatting();
|
||||
if (!formatting.equals(previousFormatting)) {
|
||||
builder.append(formatting);
|
||||
}
|
||||
builder.append(text);
|
||||
|
||||
/**
|
||||
* Returns colors and magic codes of this component
|
||||
* @return used colors and magic codes
|
||||
*/
|
||||
private String getFormatting() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (modifier.getColor() != null) {
|
||||
if (modifier.getColor().getLegacyColor() == EnumChatFormat.WHITE) {
|
||||
//preventing unwanted &r -> &f conversion and stopping the <1.13 client bug fix from working
|
||||
builder.append(EnumChatFormat.RESET.getFormat());
|
||||
} else {
|
||||
builder.append(modifier.getColor().getLegacyColor().getFormat());
|
||||
}
|
||||
}
|
||||
builder.append(modifier.getMagicCodes());
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
for (IChatBaseComponent component : getExtra()) {
|
||||
formatting = component.append(builder, formatting);
|
||||
}
|
||||
return formatting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns raw text without colors, only works correctly when component is organized
|
||||
* @return raw text in this component
|
||||
*/
|
||||
public String toRawText() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (text != null) builder.append(text);
|
||||
for (IChatBaseComponent child : getExtra()) {
|
||||
if (child.text != null) builder.append(child.text);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
/**
|
||||
* Returns colors and magic codes of this component
|
||||
*
|
||||
* @return used colors and magic codes
|
||||
*/
|
||||
private String getFormatting() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (modifier.getColor() != null) {
|
||||
if (modifier.getColor().getLegacyColor() == EnumChatFormat.WHITE) {
|
||||
//preventing unwanted &r -> &f conversion and stopping the <1.13 client bug fix from working
|
||||
builder.append(EnumChatFormat.RESET.getFormat());
|
||||
} else {
|
||||
builder.append(modifier.getColor().getLegacyColor().getFormat());
|
||||
}
|
||||
}
|
||||
builder.append(modifier.getMagicCodes());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the component into flat text with used colors (including rgb) and magic codes
|
||||
* @return converted text
|
||||
*/
|
||||
public String toFlatText() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (modifier.getColor() != null) builder.append("#").append(modifier.getColor().getHexCode());
|
||||
builder.append(modifier.getMagicCodes());
|
||||
if (text != null) builder.append(text);
|
||||
for (IChatBaseComponent child : getExtra()) {
|
||||
builder.append(child.toFlatText());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
/**
|
||||
* Returns raw text without colors, only works correctly when component is organized
|
||||
*
|
||||
* @return raw text in this component and all child components
|
||||
*/
|
||||
public String toRawText() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (text != null) builder.append(text);
|
||||
for (IChatBaseComponent child : getExtra()) {
|
||||
if (child.text != null) builder.append(child.text);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most optimized component based on text. Returns null if text is null,
|
||||
* organized component if RGB colors are used or simple component with only text field
|
||||
* containing the whole text when no RGB colors are used
|
||||
* @param text - text to create component from
|
||||
* @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")){
|
||||
//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;
|
||||
}
|
||||
/**
|
||||
* Converts the component into flat text with used colors (including rgb) and magic codes
|
||||
*
|
||||
* @return converted text
|
||||
*/
|
||||
public String toFlatText() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (modifier.getColor() != null) builder.append("#").append(modifier.getColor().getHexCode());
|
||||
builder.append(modifier.getMagicCodes());
|
||||
if (text != null) builder.append(text);
|
||||
for (IChatBaseComponent child : getExtra()) {
|
||||
builder.append(child.toFlatText());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most optimized component based on text. Returns null if text is null,
|
||||
* organized component if RGB colors are used or simple component with only text field
|
||||
* containing the whole text when no RGB colors are used
|
||||
*
|
||||
* @param text
|
||||
* text to create component from
|
||||
* @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")){
|
||||
//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;
|
||||
}
|
||||
}
|
||||
@@ -7,225 +7,216 @@ import me.neznamy.tab.api.util.Preconditions;
|
||||
*/
|
||||
public class TextColor {
|
||||
|
||||
/**
|
||||
* RGB values from 0 to 255.
|
||||
* They are only initialized if they are actually used to avoid
|
||||
* unnecessary memory allocations with string operations.
|
||||
*/
|
||||
private short red = -1;
|
||||
private short green = -1;
|
||||
private short blue = -1;
|
||||
|
||||
/** Closest legacy color to this color object. */
|
||||
private EnumChatFormat legacyColor;
|
||||
|
||||
/** HEX code of this color as a 6-digit hexadecimal string without "#" prefix. */
|
||||
private String hexCode;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* RGB values as a single number of 3 8-bit numbers (0-255).
|
||||
* It is only initialized if colors are actually used to avoid
|
||||
* unnecessary memory allocations with string operations.
|
||||
*/
|
||||
private int rgb = -1;
|
||||
|
||||
/**
|
||||
* Constructs new instance as a clone of the provided color.
|
||||
* @param color
|
||||
* color to create a clone of
|
||||
* @throws IllegalArgumentException
|
||||
* if color is {@code null}
|
||||
*/
|
||||
public TextColor(TextColor color) {
|
||||
Preconditions.checkNotNull(color, "color cannot be null");
|
||||
red = color.red;
|
||||
green = color.green;
|
||||
blue = color.blue;
|
||||
legacyColor = color.legacyColor;
|
||||
hexCode = color.hexCode;
|
||||
legacyColorForced = color.legacyColorForced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance from provided 6-digit hex code string
|
||||
* @param hexCode
|
||||
* a 6-digit combination of hex numbers as a string
|
||||
* @throws IllegalArgumentException
|
||||
* if hexCode is {@code null}
|
||||
*/
|
||||
public TextColor(String hexCode) {
|
||||
Preconditions.checkNotNull(hexCode, "hex code cannot be null");
|
||||
this.hexCode = hexCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance from provided 6-digit hex code and forced legacy color
|
||||
* @param hexCode
|
||||
* 6-digit combination of hex numbers as a string
|
||||
* @param legacyColor
|
||||
* color to use for legacy clients instead of using the closest legacy color
|
||||
* @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 cannot be null");
|
||||
Preconditions.checkNotNull(legacyColor, "legacy color cannot be null");
|
||||
this.hexCode = hexCode;
|
||||
this.legacyColorForced = true;
|
||||
this.legacyColor = legacyColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance from provided legacy color
|
||||
* @param legacyColor
|
||||
* legacy color to construct the instance from
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code legacyColor} is {@code null}
|
||||
*/
|
||||
public TextColor(EnumChatFormat legacyColor) {
|
||||
Preconditions.checkNotNull(legacyColor, "legacy color cannot be null");
|
||||
this.red = legacyColor.getRed();
|
||||
this.green = legacyColor.getGreen();
|
||||
this.blue = legacyColor.getBlue();
|
||||
this.hexCode = legacyColor.getHexCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with red, green and blue values
|
||||
* @param red
|
||||
* red value
|
||||
* @param green
|
||||
* green value
|
||||
* @param blue
|
||||
* blue value
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code red}, {@code green} or {@code blue} is out of range ({@code 0-255})
|
||||
*/
|
||||
public TextColor(int red, int green, int blue) {
|
||||
Preconditions.checkRange(red, 0, 255);
|
||||
Preconditions.checkRange(green, 0, 255);
|
||||
Preconditions.checkRange(blue, 0, 255);
|
||||
this.red = (short) red;
|
||||
this.green = (short) green;
|
||||
this.blue = (short) blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the closest legacy color based currently provided values
|
||||
*/
|
||||
private EnumChatFormat loadClosestColor() {
|
||||
double minMaxDist = 9999;
|
||||
double maxDist;
|
||||
EnumChatFormat closestColor = EnumChatFormat.WHITE;
|
||||
for (EnumChatFormat color : EnumChatFormat.values()) {
|
||||
int rDiff = Math.abs(color.getRed() - getRed());
|
||||
int gDiff = Math.abs(color.getGreen() - getGreen());
|
||||
int bDiff = Math.abs(color.getBlue() - getBlue());
|
||||
maxDist = rDiff;
|
||||
if (gDiff > maxDist) maxDist = gDiff;
|
||||
if (bDiff > maxDist) maxDist = bDiff;
|
||||
if (maxDist < minMaxDist) {
|
||||
minMaxDist = maxDist;
|
||||
closestColor = color;
|
||||
}
|
||||
}
|
||||
return closestColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code red} value
|
||||
* @return red value
|
||||
*/
|
||||
public int getRed() {
|
||||
if (red == -1) loadColors();
|
||||
return red;
|
||||
}
|
||||
/** Closest legacy color to this color object. */
|
||||
private EnumChatFormat legacyColor;
|
||||
|
||||
/**
|
||||
* Returns {@code green} value
|
||||
* @return green value
|
||||
*/
|
||||
public int getGreen() {
|
||||
if (green == -1) loadColors();
|
||||
return green;
|
||||
}
|
||||
/** HEX code of this color as a 6-digit hexadecimal string without "#" prefix. */
|
||||
private String hexCode;
|
||||
|
||||
/**
|
||||
* Returns {@code blue} value
|
||||
* @return blue value
|
||||
*/
|
||||
public int getBlue() {
|
||||
if (blue == -1) loadColors();
|
||||
return blue;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Loads red, green and blue values based on current hex code string
|
||||
*/
|
||||
private void loadColors(){
|
||||
int hexColor = Integer.parseInt(hexCode, 16);
|
||||
red = (short) ((hexColor >> 16) & 0xFF);
|
||||
green = (short) ((hexColor >> 8) & 0xFF);
|
||||
blue = (short) (hexColor & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest legacy color of this color object.
|
||||
* If the color was defined in constructor, it's returned.
|
||||
* Otherwise, the closest color is calculated the then returned.
|
||||
* @return closest legacy color
|
||||
*/
|
||||
public EnumChatFormat getLegacyColor() {
|
||||
if (legacyColor == null) {
|
||||
legacyColor = loadClosestColor();
|
||||
}
|
||||
return legacyColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rgb combination as a 6-digit hex code string
|
||||
* @return the rgb combination as a 6-digit hex code string
|
||||
*/
|
||||
public String getHexCode() {
|
||||
if (hexCode == null) {
|
||||
hexCode = String.format("%06X", (red << 16) + (green << 8) + blue);
|
||||
}
|
||||
return hexCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the color into a valid color value used in color field in chat component.
|
||||
* That is either 6-digit hex code prefixed with '#', or lowercase legacy color.
|
||||
* @return the color serialized for use in chat component
|
||||
*/
|
||||
public String toString() {
|
||||
EnumChatFormat legacyEquivalent = EnumChatFormat.fromRGBExact(getRed(), getGreen(), getBlue());
|
||||
if (legacyEquivalent != null) {
|
||||
//not sending old colors as RGB to 1.16 clients if not needed as <1.16 servers will fail to apply color
|
||||
return legacyEquivalent.toString().toLowerCase();
|
||||
}
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance as a clone of the provided color.
|
||||
*
|
||||
* @param color
|
||||
* color to create a clone of
|
||||
* @throws IllegalArgumentException
|
||||
* if color is {@code null}
|
||||
*/
|
||||
public TextColor(TextColor color) {
|
||||
Preconditions.checkNotNull(color, "color");
|
||||
rgb = color.rgb;
|
||||
legacyColor = color.legacyColor;
|
||||
hexCode = color.hexCode;
|
||||
legacyColorForced = color.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.
|
||||
* If it's prefixed with '#', it's considered as a hex code.
|
||||
* Otherwise, it is considered being a lowercase legacy color.
|
||||
* @param string
|
||||
* string from color field in chat component
|
||||
* @return An instance from specified string or null if string is null
|
||||
*/
|
||||
public static TextColor fromString(String string) {
|
||||
if (string == null) return null;
|
||||
if (string.startsWith("#")) return new TextColor(string.substring(1));
|
||||
return new TextColor(EnumChatFormat.valueOf(string.toUpperCase()));
|
||||
}
|
||||
/**
|
||||
* Constructs new instance from provided 6-digit hex code string
|
||||
*
|
||||
* @param hexCode
|
||||
* a 6-digit combination of hex numbers as a string
|
||||
* @throws IllegalArgumentException
|
||||
* if hexCode is {@code null}
|
||||
*/
|
||||
public TextColor(String hexCode) {
|
||||
Preconditions.checkNotNull(hexCode, "hex code");
|
||||
this.hexCode = hexCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance from provided 6-digit hex code and forced legacy color
|
||||
*
|
||||
* @param hexCode
|
||||
* 6-digit combination of hex numbers as a string
|
||||
* @param legacyColor
|
||||
* color to use for legacy clients instead of using the closest legacy color
|
||||
* @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");
|
||||
this.hexCode = hexCode;
|
||||
this.legacyColorForced = true;
|
||||
this.legacyColor = legacyColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance from provided legacy color
|
||||
*
|
||||
* @param legacyColor
|
||||
* legacy color to construct the instance from
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code legacyColor} is {@code null}
|
||||
*/
|
||||
public TextColor(EnumChatFormat legacyColor) {
|
||||
Preconditions.checkNotNull(legacyColor, "legacy color");
|
||||
this.rgb = (legacyColor.getRed() << 16) + (legacyColor.getGreen() << 8) + legacyColor.getBlue();
|
||||
this.hexCode = legacyColor.getHexCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with red, green and blue values
|
||||
*
|
||||
* @param red
|
||||
* red value
|
||||
* @param green
|
||||
* green value
|
||||
* @param blue
|
||||
* blue value
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code red}, {@code green} or {@code blue} is out of range ({@code 0-255})
|
||||
*/
|
||||
public TextColor(int red, int green, int blue) {
|
||||
Preconditions.checkRange(red, 0, 255, "red");
|
||||
Preconditions.checkRange(green, 0, 255, "green");
|
||||
Preconditions.checkRange(blue, 0, 255, "blue");
|
||||
this.rgb = (red << 16) + (green << 8) + blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the closest legacy color based currently provided values
|
||||
*/
|
||||
private EnumChatFormat loadClosestColor() {
|
||||
double minMaxDist = 9999;
|
||||
double maxDist;
|
||||
EnumChatFormat closestColor = EnumChatFormat.WHITE;
|
||||
for (EnumChatFormat color : EnumChatFormat.VALUES) {
|
||||
int rDiff = Math.abs(color.getRed() - getRed());
|
||||
int gDiff = Math.abs(color.getGreen() - getGreen());
|
||||
int bDiff = Math.abs(color.getBlue() - getBlue());
|
||||
maxDist = rDiff;
|
||||
if (gDiff > maxDist) maxDist = gDiff;
|
||||
if (bDiff > maxDist) maxDist = bDiff;
|
||||
if (maxDist < minMaxDist) {
|
||||
minMaxDist = maxDist;
|
||||
closestColor = color;
|
||||
}
|
||||
}
|
||||
return closestColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code red} value
|
||||
*
|
||||
* @return red value
|
||||
*/
|
||||
public int getRed() {
|
||||
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
|
||||
return (rgb >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code green} value
|
||||
*
|
||||
* @return green value
|
||||
*/
|
||||
public int getGreen() {
|
||||
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
|
||||
return (rgb >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code blue} value
|
||||
*
|
||||
* @return blue value
|
||||
*/
|
||||
public int getBlue() {
|
||||
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
|
||||
return rgb & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest legacy color of this color object.
|
||||
* If the color was defined in constructor, it's returned.
|
||||
* Otherwise, the closest color is calculated the then returned.
|
||||
*
|
||||
* @return closest legacy color
|
||||
*/
|
||||
public EnumChatFormat getLegacyColor() {
|
||||
if (legacyColor == null) legacyColor = loadClosestColor();
|
||||
return legacyColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rgb combination as a 6-digit hex code string
|
||||
*
|
||||
* @return the rgb combination as a 6-digit hex code string
|
||||
*/
|
||||
public String getHexCode() {
|
||||
if (hexCode == null) hexCode = String.format("%06X", rgb);
|
||||
return hexCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the color into a valid color value used in color field in chat component.
|
||||
* That is either 6-digit hex code prefixed with '#', or lowercase legacy color.
|
||||
*
|
||||
* @return the color serialized for use in chat component
|
||||
*/
|
||||
public String toString() {
|
||||
EnumChatFormat legacyEquivalent = EnumChatFormat.fromRGBExact(getRed(), getGreen(), getBlue());
|
||||
if (legacyEquivalent != null) {
|
||||
//not sending old colors as RGB to 1.16 clients if not needed as <1.16 servers will fail to apply color
|
||||
return legacyEquivalent.toString().toLowerCase();
|
||||
}
|
||||
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.
|
||||
* If it's prefixed with '#', it's considered as a hex code.
|
||||
* Otherwise, it is considered being a lowercase legacy color.
|
||||
*
|
||||
* @param string
|
||||
* string from color field in chat component
|
||||
* @return An instance from specified string or null if string is null
|
||||
*/
|
||||
public static TextColor fromString(String string) {
|
||||
if (string == null) return null;
|
||||
if (string.startsWith("#")) return new TextColor(string.substring(1));
|
||||
return new TextColor(EnumChatFormat.valueOf(string.toUpperCase()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class extending IChatBaseComponent that holds the original value
|
||||
* to avoid deserializing it, which is an unnecessary operation that
|
||||
* slows down the performance, as well as makes code more complicated
|
||||
* and open to problems.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public Object get() {
|
||||
return originalComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(ProtocolVersion clientVersion) {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IChatBaseComponent> getExtra(){
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatModifier getModifier() {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
@@ -8,176 +8,200 @@ import me.neznamy.tab.api.chat.TextColor;
|
||||
import me.neznamy.tab.api.chat.rgb.format.BukkitFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.format.CMIFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.format.HtmlFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.format.KyoriFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.format.RGBFormatter;
|
||||
import me.neznamy.tab.api.chat.rgb.format.UnnamedFormat1;
|
||||
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;
|
||||
|
||||
/**
|
||||
* A helper class to reformat all RGB formats into the default #RRGGBB and apply gradients
|
||||
*/
|
||||
public class RGBUtils {
|
||||
|
||||
/** Instance of the class */
|
||||
private static final RGBUtils instance = new RGBUtils();
|
||||
|
||||
/** Registered RGB formatters */
|
||||
private final RGBFormatter[] formats;
|
||||
|
||||
/** Registered gradient patterns */
|
||||
private final GradientPattern[] gradients;
|
||||
|
||||
/** TAB's RGB pattern, used to convert text to bukkit format for boss bar */
|
||||
private final Pattern tabPattern = Pattern.compile("#[0-9a-fA-F]{6}");
|
||||
/** Instance of the class */
|
||||
private static final RGBUtils instance = new RGBUtils();
|
||||
|
||||
/** RGB pattern for legacy codes */
|
||||
private final Pattern tabPatternLegacy = Pattern.compile("#[0-9a-fA-F]{6}\\|.");
|
||||
/** Registered RGB formatters */
|
||||
private final RGBFormatter[] formats;
|
||||
|
||||
/**
|
||||
* Constructs new instance and loads all RGB patterns and gradients
|
||||
*/
|
||||
public RGBUtils() {
|
||||
formats = new RGBFormatter[] {
|
||||
new BukkitFormat(),
|
||||
new CMIFormat(),
|
||||
new UnnamedFormat1(),
|
||||
new HtmlFormat()
|
||||
};
|
||||
gradients = new GradientPattern[] {
|
||||
//{#RRGGBB>}text{#RRGGBB<}
|
||||
new CMIGradient(),
|
||||
//<#RRGGBB>Text</#RRGGBB>
|
||||
new CommonGradient(Pattern.compile("<#[0-9a-fA-F]{6}>[^<]*</#[0-9a-fA-F]{6}>"),
|
||||
Pattern.compile("<#[0-9a-fA-F]{6}\\|.>[^<]*</#[0-9a-fA-F]{6}>"),
|
||||
"<#", 9, 2, 9, 7),
|
||||
//<$#RRGGBB>Text<$#RRGGBB>
|
||||
new CommonGradient(Pattern.compile("<\\$#[0-9a-fA-F]{6}>[^<]*<\\$#[0-9a-fA-F]{6}>"),
|
||||
Pattern.compile("<\\$#[0-9a-fA-F]{6}\\|.>[^<]*<\\$#[0-9a-fA-F]{6}>"),
|
||||
"<$", 10, 3, 10, 7),
|
||||
new KyoriGradient()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of this class
|
||||
* @return instance
|
||||
*/
|
||||
public static RGBUtils getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all RGB formats and gradients to text and returns it.
|
||||
* This method is called on every property initialization to convert formats
|
||||
* once instead of on every refresh, if possible. Because of that, parameter
|
||||
* {@code ignorePlaceholders} exists to not break raw placeholder identifiers
|
||||
* @param text
|
||||
* original text
|
||||
* @param ignorePlaceholders
|
||||
* whether placeholders should be ignored or not
|
||||
* @return text where everything is converted to #RRGGBB
|
||||
*/
|
||||
public String applyFormats(String text, boolean ignorePlaceholders) {
|
||||
String replaced = text;
|
||||
for (GradientPattern pattern : gradients) {
|
||||
replaced = pattern.applyPattern(replaced, ignorePlaceholders);
|
||||
}
|
||||
for (RGBFormatter formatter : formats) {
|
||||
replaced = formatter.reformat(replaced);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
/** Registered gradient patterns */
|
||||
private final GradientPattern[] gradients;
|
||||
|
||||
/**
|
||||
* Converts TAB's RGB format (#RRGGBB) into bukkit one (&x&r&r&g&g&b&b)
|
||||
* for modern clients (1.16+), for legacy clients it will use the closest color.
|
||||
* @param text
|
||||
* text to convert
|
||||
* @param rgbClient
|
||||
* whether client accepts RGB or not
|
||||
* @return converted text
|
||||
*/
|
||||
public String convertToBukkitFormat(String text, boolean rgbClient) {
|
||||
if (text == null) return null;
|
||||
if (!text.contains("#")) return text; //no rgb codes
|
||||
if (rgbClient) {
|
||||
//converting random formats to TAB one
|
||||
String replaced = applyFormats(text, false);
|
||||
for (Pattern p : new Pattern[]{tabPatternLegacy, tabPattern}) {
|
||||
Matcher m = p.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = "&x&" + hexCode.charAt(1) + "&" + hexCode.charAt(2) + "&" + hexCode.charAt(3) + "&" + hexCode.charAt(4) + "&" + hexCode.charAt(5) + "&" + hexCode.charAt(6);
|
||||
replaced = replaced.replace(hexCode, EnumChatFormat.color(fixed));
|
||||
}
|
||||
}
|
||||
return replaced;
|
||||
} else {
|
||||
return convertRGBtoLegacy(text);
|
||||
}
|
||||
}
|
||||
/** TAB's RGB pattern, used to convert text to bukkit format for boss bar */
|
||||
private final Pattern tabPattern = Pattern.compile("#[0-9a-fA-F]{6}");
|
||||
|
||||
/**
|
||||
* Converts all hex codes in given string to legacy codes
|
||||
* @param text
|
||||
* text to convert
|
||||
* @return translated text
|
||||
*/
|
||||
public String convertRGBtoLegacy(String text) {
|
||||
if (text == null) return null;
|
||||
if (!text.contains("#")) return EnumChatFormat.color(text);
|
||||
String applied = applyFormats(text, false);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < applied.length(); i++){
|
||||
char c = applied.charAt(i);
|
||||
if (c == '#' && applied.length() > i+6) {
|
||||
String hexCode = applied.substring(i+1, i+7);
|
||||
if (isHexCode(hexCode)) {
|
||||
if (containsLegacyCode(applied, i)) {
|
||||
sb.append(new TextColor(hexCode, EnumChatFormat.getByChar(applied.charAt(i+8))).getLegacyColor().getFormat());
|
||||
i += 8;
|
||||
} else {
|
||||
sb.append(new TextColor(hexCode).getLegacyColor().getFormat());
|
||||
i += 6;
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
/** RGB pattern for legacy codes */
|
||||
private final Pattern tabPatternLegacy = Pattern.compile("#[0-9a-fA-F]{6}\\|.");
|
||||
|
||||
/**
|
||||
* Returns true if entered string is a valid 6-digit combination of
|
||||
* hexadecimal numbers, false if not
|
||||
* @param string
|
||||
* string to check
|
||||
* @return {@code true} if valid, {@code false} if not
|
||||
*/
|
||||
public boolean isHexCode(String string) {
|
||||
if (string.length() != 6) return false;
|
||||
for (int i=0; i<6; i++) {
|
||||
char c = string.charAt(i);
|
||||
if (c < 48 || (c > 57 && c < 65) || (c > 70 && c < 97) || c > 102) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if text contains legacy color request at defined RGB index start
|
||||
* @param text
|
||||
* text to check
|
||||
* @param i
|
||||
* current index start
|
||||
* @return {@code true} if legacy color is defined and valid, {@code false} otherwise
|
||||
*/
|
||||
private static boolean containsLegacyCode(String text, int i) {
|
||||
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
|
||||
return EnumChatFormat.getByChar(text.charAt(i+8)) != null;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance and loads all RGB patterns and gradients
|
||||
*/
|
||||
public RGBUtils() {
|
||||
formats = new RGBFormatter[] {
|
||||
new BukkitFormat(),
|
||||
new CMIFormat(),
|
||||
new UnnamedFormat1(),
|
||||
new HtmlFormat(),
|
||||
new KyoriFormat()
|
||||
};
|
||||
gradients = new GradientPattern[] {
|
||||
//{#RRGGBB>}text{#RRGGBB<}
|
||||
new CMIGradient(),
|
||||
//<#RRGGBB>Text</#RRGGBB>
|
||||
new CommonGradient(Pattern.compile("<#[0-9a-fA-F]{6}>[^<]*</#[0-9a-fA-F]{6}>"),
|
||||
Pattern.compile("<#[0-9a-fA-F]{6}\\|.>[^<]*</#[0-9a-fA-F]{6}>"),
|
||||
"<#", 9, 2, 9, 7),
|
||||
//<$#RRGGBB>Text<$#RRGGBB>
|
||||
new CommonGradient(Pattern.compile("<\\$#[0-9a-fA-F]{6}>[^<]*<\\$#[0-9a-fA-F]{6}>"),
|
||||
Pattern.compile("<\\$#[0-9a-fA-F]{6}\\|.>[^<]*<\\$#[0-9a-fA-F]{6}>"),
|
||||
"<$", 10, 3, 10, 7),
|
||||
new KyoriGradient()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of this class
|
||||
*
|
||||
* @return instance
|
||||
*/
|
||||
public static RGBUtils getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all RGB formats and gradients to text and returns it.
|
||||
*
|
||||
* @param text
|
||||
* original text
|
||||
* @return text where everything is converted to #RRGGBB
|
||||
*/
|
||||
public String applyFormats(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
String replaced = text;
|
||||
for (GradientPattern pattern : gradients) {
|
||||
replaced = pattern.applyPattern(replaced, false);
|
||||
}
|
||||
for (RGBFormatter formatter : formats) {
|
||||
replaced = formatter.reformat(replaced);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all gradient formats to text and returns it. This only affects
|
||||
* usage where no placeholder is used inside.
|
||||
*
|
||||
* @param text
|
||||
* original text
|
||||
* @return text where all gradients with static text are converted to #RRGGBB
|
||||
*/
|
||||
public String applyCleanGradients(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
String replaced = text;
|
||||
for (GradientPattern pattern : gradients) {
|
||||
replaced = pattern.applyPattern(replaced, true);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts TAB's RGB format (#RRGGBB) into bukkit one
|
||||
* (&x&r&r&g&g&b&b) for modern
|
||||
* clients (1.16+), for legacy clients it will use the closest color.
|
||||
*
|
||||
* @param text
|
||||
* text to convert
|
||||
* @param rgbClient
|
||||
* whether client accepts RGB or not
|
||||
* @return converted text
|
||||
*/
|
||||
public String convertToBukkitFormat(String text, boolean rgbClient) {
|
||||
if (text == null) return null;
|
||||
if (!text.contains("#")) return text; //no rgb codes
|
||||
if (rgbClient) {
|
||||
//converting random formats to TAB one
|
||||
String replaced = applyFormats(text);
|
||||
for (Pattern p : new Pattern[]{tabPatternLegacy, tabPattern}) {
|
||||
Matcher m = p.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = "&x&" + hexCode.charAt(1) + "&" + hexCode.charAt(2) + "&" + hexCode.charAt(3) + "&" + hexCode.charAt(4) + "&" + hexCode.charAt(5) + "&" + hexCode.charAt(6);
|
||||
replaced = replaced.replace(hexCode, EnumChatFormat.color(fixed));
|
||||
}
|
||||
}
|
||||
return replaced;
|
||||
} else {
|
||||
return convertRGBtoLegacy(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all hex codes in given string to legacy codes
|
||||
*
|
||||
* @param text
|
||||
* text to convert
|
||||
* @return translated text
|
||||
*/
|
||||
public String convertRGBtoLegacy(String text) {
|
||||
if (text == null) return null;
|
||||
if (!text.contains("#")) return EnumChatFormat.color(text);
|
||||
String applied = applyFormats(text);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < applied.length(); i++){
|
||||
char c = applied.charAt(i);
|
||||
if (c == '#' && applied.length() > i+6) {
|
||||
String hexCode = applied.substring(i+1, i+7);
|
||||
if (isHexCode(hexCode)) {
|
||||
if (containsLegacyCode(applied, i)) {
|
||||
sb.append(new TextColor(hexCode, EnumChatFormat.getByChar(applied.charAt(i+8))).getLegacyColor().getFormat());
|
||||
i += 8;
|
||||
} else {
|
||||
sb.append(new TextColor(hexCode).getLegacyColor().getFormat());
|
||||
i += 6;
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if entered string is a valid 6-digit combination of
|
||||
* hexadecimal numbers, false if not
|
||||
*
|
||||
* @param string
|
||||
* string to check
|
||||
* @return {@code true} if valid, {@code false} if not
|
||||
*/
|
||||
public boolean isHexCode(String string) {
|
||||
Preconditions.checkNotNull(string, "string");
|
||||
if (string.length() != 6) return false;
|
||||
for (int i=0; i<6; i++) {
|
||||
char c = string.charAt(i);
|
||||
if (c < 48 || (c > 57 && c < 65) || (c > 70 && c < 97) || c > 102) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if text contains legacy color request at defined RGB index start
|
||||
*
|
||||
* @param text
|
||||
* text to check
|
||||
* @param i
|
||||
* current index start
|
||||
* @return {@code true} if legacy color is defined and valid, {@code false} otherwise
|
||||
*/
|
||||
private static boolean containsLegacyCode(String text, int i) {
|
||||
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
|
||||
return EnumChatFormat.getByChar(text.charAt(i+8)) != null;
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,22 @@ import java.util.regex.Pattern;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
|
||||
/**
|
||||
* Formatter for &x&R&R&G&G&B&B
|
||||
* Formatter for &x&R&R&G&G&B&B
|
||||
*/
|
||||
public class BukkitFormat implements RGBFormatter {
|
||||
|
||||
private final Pattern pattern = Pattern.compile("[" + EnumChatFormat.COLOR_CHAR + "&]x[[" + EnumChatFormat.COLOR_CHAR + "&]\\p{XDigit}]{12}");
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
if (!text.contains("&") && !text.contains(EnumChatFormat.COLOR_CHAR + "x")) return text;
|
||||
String replaced = text;
|
||||
Matcher m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = new String(new char[] {'#', hexCode.charAt(3), hexCode.charAt(5), hexCode.charAt(7), hexCode.charAt(9), hexCode.charAt(11), hexCode.charAt(13)});
|
||||
replaced = replaced.replace(hexCode, fixed);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
private final Pattern pattern = Pattern.compile("[" + EnumChatFormat.COLOR_CHAR + "&]x[" + EnumChatFormat.COLOR_CHAR + "&\\p{XDigit}]{12}");
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
if (!text.contains("&x") && !text.contains(EnumChatFormat.COLOR_CHAR + "x")) return text;
|
||||
String replaced = text;
|
||||
Matcher m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = new String(new char[] {'#', hexCode.charAt(3), hexCode.charAt(5), hexCode.charAt(7), hexCode.charAt(9), hexCode.charAt(11), hexCode.charAt(13)});
|
||||
replaced = replaced.replace(hexCode, fixed);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,18 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class CMIFormat implements RGBFormatter {
|
||||
|
||||
private final Pattern pattern = Pattern.compile("\\{#[0-9a-fA-F]{6}\\}");
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
if (!text.contains("{#")) return text;
|
||||
String replaced = text;
|
||||
Matcher m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = hexCode.substring(2, 8);
|
||||
replaced = replaced.replace(hexCode, "#" + fixed);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
private final Pattern pattern = Pattern.compile("\\{#[0-9a-fA-F]{6}}");
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
if (!text.contains("{#")) return text;
|
||||
String replaced = text;
|
||||
Matcher m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = hexCode.substring(2, 8);
|
||||
replaced = replaced.replace(hexCode, "#" + fixed);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
}
|
||||
@@ -8,18 +8,18 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class HtmlFormat implements RGBFormatter {
|
||||
|
||||
private final Pattern pattern = Pattern.compile("#<[0-9a-fA-F]{6}>");
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
if (!text.contains("#<")) return text;
|
||||
Matcher m = pattern.matcher(text);
|
||||
String replaced = text;
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = hexCode.substring(2, 8);
|
||||
replaced = replaced.replace(hexCode, "#" + fixed);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
private final Pattern pattern = Pattern.compile("#<[0-9a-fA-F]{6}>");
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
if (!text.contains("#<")) return text;
|
||||
Matcher m = pattern.matcher(text);
|
||||
String replaced = text;
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = hexCode.substring(2, 8);
|
||||
replaced = replaced.replace(hexCode, "#" + fixed);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.neznamy.tab.api.chat.rgb.format;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Formatter for <#RRGGBB>
|
||||
*/
|
||||
public class KyoriFormat implements RGBFormatter {
|
||||
|
||||
private final Pattern pattern = Pattern.compile("<#[0-9a-fA-F]{6}>");
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
if (!text.contains("<#")) return text;
|
||||
String replaced = text;
|
||||
Matcher m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String hexCode = m.group();
|
||||
String fixed = hexCode.substring(2, 8);
|
||||
replaced = replaced.replace(hexCode, "#" + fixed);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,12 @@ package me.neznamy.tab.api.chat.rgb.format;
|
||||
*/
|
||||
public interface RGBFormatter {
|
||||
|
||||
/**
|
||||
* Reformats RGB codes in provided text into #RRGGBB format
|
||||
* @param text - text to format
|
||||
* @return reformatted text
|
||||
*/
|
||||
String reformat(String text);
|
||||
/**
|
||||
* Reformats RGB codes in provided text into #RRGGBB format
|
||||
*
|
||||
* @param text
|
||||
* text to format
|
||||
* @return reformatted text
|
||||
*/
|
||||
String reformat(String text);
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
package me.neznamy.tab.api.chat.rgb.format;
|
||||
|
||||
/**
|
||||
* Formatter for &#RRGGBB
|
||||
* Formatter for &#RRGGBB
|
||||
*/
|
||||
public class UnnamedFormat1 implements RGBFormatter {
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
return text.contains("&#") ? text.replace("&#", "#") : text;
|
||||
}
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
return text.contains("&#") ? text.replace("&#", "#") : text;
|
||||
}
|
||||
}
|
||||
@@ -5,26 +5,26 @@ import java.util.regex.Pattern;
|
||||
|
||||
public class CMIGradient extends CommonGradient {
|
||||
|
||||
//pattern for {#RRGGBB<>}
|
||||
private final Pattern shortcutPattern = Pattern.compile("\\{#[0-9a-fA-F]{6}<>\\}");
|
||||
|
||||
public CMIGradient() {
|
||||
super(Pattern.compile("\\{#[0-9a-fA-F]{6}>\\}[^\\{]*\\{#[0-9a-fA-F]{6}<\\}"),
|
||||
Pattern.compile("\\{#[0-9a-fA-F]{6}\\|.>\\}[^\\{]*\\{#[0-9a-fA-F]{6}<\\}"),
|
||||
"{#", 9, 2, 10, 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String applyPattern(String text, boolean ignorePlaceholders) {
|
||||
String replaced = text;
|
||||
if (replaced.contains("<>}")) {
|
||||
Matcher m = shortcutPattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
String code = format.substring(2, 8);
|
||||
replaced = replaced.replace(format, "{#" + code + "<}{#" + code + ">}");
|
||||
}
|
||||
}
|
||||
return super.applyPattern(replaced, ignorePlaceholders);
|
||||
}
|
||||
//pattern for {#RRGGBB<>}
|
||||
private final Pattern shortcutPattern = Pattern.compile("\\{#[0-9a-fA-F]{6}<>}");
|
||||
|
||||
public CMIGradient() {
|
||||
super(Pattern.compile("\\{#[0-9a-fA-F]{6}>}[^{]*\\{#[0-9a-fA-F]{6}<}"),
|
||||
Pattern.compile("\\{#[0-9a-fA-F]{6}\\|.>}[^{]*\\{#[0-9a-fA-F]{6}<}"),
|
||||
"{#", 9, 2, 10, 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String applyPattern(String text, boolean ignorePlaceholders) {
|
||||
String replaced = text;
|
||||
if (replaced.contains("<>}")) {
|
||||
Matcher m = shortcutPattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
String code = format.substring(2, 8);
|
||||
replaced = replaced.replace(format, "{#" + code + "<}{#" + code + ">}");
|
||||
}
|
||||
}
|
||||
return super.applyPattern(replaced, ignorePlaceholders);
|
||||
}
|
||||
}
|
||||
@@ -8,50 +8,50 @@ import me.neznamy.tab.api.chat.TextColor;
|
||||
|
||||
public class CommonGradient implements GradientPattern {
|
||||
|
||||
private final Pattern pattern;
|
||||
private final Pattern legacyPattern;
|
||||
private final String containCheck;
|
||||
private final int legacyCharPosition;
|
||||
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;
|
||||
String replaced = text;
|
||||
Matcher m = legacyPattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
EnumChatFormat legacyColor = EnumChatFormat.getByChar(format.charAt(legacyCharPosition));
|
||||
if ((ignorePlaceholders && format.contains("%")) || legacyColor == null) continue;
|
||||
TextColor start = new TextColor(format.substring(startColorStart, startColorStart+6), legacyColor);
|
||||
String message = format.substring(messageStart+2, format.length()-10);
|
||||
TextColor end = new TextColor(format.substring(format.length()-endColorStartSub, format.length()-endColorStartSub+6));
|
||||
String applied = asGradient(start, message, end);
|
||||
replaced = replaced.replace(format, applied);
|
||||
}
|
||||
m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
if (ignorePlaceholders && format.contains("%")) continue;
|
||||
TextColor start = new TextColor(format.substring(startColorStart, startColorStart+6));
|
||||
String message = format.substring(messageStart, format.length()-10);
|
||||
TextColor end = new TextColor(format.substring(format.length()-endColorStartSub, format.length()-endColorStartSub+6));
|
||||
String applied = asGradient(start, message, end);
|
||||
replaced = replaced.replace(format, applied);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
private final Pattern pattern;
|
||||
private final Pattern legacyPattern;
|
||||
private final String containCheck;
|
||||
private final int legacyCharPosition;
|
||||
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;
|
||||
String replaced = text;
|
||||
Matcher m = legacyPattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
EnumChatFormat legacyColor = EnumChatFormat.getByChar(format.charAt(legacyCharPosition));
|
||||
if ((ignorePlaceholders && format.contains("%")) || legacyColor == null) continue;
|
||||
TextColor start = new TextColor(format.substring(startColorStart, startColorStart+6), legacyColor);
|
||||
String message = format.substring(messageStart+2, format.length()-10);
|
||||
TextColor end = new TextColor(format.substring(format.length()-endColorStartSub, format.length()-endColorStartSub+6));
|
||||
String applied = asGradient(start, message, end);
|
||||
replaced = replaced.replace(format, applied);
|
||||
}
|
||||
m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
if (ignorePlaceholders && format.contains("%")) continue;
|
||||
TextColor start = new TextColor(format.substring(startColorStart, startColorStart+6));
|
||||
String message = format.substring(messageStart, format.length()-10);
|
||||
TextColor end = new TextColor(format.substring(format.length()-endColorStartSub, format.length()-endColorStartSub+6));
|
||||
String applied = asGradient(start, message, end);
|
||||
replaced = replaced.replace(format, applied);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
}
|
||||
@@ -8,44 +8,50 @@ import me.neznamy.tab.api.chat.TextColor;
|
||||
*/
|
||||
public interface GradientPattern {
|
||||
|
||||
/**
|
||||
* Applies gradients in provided text and returns text using only #RRGGBB
|
||||
* @param text - text to be reformatted
|
||||
* @return reformatted text
|
||||
*/
|
||||
String applyPattern(String text, boolean ignorePlaceholders);
|
||||
|
||||
/**
|
||||
* Returns gradient text based on start color, text and end color
|
||||
* @param start - start color
|
||||
* @param text - text to be reformatted
|
||||
* @param end - end color
|
||||
* @return reformatted text
|
||||
*/
|
||||
default String asGradient(TextColor start, String text, TextColor end) {
|
||||
//lazy support for magic codes in gradients
|
||||
String magicCodes = EnumChatFormat.getLastColors(text);
|
||||
String deColorized = text.substring(magicCodes.length());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int length = deColorized.length();
|
||||
if (length == 1) {
|
||||
sb.append("#");
|
||||
sb.append(new TextColor(start.getRed(), start.getGreen(), start.getBlue()).getHexCode());
|
||||
if (start.isLegacyColorForced()) sb.append("|").append(start.getLegacyColor().getCharacter());
|
||||
sb.append(magicCodes);
|
||||
sb.append(deColorized);
|
||||
return sb.toString();
|
||||
}
|
||||
for (int i=0; i<length; i++) {
|
||||
int red = (int) (start.getRed() + (float)(end.getRed() - start.getRed())/(length-1)*i);
|
||||
int green = (int) (start.getGreen() + (float)(end.getGreen() - start.getGreen())/(length-1)*i);
|
||||
int blue = (int) (start.getBlue() + (float)(end.getBlue() - start.getBlue())/(length-1)*i);
|
||||
sb.append("#");
|
||||
sb.append(new TextColor(red, green, blue).getHexCode());
|
||||
if (start.isLegacyColorForced()) sb.append("|").append(start.getLegacyColor().getCharacter());
|
||||
sb.append(magicCodes);
|
||||
sb.append(deColorized.charAt(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
/**
|
||||
* Applies gradients in provided text and returns text using only #RRGGBB
|
||||
*
|
||||
* @param text
|
||||
* text to be reformatted
|
||||
* @return reformatted text
|
||||
*/
|
||||
String applyPattern(String text, boolean ignorePlaceholders);
|
||||
|
||||
/**
|
||||
* Returns gradient text based on start color, text and end color
|
||||
*
|
||||
* @param start
|
||||
* start color
|
||||
* @param text
|
||||
* text to be reformatted
|
||||
* @param end
|
||||
* end color
|
||||
* @return reformatted text
|
||||
*/
|
||||
default String asGradient(TextColor start, String text, TextColor end) {
|
||||
//lazy support for magic codes in gradients
|
||||
String magicCodes = EnumChatFormat.getLastColors(text);
|
||||
String deColorized = text.substring(magicCodes.length());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int length = deColorized.length();
|
||||
if (length == 1) {
|
||||
sb.append("#");
|
||||
sb.append(new TextColor(start.getRed(), start.getGreen(), start.getBlue()).getHexCode());
|
||||
if (start.isLegacyColorForced()) sb.append("|").append(start.getLegacyColor().getCharacter());
|
||||
sb.append(magicCodes);
|
||||
sb.append(deColorized);
|
||||
return sb.toString();
|
||||
}
|
||||
for (int i=0; i<length; i++) {
|
||||
int red = (int) (start.getRed() + (float)(end.getRed() - start.getRed())/(length-1)*i);
|
||||
int green = (int) (start.getGreen() + (float)(end.getGreen() - start.getGreen())/(length-1)*i);
|
||||
int blue = (int) (start.getBlue() + (float)(end.getBlue() - start.getBlue())/(length-1)*i);
|
||||
sb.append("#");
|
||||
sb.append(new TextColor(red, green, blue).getHexCode());
|
||||
if (start.isLegacyColorForced()) sb.append("|").append(start.getLegacyColor().getCharacter());
|
||||
sb.append(magicCodes);
|
||||
sb.append(deColorized.charAt(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -7,39 +7,40 @@ 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 {
|
||||
|
||||
private final Pattern pattern = Pattern.compile("<gradient:#[0-9a-fA-F]{6}:#[0-9a-fA-F]{6}>[^<]*</gradient>");
|
||||
private final Pattern pattern = Pattern.compile("<gradient:#[0-9a-fA-F]{6}:#[0-9a-fA-F]{6}>[^<]*</gradient>");
|
||||
|
||||
private final Pattern patternLegacy = Pattern.compile("<gradient:#[0-9a-fA-F]{6}\\|.:#[0-9a-fA-F]{6}>[^<]*</gradient>");
|
||||
|
||||
@Override
|
||||
public String applyPattern(String text, boolean ignorePlaceholders) {
|
||||
if (!text.contains("<grad")) return text;
|
||||
String replaced = text;
|
||||
Matcher m = patternLegacy.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
EnumChatFormat legacyColor = EnumChatFormat.getByChar(format.charAt(18));
|
||||
if ((ignorePlaceholders && format.contains("%")) || legacyColor == null) continue;
|
||||
TextColor start = new TextColor(format.substring(11, 17), legacyColor);
|
||||
String message = format.substring(28, format.length()-11);
|
||||
TextColor end = new TextColor(format.substring(21, 27));
|
||||
String applied = asGradient(start, message, end);
|
||||
replaced = replaced.replace(format, applied);
|
||||
}
|
||||
m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
if (ignorePlaceholders && format.contains("%")) continue;
|
||||
TextColor start = new TextColor(format.substring(11, 17));
|
||||
String message = format.substring(26, format.length()-11);
|
||||
TextColor end = new TextColor(format.substring(19, 25));
|
||||
String applied = asGradient(start, message, end);
|
||||
replaced = replaced.replace(format, applied);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
private final Pattern patternLegacy = Pattern.compile("<gradient:#[0-9a-fA-F]{6}\\|.:#[0-9a-fA-F]{6}>[^<]*</gradient>");
|
||||
|
||||
@Override
|
||||
public String applyPattern(String text, boolean ignorePlaceholders) {
|
||||
if (!text.contains("<grad")) return text;
|
||||
String replaced = text;
|
||||
Matcher m = patternLegacy.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
EnumChatFormat legacyColor = EnumChatFormat.getByChar(format.charAt(18));
|
||||
if ((ignorePlaceholders && format.contains("%")) || legacyColor == null) continue;
|
||||
TextColor start = new TextColor(format.substring(11, 17), legacyColor);
|
||||
String message = format.substring(28, format.length()-11);
|
||||
TextColor end = new TextColor(format.substring(21, 27));
|
||||
String applied = asGradient(start, message, end);
|
||||
replaced = replaced.replace(format, applied);
|
||||
}
|
||||
m = pattern.matcher(replaced);
|
||||
while (m.find()) {
|
||||
String format = m.group();
|
||||
if (ignorePlaceholders && format.contains("%")) continue;
|
||||
TextColor start = new TextColor(format.substring(11, 17));
|
||||
String message = format.substring(26, format.length()-11);
|
||||
TextColor end = new TextColor(format.substring(19, 25));
|
||||
String applied = asGradient(start, message, end);
|
||||
replaced = replaced.replace(format, applied);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ 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
|
||||
@@ -24,405 +25,406 @@ import me.neznamy.tab.api.TabAPI;
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class ConfigurationFile {
|
||||
|
||||
/** Comments on top of the file */
|
||||
protected List<String> header;
|
||||
/** Comments on top of the file */
|
||||
protected List<String> header;
|
||||
|
||||
/** Configuration file content */
|
||||
protected Map<String, Object> values;
|
||||
/** Configuration file content */
|
||||
protected Map<String, Object> values;
|
||||
|
||||
/** File to use */
|
||||
protected final File file;
|
||||
/** File to use */
|
||||
protected final File file;
|
||||
|
||||
/**
|
||||
* Constructs new instance and attempts to load specified configuration file.
|
||||
* If file does not exist, default file is copied from {@code source}.
|
||||
*
|
||||
* @param source
|
||||
* Source to copy file from if it does not exist
|
||||
* @param destination
|
||||
* File destination to use
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code destination} is null
|
||||
* @throws IllegalStateException
|
||||
* if file does not exist and source is null
|
||||
* @throws IOException
|
||||
* if I/O operation with the file unexpectedly fails
|
||||
*/
|
||||
protected ConfigurationFile(InputStream source, File destination) throws IOException {
|
||||
if (destination == null) throw new IllegalArgumentException("Destination cannot be null");
|
||||
this.file = destination;
|
||||
if (file.getParentFile() != null) file.getParentFile().mkdirs();
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
detectHeader();
|
||||
}
|
||||
/**
|
||||
* Constructs new instance and attempts to load specified configuration file.
|
||||
* If file does not exist, default file is copied from {@code source}.
|
||||
*
|
||||
* @param source
|
||||
* Source to copy file from if it does not exist
|
||||
* @param destination
|
||||
* File destination to use
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code destination} is null
|
||||
* @throws IllegalStateException
|
||||
* if file does not exist and source is null
|
||||
* @throws IOException
|
||||
* if I/O operation with the file unexpectedly fails
|
||||
*/
|
||||
protected ConfigurationFile(InputStream source, File destination) throws IOException {
|
||||
Preconditions.checkNotNull(destination, "destination");
|
||||
this.file = destination;
|
||||
if (file.getParentFile() != null && !file.getParentFile().exists()) Files.createDirectories(file.getParentFile().toPath());
|
||||
if (!file.exists() && source == null) throw new IllegalStateException("File does not exist and source is null");
|
||||
if (file.createNewFile()) {
|
||||
// Files.copy(source, file.toPath());
|
||||
//avoiding MalformedInputException thrown on fabric due to file not having UTF8 encoding by default
|
||||
//also automatically using the encoding so users don't have to worry about it anymore
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8));
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))){
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
writer.write(line + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
detectHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves values from map to the file
|
||||
*/
|
||||
public abstract void save();
|
||||
/**
|
||||
* Saves values from map to the file
|
||||
*/
|
||||
public abstract void save();
|
||||
|
||||
/**
|
||||
* Returns simple name of the file
|
||||
* @return simple name of the file
|
||||
*/
|
||||
public String getName() {
|
||||
return file.getName();
|
||||
}
|
||||
/**
|
||||
* Returns simple name of the file
|
||||
*
|
||||
* @return simple name of the file
|
||||
*/
|
||||
public String getName() {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root value map
|
||||
* @return the root value map
|
||||
*/
|
||||
public Map<String, Object> getValues(){
|
||||
return values;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
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()}
|
||||
* called and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file
|
||||
*/
|
||||
public Object getObject(String path, Object defaultValue) {
|
||||
Object value = values;
|
||||
for (String section : path.contains(".") ? path.split("\\.") : new String[] {path}) {
|
||||
if (!(value instanceof Map)) {
|
||||
if (defaultValue != null) set(path, defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
value = getIgnoreCase((Map<Object, Object>) value, section);
|
||||
}
|
||||
if (value == null && defaultValue != null) {
|
||||
TabAPI.getInstance().debug("Inserting missing config option \"" + path + "\" with value \"" + defaultValue + "\" into " + file.getName());
|
||||
set(path, defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Gets config option with specified path. If the option is not present and
|
||||
* {@code defaultValue} is not {@code null}, value is inserted, {@link #save()}
|
||||
* called and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file
|
||||
*/
|
||||
public Object getObject(String path, Object defaultValue) {
|
||||
Object value = values;
|
||||
for (String section : path.contains(".") ? path.split("\\.") : new String[] {path}) {
|
||||
if (!(value instanceof Map)) {
|
||||
if (defaultValue != null) set(path, defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
value = getIgnoreCase((Map<Object, Object>) value, section);
|
||||
}
|
||||
if (value == null && defaultValue != null) {
|
||||
TabAPI.getInstance().debug("Inserting missing config option \"" + path + "\" with value \"" + defaultValue + "\" into " + file.getName());
|
||||
set(path, defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path. If option is not present,
|
||||
* {@code null} is returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from configuration file or null if not present
|
||||
*/
|
||||
public Object getObject(String path) {
|
||||
return getObject(path, null);
|
||||
}
|
||||
|
||||
public Object getObject(String[] path) {
|
||||
Object value = values;
|
||||
for (String section : path) {
|
||||
if (!(value instanceof Map)) {
|
||||
return null;
|
||||
}
|
||||
value = getIgnoreCase((Map<Object, Object>) value, section);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path. If option is not present,
|
||||
* {@code null} is returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from configuration file or null if not present
|
||||
*/
|
||||
public Object getObject(String path) {
|
||||
return getObject(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value from map without case sensitivity of the key. Returns
|
||||
* {@code null} if no such key is found.
|
||||
*
|
||||
* @param map
|
||||
* map to get value from
|
||||
* @param key
|
||||
* case-insensitive key name
|
||||
* @return map value from case-insensitive key
|
||||
*/
|
||||
private Object getIgnoreCase(Map<Object, Object> map, String key) {
|
||||
for (Entry<Object, Object> entry : map.entrySet()) {
|
||||
if (entry.getKey().toString().equalsIgnoreCase(key)) return entry.getValue();
|
||||
}
|
||||
return map.get(key);
|
||||
}
|
||||
public Object getObject(String[] path) {
|
||||
Object value = values;
|
||||
for (String section : path) {
|
||||
if (!(value instanceof Map)) {
|
||||
return null;
|
||||
}
|
||||
value = getIgnoreCase((Map<Object, Object>) value, section);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code String}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
public String getString(String path) {
|
||||
return getString(path, null);
|
||||
}
|
||||
/**
|
||||
* Returns value from map without case sensitivity of the key. Returns
|
||||
* {@code null} if no such key is found.
|
||||
*
|
||||
* @param map
|
||||
* map to get value from
|
||||
* @param key
|
||||
* case-insensitive key name
|
||||
* @return map value from case-insensitive key
|
||||
*/
|
||||
private Object getIgnoreCase(Map<Object, Object> map, String key) {
|
||||
for (Entry<Object, Object> entry : map.entrySet()) {
|
||||
if (entry.getKey().toString().equalsIgnoreCase(key)) return entry.getValue();
|
||||
}
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code String}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code String}
|
||||
*/
|
||||
public String getString(String path, String defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
return String.valueOf(value);
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code String}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
public String getString(String path) {
|
||||
return getString(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code List<String>}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
public List<String> getStringList(String path) {
|
||||
return getStringList(path, null);
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code String}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code String}
|
||||
*/
|
||||
public String getString(String path, String defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code List<String>}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path of the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code List<String>}
|
||||
*/
|
||||
public List<String> getStringList(String path, List<String> defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
if (!(value instanceof List)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<String> fixedList = new ArrayList<>();
|
||||
for (Object key : (List<Object>)value) {
|
||||
fixedList.add(key.toString());
|
||||
}
|
||||
return fixedList;
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code List<String>}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
public List<String> getStringList(String path) {
|
||||
return getStringList(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Integer}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
public Integer getInt(String path) {
|
||||
return getInt(path, null);
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code List<String>}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path of the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code List<String>}
|
||||
*/
|
||||
public List<String> getStringList(String path, List<String> defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
if (!(value instanceof List)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<String> fixedList = new ArrayList<>();
|
||||
for (Object key : (List<Object>)value) {
|
||||
fixedList.add(key.toString());
|
||||
}
|
||||
return fixedList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Integer}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code Integer}
|
||||
*/
|
||||
public Integer getInt(String path, Integer defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
try{
|
||||
return Integer.parseInt(value.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code Integer}. Returns {@code null} if
|
||||
* option is not present.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from file or null if not present
|
||||
*/
|
||||
public Integer getInt(String path) {
|
||||
return getInt(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Boolean}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code Boolean}
|
||||
*/
|
||||
public boolean getBoolean(String path, boolean defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
return Boolean.parseBoolean(value.toString());
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code Integer}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code Integer}
|
||||
*/
|
||||
public Integer getInt(String path, Integer defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
try{
|
||||
return Integer.parseInt(value.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Double}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code Double}
|
||||
*/
|
||||
public Double getDouble(String path, double defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
try {
|
||||
return Double.parseDouble(value.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code Boolean}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code Boolean}
|
||||
*/
|
||||
public boolean getBoolean(String path, boolean defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
return Boolean.parseBoolean(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns config option with specified path as {@code Map<K, V>}. If the option
|
||||
* is not present or value is not a {@code Map}, new empty {@code Map} is returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from configuration file as {@code Map<K, V>}
|
||||
*/
|
||||
public <K, V> Map<K, V> getConfigurationSection(String path) {
|
||||
if (path == null || path.length() == 0) return (Map<K, V>) values;
|
||||
Object value = getObject(path, null);
|
||||
if (value == null) return new LinkedHashMap<>();
|
||||
if (value instanceof Map) {
|
||||
return (Map<K, V>) value;
|
||||
} else {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code Double}. If the option is not present
|
||||
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
|
||||
* and {@code defaultValue} returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param defaultValue
|
||||
* Value to be inserted and returned if option is not present
|
||||
* @return value from configuration file as {@code Double}
|
||||
*/
|
||||
public Double getDouble(String path, double defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
try {
|
||||
return Double.parseDouble(value.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the file has option with specified path, {@code false}
|
||||
* if not.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return {@code true} if present, {@code false} if not
|
||||
*/
|
||||
public boolean hasConfigOption(String path) {
|
||||
return getObject(path) != null;
|
||||
}
|
||||
/**
|
||||
* Returns config option with specified path as {@code Map<K, V>}. If the option
|
||||
* is not present or value is not a {@code Map}, new empty {@code Map} is returned.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return value from configuration file as {@code Map<K, V>}
|
||||
*/
|
||||
public <K, V> Map<K, V> getConfigurationSection(String path) {
|
||||
if (path == null || path.length() == 0) return (Map<K, V>) values;
|
||||
Object value = getObject(path, null);
|
||||
if (value instanceof Map) {
|
||||
return (Map<K, V>) value;
|
||||
} else {
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value to the specified path and saves the file to disk by calling {@link #save()}.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param value
|
||||
* Value to save
|
||||
*/
|
||||
public void set(String path, Object value) {
|
||||
set(values, path, value);
|
||||
save();
|
||||
}
|
||||
/**
|
||||
* Returns {@code true} if the file has option with specified path, {@code false}
|
||||
* if not.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @return {@code true} if present, {@code false} if not
|
||||
*/
|
||||
public boolean hasConfigOption(String path) {
|
||||
return getObject(path) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value to the specified map with specified path and value. This is an internal method
|
||||
* that correctly creates maps to separate sections using "{@code .}".
|
||||
*
|
||||
* @param map
|
||||
* Map to insert value to
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param value
|
||||
* Value to save
|
||||
* @return the first argument to allow chaining
|
||||
*/
|
||||
private Map<String, Object> set(Map<String, Object> map, String path, Object value) {
|
||||
if (path.contains(".")) {
|
||||
String keyWord = getRealKey(map, path.split("\\.")[0]);
|
||||
Object subMap = map.get(keyWord);
|
||||
if (!(subMap instanceof Map)) {
|
||||
subMap = new LinkedHashMap<>();
|
||||
}
|
||||
map.put(keyWord, set((Map<String, Object>) subMap, path.substring(keyWord.length()+1), value));
|
||||
} else {
|
||||
if (value == null) {
|
||||
map.remove(getRealKey(map, path));
|
||||
} else {
|
||||
map.put(path, value);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
/**
|
||||
* Sets value to the specified path and saves the file to disk by calling {@link #save()}.
|
||||
*
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param value
|
||||
* Value to save
|
||||
*/
|
||||
public void set(String path, Object value) {
|
||||
set(values, path, value);
|
||||
save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the real key name without case sensitivity from map. If not found, inserted key
|
||||
* is returned.
|
||||
*
|
||||
* @param map
|
||||
* Map to check keys of
|
||||
* @param key
|
||||
* Key to find
|
||||
* @return The real key name
|
||||
*/
|
||||
private String getRealKey(Map<?, ?> map, String key) {
|
||||
for (Object mapKey : map.keySet()) {
|
||||
if (mapKey.toString().equalsIgnoreCase(key)) return mapKey.toString();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
/**
|
||||
* Sets value to the specified map with specified path and value. This is an internal method
|
||||
* that correctly creates maps to separate sections using "{@code .}".
|
||||
*
|
||||
* @param map
|
||||
* Map to insert value to
|
||||
* @param path
|
||||
* Path to the option with sections separated with "{@code .}"
|
||||
* @param value
|
||||
* Value to save
|
||||
* @return the first argument to allow chaining
|
||||
*/
|
||||
private Map<String, Object> set(Map<String, Object> map, String path, Object value) {
|
||||
if (path.contains(".")) {
|
||||
String keyWord = getRealKey(map, path.split("\\.")[0]);
|
||||
Object subMap = map.get(keyWord);
|
||||
if (!(subMap instanceof Map)) {
|
||||
subMap = new LinkedHashMap<>();
|
||||
}
|
||||
map.put(keyWord, set((Map<String, Object>) subMap, path.substring(keyWord.length()+1), value));
|
||||
} else {
|
||||
if (value == null) {
|
||||
map.remove(getRealKey(map, path));
|
||||
} else {
|
||||
map.put(path, value);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects header of a file (first lines of file starting with #).
|
||||
*
|
||||
* @throws IOException
|
||||
* if I/O operation fails
|
||||
*/
|
||||
private void detectHeader() throws IOException {
|
||||
header = new ArrayList<>();
|
||||
for (String line : Files.readAllLines(file.toPath())) {
|
||||
if (line.startsWith("#")) {
|
||||
header.add(line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the real key name without case sensitivity from map. If not found, inserted key
|
||||
* is returned.
|
||||
*
|
||||
* @param map
|
||||
* Map to check keys of
|
||||
* @param key
|
||||
* Key to find
|
||||
* @return The real key name
|
||||
*/
|
||||
private String getRealKey(Map<?, ?> map, String key) {
|
||||
for (Object mapKey : map.keySet()) {
|
||||
if (mapKey.toString().equalsIgnoreCase(key)) return mapKey.toString();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts header back into file. This is required after calling {@link #save()}, because
|
||||
* it destroys the header.
|
||||
*
|
||||
* @throws IOException
|
||||
* if I/O operation fails
|
||||
*/
|
||||
public void fixHeader() throws IOException {
|
||||
if (header == null) return;
|
||||
List<String> content = new ArrayList<>(header);
|
||||
content.addAll(Files.readAllLines(file.toPath()));
|
||||
Files.delete(file.toPath());
|
||||
if (file.createNewFile()) {
|
||||
Files.write(file.toPath(), content);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Detects header of a file (first lines of file starting with #).
|
||||
*
|
||||
* @throws IOException
|
||||
* if I/O operation fails
|
||||
*/
|
||||
private void detectHeader() throws IOException {
|
||||
header = new ArrayList<>();
|
||||
for (String line : Files.readAllLines(file.toPath())) {
|
||||
if (line.startsWith("#")) {
|
||||
header.add(line);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
/**
|
||||
* Inserts header back into file. This is required after calling {@link #save()}, because
|
||||
* it destroys the header.
|
||||
*
|
||||
* @throws IOException
|
||||
* if I/O operation fails
|
||||
*/
|
||||
public void fixHeader() throws IOException {
|
||||
if (header == null) return;
|
||||
List<String> content = new ArrayList<>(header);
|
||||
content.addAll(Files.readAllLines(file.toPath()));
|
||||
Files.delete(file.toPath());
|
||||
if (file.createNewFile()) {
|
||||
Files.write(file.toPath(), content);
|
||||
}
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -23,65 +23,66 @@ import me.neznamy.yamlassist.YamlAssist;
|
||||
*/
|
||||
public class YamlConfigurationFile extends ConfigurationFile {
|
||||
|
||||
/** SnakeYAML instance */
|
||||
private static final Yaml yaml;
|
||||
|
||||
static {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
yaml = new Yaml(options);
|
||||
}
|
||||
/** SnakeYAML instance */
|
||||
private static final Yaml yaml;
|
||||
|
||||
/**
|
||||
* Constructs new instance and attempts to load specified configuration file.
|
||||
* If file does not exist, default file is copied from {@code source}.
|
||||
*
|
||||
* @param source
|
||||
* Source to copy file from if it does not exist
|
||||
* @param destination
|
||||
* File destination to use
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code destination} is null
|
||||
* @throws IllegalStateException
|
||||
* if file does not exist and source is null
|
||||
* @throws YAMLException
|
||||
* if file has invalid YAML syntax
|
||||
* @throws IOException
|
||||
* if I/O operation with the file unexpectedly fails
|
||||
*/
|
||||
public YamlConfigurationFile(InputStream source, File destination) throws YAMLException, IOException {
|
||||
super(source, destination);
|
||||
FileInputStream input = null;
|
||||
try {
|
||||
input = new FileInputStream(file);
|
||||
values = yaml.load(input);
|
||||
if (values == null) values = new LinkedHashMap<>();
|
||||
input.close();
|
||||
} catch (YAMLException e) {
|
||||
if (input != null) input.close();
|
||||
TabAPI tab = TabAPI.getInstance();
|
||||
tab.sendConsoleMessage("&c[TAB] File " + destination + " has broken syntax.", true);
|
||||
tab.sendConsoleMessage("&6[TAB] Error message from yaml parser: " + e.getMessage(), true);
|
||||
List<String> suggestions = YamlAssist.getSuggestions(file);
|
||||
if (!suggestions.isEmpty()) {
|
||||
tab.sendConsoleMessage("&d[TAB] Suggestions to fix yaml syntax:", true);
|
||||
for (String suggestion : suggestions) {
|
||||
tab.sendConsoleMessage("&d[TAB] - " + suggestion, true);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
static {
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
yaml = new Yaml(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
try {
|
||||
Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
|
||||
yaml.dump(values, writer);
|
||||
writer.close();
|
||||
fixHeader();
|
||||
} catch (IOException e) {
|
||||
TabAPI.getInstance().sendConsoleMessage("&c[TAB] Failed to save yaml file " + file.getPath() + " with content " + values.toString(), true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructs new instance and attempts to load specified configuration file.
|
||||
* If file does not exist, default file is copied from {@code source}.
|
||||
*
|
||||
* @param source
|
||||
* Source to copy file from if it does not exist
|
||||
* @param destination
|
||||
* File destination to use
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code destination} is null
|
||||
* @throws IllegalStateException
|
||||
* if file does not exist and source is null
|
||||
* @throws YAMLException
|
||||
* if file has invalid YAML syntax
|
||||
* @throws IOException
|
||||
* if I/O operation with the file unexpectedly fails
|
||||
*/
|
||||
public YamlConfigurationFile(InputStream source, File destination) throws YAMLException, IOException {
|
||||
super(source, destination);
|
||||
FileInputStream input = null;
|
||||
try {
|
||||
input = new FileInputStream(file);
|
||||
values = yaml.load(input);
|
||||
if (values == null) values = new LinkedHashMap<>();
|
||||
input.close();
|
||||
} catch (YAMLException e) {
|
||||
if (input != null) input.close();
|
||||
TabAPI tab = TabAPI.getInstance();
|
||||
tab.setBrokenFile(destination.getName());
|
||||
tab.sendConsoleMessage("&cFile " + destination + " has broken syntax.", true);
|
||||
tab.sendConsoleMessage("&6Error message from yaml parser: " + e.getMessage(), true);
|
||||
List<String> suggestions = YamlAssist.getSuggestions(file);
|
||||
if (!suggestions.isEmpty()) {
|
||||
tab.sendConsoleMessage("&dSuggestions to fix yaml syntax:", true);
|
||||
for (String suggestion : suggestions) {
|
||||
tab.sendConsoleMessage("&d- " + suggestion, true);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
try {
|
||||
Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
|
||||
yaml.dump(values, writer);
|
||||
writer.close();
|
||||
fixHeader();
|
||||
} catch (IOException e) {
|
||||
TabAPI.getInstance().sendConsoleMessage("&cFailed to save yaml file " + file.getPath() + " with content " + values.toString(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package me.neznamy.tab.api.event;
|
||||
|
||||
/**
|
||||
* The event bus. This is used to register event listeners/handlers for
|
||||
* handling events that may be fired by TAB.
|
||||
*/
|
||||
public interface EventBus {
|
||||
|
||||
/**
|
||||
* Scans the given listener for any method that is annotated with
|
||||
* {@link Subscribe} and registers it as a listener to this event bus.
|
||||
*
|
||||
* All methods in this class that are annotated with {@link Subscribe}
|
||||
* must meet the following criteria:
|
||||
* <ul>
|
||||
* <li>It must be <strong>public</strong>.</li>
|
||||
* <li>It must <strong>not</strong> be abstract.</li>
|
||||
* <li>It must have exactly <strong>one</strong> parameter.</li>
|
||||
* <li>Its single parameter must be <strong>an event</strong>.</li>
|
||||
* </ul>
|
||||
* If a method does not meet the above criteria, an exception <strong>will</strong>
|
||||
* be thrown.
|
||||
*
|
||||
* @param listener the listener to register
|
||||
* @throws RuntimeException if a method does not meet the criteria
|
||||
*/
|
||||
void register(Object listener);
|
||||
|
||||
/**
|
||||
* Registers the given handler to this event bus.
|
||||
*
|
||||
* @param type the class type of the event
|
||||
* @param handler the handler to register
|
||||
* @param <E> the type of the event
|
||||
*/
|
||||
<E extends TabEvent> void register(Class<E> type, EventHandler<E> handler);
|
||||
|
||||
/**
|
||||
* Scans the given listener for any method that is annotated with
|
||||
* {@link Subscribe} and unregisters it from this event bus.
|
||||
*
|
||||
* @param listener the listener to unregister
|
||||
*/
|
||||
void unregister(Object listener);
|
||||
|
||||
/**
|
||||
* Unregisters the given handler from this event bus.
|
||||
*
|
||||
* @param handler the handler to unregister
|
||||
* @param <E> the type of the event
|
||||
*/
|
||||
<E extends TabEvent> void unregister(EventHandler<E> handler);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.neznamy.tab.api.event;
|
||||
|
||||
/**
|
||||
* A handler for an event. This allows listening for events in a more
|
||||
* functional style.
|
||||
*
|
||||
* @param <E> the type of event this handler handles
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface EventHandler<E> {
|
||||
|
||||
/**
|
||||
* Called when the given event is fired from an event bus.
|
||||
*
|
||||
* @param event the event to handle
|
||||
*/
|
||||
void handle(E event);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package me.neznamy.tab.api.event;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Subscribes to a {@link TabEvent}.
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Subscribe {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package me.neznamy.tab.api.event;
|
||||
|
||||
/**
|
||||
* An event that may be fired by TAB when something occurs.
|
||||
*
|
||||
* This is a marker interface. All events <strong>must</strong> implement this
|
||||
* interface.
|
||||
*/
|
||||
public interface TabEvent {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.neznamy.tab.api.event.player;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.event.TabEvent;
|
||||
|
||||
/**
|
||||
* Called when the player has been fully loaded by TAB.
|
||||
*/
|
||||
public interface PlayerLoadEvent extends TabEvent {
|
||||
|
||||
/**
|
||||
* Gets the player that was loaded.
|
||||
*
|
||||
* @return the player that was loaded
|
||||
*/
|
||||
TabPlayer getPlayer();
|
||||
|
||||
/**
|
||||
* Returns true if player was loaded because they joined,
|
||||
* false if the player was loaded because tab reload command was executed
|
||||
*
|
||||
* @return true if player joined, false if plugin was reloaded
|
||||
*/
|
||||
boolean isJoin();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package me.neznamy.tab.api.event.plugin;
|
||||
|
||||
import me.neznamy.tab.api.event.TabEvent;
|
||||
import me.neznamy.tab.api.placeholder.Placeholder;
|
||||
|
||||
public interface PlaceholderRegisterEvent extends TabEvent {
|
||||
|
||||
String getIdentifier();
|
||||
|
||||
void setPlaceholder(Placeholder placeholder);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.neznamy.tab.api.event.plugin;
|
||||
|
||||
import me.neznamy.tab.api.event.TabEvent;
|
||||
|
||||
/**
|
||||
* Called when TAB is fully loaded.
|
||||
*/
|
||||
public interface TabLoadEvent extends TabEvent {
|
||||
}
|
||||
@@ -2,15 +2,11 @@ package me.neznamy.tab.api.placeholder;
|
||||
|
||||
public interface Placeholder {
|
||||
|
||||
int getRefresh();
|
||||
int getRefresh();
|
||||
|
||||
String getIdentifier();
|
||||
String getIdentifier();
|
||||
|
||||
boolean isTriggerMode();
|
||||
void enableTriggerMode(Runnable onActivation, Runnable onDisable);
|
||||
|
||||
void enableTriggerMode();
|
||||
|
||||
void enableTriggerMode(Runnable onActivation, Runnable onDisable);
|
||||
|
||||
void unload();
|
||||
void unload();
|
||||
}
|
||||
@@ -10,35 +10,42 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
public interface PlaceholderManager {
|
||||
|
||||
/**
|
||||
* Registers a server placeholder (placeholder with same output for all players)
|
||||
*/
|
||||
ServerPlaceholder registerServerPlaceholder(String identifier, int refresh, Supplier<Object> supplier);
|
||||
|
||||
/**
|
||||
* Registers a player placeholder (placeholder with player-specific output)
|
||||
*/
|
||||
PlayerPlaceholder registerPlayerPlaceholder(String identifier, int refresh, Function<TabPlayer, Object> function);
|
||||
/**
|
||||
* Registers a server placeholder (placeholder with same output for all players)
|
||||
*/
|
||||
ServerPlaceholder registerServerPlaceholder(String identifier, int refresh, Supplier<Object> supplier);
|
||||
|
||||
/**
|
||||
* Registers a relational placeholder (different output for each player pair)
|
||||
*/
|
||||
RelationalPlaceholder registerRelationalPlaceholder(String identifier, int refresh, BiFunction<TabPlayer, TabPlayer, Object> function);
|
||||
|
||||
/**
|
||||
* Detects placeholders in text using %% pattern and returns list of all detected identifiers
|
||||
* @param text - text to detect placeholders in
|
||||
* @return list of detected identifiers
|
||||
*/
|
||||
List<String> detectPlaceholders(String text);
|
||||
/**
|
||||
* Registers a player placeholder (placeholder with player-specific output)
|
||||
*/
|
||||
PlayerPlaceholder registerPlayerPlaceholder(String identifier, int refresh, Function<TabPlayer, Object> function);
|
||||
|
||||
/**
|
||||
* Adds placeholder to list of used placeholders and assigns this feature as using it,
|
||||
* which will then receive refresh() if values changes
|
||||
* @param identifier - placeholder identifier
|
||||
* @param feature - feature using the placeholder
|
||||
*/
|
||||
void addUsedPlaceholder(String identifier, TabFeature feature);
|
||||
|
||||
String findReplacement(String placeholder, String output);
|
||||
/**
|
||||
* Registers a relational placeholder (different output for each player pair)
|
||||
*/
|
||||
RelationalPlaceholder registerRelationalPlaceholder(String identifier, int refresh, BiFunction<TabPlayer, TabPlayer, Object> function);
|
||||
|
||||
/**
|
||||
* Detects placeholders in text using %% pattern and returns list of all detected identifiers
|
||||
*
|
||||
* @param text
|
||||
* text to detect placeholders in
|
||||
* @return list of detected identifiers
|
||||
*/
|
||||
List<String> detectPlaceholders(String text);
|
||||
|
||||
/**
|
||||
* Adds placeholder to list of used placeholders and assigns this feature as using it,
|
||||
* which will then receive refresh() if values changes
|
||||
*
|
||||
* @param identifier
|
||||
* placeholder identifier
|
||||
* @param feature
|
||||
* feature using the placeholder
|
||||
*/
|
||||
void addUsedPlaceholder(String identifier, TabFeature feature);
|
||||
|
||||
String findReplacement(String placeholder, String output);
|
||||
|
||||
List<String> getUsedPlaceholders();
|
||||
}
|
||||
@@ -4,7 +4,25 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
public interface PlayerPlaceholder extends Placeholder {
|
||||
|
||||
void updateValue(TabPlayer player, Object value);
|
||||
|
||||
Object request(TabPlayer p);
|
||||
void updateValue(TabPlayer player, Object value);
|
||||
|
||||
/**
|
||||
* Calls the placeholder request function and returns the output.
|
||||
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
|
||||
* file and "ERROR" is returned.
|
||||
*
|
||||
* @param p
|
||||
* player to get placeholder value for
|
||||
* @return value placeholder returned or "ERROR" if it threw an error
|
||||
*/
|
||||
Object request(TabPlayer p);
|
||||
|
||||
/**
|
||||
* Returns last known value for given player.
|
||||
*
|
||||
* @param player
|
||||
* player to get last value for
|
||||
* @return last known value for entered player
|
||||
*/
|
||||
String getLastValue(TabPlayer player);
|
||||
}
|
||||
@@ -4,7 +4,30 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
public interface RelationalPlaceholder extends Placeholder {
|
||||
|
||||
void updateValue(TabPlayer viewer, TabPlayer target, Object value);
|
||||
|
||||
Object request(TabPlayer viewer, TabPlayer target);
|
||||
void updateValue(TabPlayer viewer, TabPlayer target, Object value);
|
||||
|
||||
/**
|
||||
* Calls the placeholder request function and returns the output.
|
||||
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
|
||||
* file and "ERROR" is returned.
|
||||
*
|
||||
* @param viewer
|
||||
* player looking at output of the placeholder
|
||||
* @param target
|
||||
* player the placeholder is displayed on
|
||||
* @return value placeholder returned or "ERROR" if it threw an error
|
||||
*/
|
||||
Object request(TabPlayer viewer, TabPlayer target);
|
||||
|
||||
/**
|
||||
* Returns last known value for given players. First player is viewer,
|
||||
* second player is target.
|
||||
*
|
||||
* @param viewer
|
||||
* viewer of the placeholder
|
||||
* @param target
|
||||
* target who is the text displayed on
|
||||
* @return last known value for entered player duo
|
||||
*/
|
||||
String getLastValue(TabPlayer viewer, TabPlayer target);
|
||||
}
|
||||
@@ -2,7 +2,21 @@ package me.neznamy.tab.api.placeholder;
|
||||
|
||||
public interface ServerPlaceholder extends Placeholder {
|
||||
|
||||
void updateValue(Object value);
|
||||
|
||||
Object request();
|
||||
void updateValue(Object value);
|
||||
|
||||
/**
|
||||
* Calls the placeholder request function and returns the output.
|
||||
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
|
||||
* file and "ERROR" is returned.
|
||||
*
|
||||
* @return value placeholder returned or "ERROR" if it threw an error
|
||||
*/
|
||||
Object request();
|
||||
|
||||
/**
|
||||
* Returns last known value of the placeholder.
|
||||
*
|
||||
* @return last known value
|
||||
*/
|
||||
String getLastValue();
|
||||
}
|
||||
@@ -8,233 +8,262 @@ 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;
|
||||
|
||||
/**
|
||||
* An interface representing a platform-specific packet builder
|
||||
* A class for packet building, methods are overridden in
|
||||
* platform-specific builders which extend this class to build actual packets.
|
||||
* Packets which don't have a method override are forwarded into
|
||||
* sendPacket method, where they are handled using platform's API instead.
|
||||
*/
|
||||
public abstract class PacketBuilder {
|
||||
public class PacketBuilder {
|
||||
|
||||
/** Function map turning custom packet class into platform-specific packets */
|
||||
protected final Map<Class<? extends TabPacket>, BiFunctionWithException<TabPacket, ProtocolVersion, Object>> buildMap = new HashMap<>();
|
||||
/** Function map turning custom packet class into platform-specific packets */
|
||||
protected final Map<Class<? extends TabPacket>, BiFunctionWithException<TabPacket, ProtocolVersion, Object>> buildMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance and fills {@link #buildMap} with abstract build methods
|
||||
* of packets present on all platforms.
|
||||
*/
|
||||
protected PacketBuilder() {
|
||||
buildMap.put(PacketPlayOutBoss.class, (packet, version) -> build((PacketPlayOutBoss)packet, version));
|
||||
buildMap.put(PacketPlayOutChat.class, (packet, version) -> build((PacketPlayOutChat)packet, version));
|
||||
buildMap.put(PacketPlayOutPlayerInfo.class, (packet, version) -> build((PacketPlayOutPlayerInfo)packet, version));
|
||||
buildMap.put(PacketPlayOutPlayerListHeaderFooter.class, (packet, version) -> build((PacketPlayOutPlayerListHeaderFooter)packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardDisplayObjective.class, (packet, version) -> build((PacketPlayOutScoreboardDisplayObjective)packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardObjective.class, (packet, version) -> build((PacketPlayOutScoreboardObjective)packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardScore.class, (packet, version) -> build((PacketPlayOutScoreboardScore)packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardTeam.class, (packet, version) -> build((PacketPlayOutScoreboardTeam)packet, version));
|
||||
}
|
||||
/**
|
||||
* Constructs new instance and fills {@link #buildMap} with abstract build methods
|
||||
* of packets present on all platforms.
|
||||
*/
|
||||
public PacketBuilder() {
|
||||
buildMap.put(PacketPlayOutBoss.class, (packet, version) -> build((PacketPlayOutBoss)packet, version));
|
||||
buildMap.put(PacketPlayOutChat.class, (packet, version) -> build((PacketPlayOutChat)packet, version));
|
||||
buildMap.put(PacketPlayOutPlayerInfo.class, (packet, version) -> build((PacketPlayOutPlayerInfo)packet, version));
|
||||
buildMap.put(PacketPlayOutPlayerListHeaderFooter.class, (packet, version) -> build((PacketPlayOutPlayerListHeaderFooter)packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardDisplayObjective.class, (packet, version) -> build((PacketPlayOutScoreboardDisplayObjective)packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardObjective.class, (packet, version) -> build((PacketPlayOutScoreboardObjective)packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardScore.class, (packet, version) -> build((PacketPlayOutScoreboardScore)packet, version));
|
||||
buildMap.put(PacketPlayOutScoreboardTeam.class, (packet, version) -> build((PacketPlayOutScoreboardTeam)packet, version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts custom packet into platform-specific packet by calling a function from
|
||||
* {@link #buildMap}.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to build
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if reflection fails
|
||||
*/
|
||||
public Object build(TabPacket packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return buildMap.get(packet.getClass()).apply(packet, clientVersion);
|
||||
}
|
||||
/**
|
||||
* Converts custom packet into platform-specific packet by calling a function from
|
||||
* {@link #buildMap}.
|
||||
*
|
||||
* @param packet
|
||||
* Packet to build
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if reflection fails
|
||||
*/
|
||||
public Object build(TabPacket packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
Preconditions.checkNotNull(packet, "packet");
|
||||
Preconditions.checkNotNull(clientVersion, "clientVersion");
|
||||
return buildMap.get(packet.getClass()).apply(packet, clientVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutBoss class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract Object build(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutBoss class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutChat class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutChat class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutPlayerInfo class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutPlayerInfo class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutPlayerListHeaderFooter class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract Object build(PacketPlayOutPlayerListHeaderFooter packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutPlayerListHeaderFooter class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutPlayerListHeaderFooter packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutScoreboardDisplayObjective class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract Object build(PacketPlayOutScoreboardDisplayObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutScoreboardDisplayObjective class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutScoreboardDisplayObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutScoreboardObjective class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract Object build(PacketPlayOutScoreboardObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutScoreboardObjective class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutScoreboardObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutScoreboardScore class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutScoreboardScore class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutScoreboardTeam class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Constructs platform-specific PacketPlayOutScoreboardTeam class based on custom packet class
|
||||
*
|
||||
* @param packet
|
||||
* Custom packet to be built
|
||||
* @param clientVersion
|
||||
* Protocol version of player to build the packet for
|
||||
* @return Platform-specific packet
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cuts given string to specified character length (or length-1 if last character is a color character)
|
||||
* and translates RGB to legacy colors. If string is not that long, the original string is returned.
|
||||
* RGB codes are converted into legacy, since cutting is only needed for <1.13.
|
||||
* If {@code string} is {@code null}, empty string is returned.
|
||||
* @param string
|
||||
* String to cut
|
||||
* @param length
|
||||
* Length to cut to
|
||||
* @return string cut to {@code length} characters
|
||||
*/
|
||||
public String cutTo(String string, int length) {
|
||||
if (string == null) return "";
|
||||
String legacyText = string;
|
||||
if (string.contains("#")) {
|
||||
//converting RGB to legacy colors
|
||||
legacyText = RGBUtils.getInstance().convertRGBtoLegacy(string);
|
||||
}
|
||||
if (legacyText.length() <= length) return legacyText;
|
||||
if (legacyText.charAt(length-1) == EnumChatFormat.COLOR_CHAR) {
|
||||
return legacyText.substring(0, length-1); //cutting one extra character to prevent prefix ending with "&"
|
||||
} else {
|
||||
return legacyText.substring(0, length);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Cuts given string to specified character length (or length-1 if last character is a color character)
|
||||
* and translates RGB to legacy colors. If string is not that long, the original string is returned.
|
||||
* RGB codes are converted into legacy, since cutting is only needed for <1.13.
|
||||
* If {@code string} is {@code null}, empty string is returned.
|
||||
*
|
||||
* @param string
|
||||
* String to cut
|
||||
* @param length
|
||||
* Length to cut to
|
||||
* @return string cut to {@code length} characters
|
||||
*/
|
||||
public String cutTo(String string, int length) {
|
||||
if (string == null) return "";
|
||||
String legacyText = string;
|
||||
if (string.contains("#")) {
|
||||
//converting RGB to legacy colors
|
||||
legacyText = RGBUtils.getInstance().convertRGBtoLegacy(string);
|
||||
}
|
||||
if (legacyText.length() <= length) return legacyText;
|
||||
if (legacyText.charAt(length-1) == EnumChatFormat.COLOR_CHAR) {
|
||||
return legacyText.substring(0, length-1); //cutting one extra character to prevent prefix ending with "&"
|
||||
} else {
|
||||
return legacyText.substring(0, length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* needed and returned.
|
||||
*
|
||||
* @param text
|
||||
* Text to convert
|
||||
* @param clientVersion
|
||||
* Version of player to convert text for
|
||||
* @return serialized component for 1.13+ clients, cut string for 1.12-
|
||||
*/
|
||||
public String jsonOrCut(String text, ProtocolVersion clientVersion, int length) {
|
||||
if (text == null) return null;
|
||||
if (clientVersion.getMinorVersion() >= 13) {
|
||||
return IChatBaseComponent.optimizedComponent(text).toString(clientVersion);
|
||||
} else {
|
||||
return cutTo(text, length);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
* needed and returned.
|
||||
*
|
||||
* @param text
|
||||
* Text to convert
|
||||
* @param clientVersion
|
||||
* Version of player to convert text for
|
||||
* @return serialized component for 1.13+ clients, cut string for 1.12-
|
||||
*/
|
||||
public String jsonOrCut(String text, ProtocolVersion clientVersion, int length) {
|
||||
if (text == null) return null;
|
||||
if (clientVersion.getMinorVersion() >= 13) {
|
||||
return IChatBaseComponent.optimizedComponent(text).toString(clientVersion);
|
||||
} else {
|
||||
return cutTo(text, length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts platform-specific instance of player info packet into
|
||||
* {@link PacketPlayOutPlayerInfo} object.
|
||||
*
|
||||
* @param packet
|
||||
* platform-specific info packet
|
||||
* @param clientVersion
|
||||
* Version of client receiving the packet
|
||||
* @return The packet converted into {@link PacketPlayOutPlayerInfo}
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract PacketPlayOutPlayerInfo readPlayerInfo(Object packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Converts platform-specific instance of player info packet into
|
||||
* {@link PacketPlayOutPlayerInfo} object.
|
||||
*
|
||||
* @param packet
|
||||
* platform-specific info packet
|
||||
* @param clientVersion
|
||||
* Version of client receiving the packet
|
||||
* @return The packet converted into {@link PacketPlayOutPlayerInfo}
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo readPlayerInfo(Object packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts platform-specific instance of objective packet into
|
||||
* {@link PacketPlayOutScoreboardObjective} object.
|
||||
*
|
||||
* @param packet
|
||||
* platform-specific objective packet
|
||||
* @return The packet converted into {@link PacketPlayOutScoreboardObjective}
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract PacketPlayOutScoreboardObjective readObjective(Object packet) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Converts platform-specific instance of objective packet into
|
||||
* {@link PacketPlayOutScoreboardObjective} object.
|
||||
*
|
||||
* @param packet
|
||||
* platform-specific objective packet
|
||||
* @return The packet converted into {@link PacketPlayOutScoreboardObjective}
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public PacketPlayOutScoreboardObjective readObjective(Object packet) throws ReflectiveOperationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts platform-specific instance of display objective packet into
|
||||
* {@link PacketPlayOutScoreboardDisplayObjective} object.
|
||||
*
|
||||
* @param packet
|
||||
* platform-specific display objective packet
|
||||
* @return The packet converted into {@link PacketPlayOutScoreboardDisplayObjective}
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public abstract PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object packet) throws ReflectiveOperationException;
|
||||
/**
|
||||
* Converts platform-specific instance of display objective packet into
|
||||
* {@link PacketPlayOutScoreboardDisplayObjective} object.
|
||||
*
|
||||
* @param packet
|
||||
* platform-specific display objective packet
|
||||
* @return The packet converted into {@link PacketPlayOutScoreboardDisplayObjective}
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object packet) throws ReflectiveOperationException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -4,277 +4,299 @@ import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.bossbar.BarColor;
|
||||
import me.neznamy.tab.api.bossbar.BarStyle;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/** UUID of the BossBar */
|
||||
private final UUID id;
|
||||
/** UUID of the BossBar */
|
||||
private final UUID id;
|
||||
|
||||
/** Action of this packet */
|
||||
private final Action operation;
|
||||
/** Action of this packet */
|
||||
private final Action action;
|
||||
|
||||
/** BossBar title */
|
||||
private String name;
|
||||
/** BossBar title */
|
||||
private String name;
|
||||
|
||||
/** BossBar progress (0-1)*/
|
||||
private float pct;
|
||||
/** BossBar progress (0-1)*/
|
||||
private float pct;
|
||||
|
||||
/** BossBar color */
|
||||
private BarColor color;
|
||||
/** BossBar color */
|
||||
private BarColor color;
|
||||
|
||||
/** BossBar style */
|
||||
private BarStyle overlay;
|
||||
/** BossBar style */
|
||||
private BarStyle overlay;
|
||||
|
||||
/** Darken screen flag */
|
||||
private boolean darkenScreen;
|
||||
/** Darken screen flag */
|
||||
private boolean darkenScreen;
|
||||
|
||||
/** Play music flag */
|
||||
private boolean playMusic;
|
||||
/** Play music flag */
|
||||
private boolean playMusic;
|
||||
|
||||
/** Create fog flag */
|
||||
private boolean createWorldFog;
|
||||
/** Create fog flag */
|
||||
private boolean createWorldFog;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.ADD action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param name
|
||||
* BossBar title
|
||||
* @param pct
|
||||
* BossBar progress
|
||||
* @param color
|
||||
* BossBar color
|
||||
* @param overlay
|
||||
* BossBar style
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, String name, float pct, BarColor color, BarStyle overlay) {
|
||||
this.operation = Action.ADD;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.pct = pct;
|
||||
this.color = color;
|
||||
this.overlay = overlay;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.ADD action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param name
|
||||
* BossBar title
|
||||
* @param pct
|
||||
* BossBar progress
|
||||
* @param color
|
||||
* BossBar color
|
||||
* @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");
|
||||
this.action = Action.ADD;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.pct = pct;
|
||||
this.color = color;
|
||||
this.overlay = overlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.REMOVE action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id) {
|
||||
this.operation = Action.REMOVE;
|
||||
this.id = id;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.REMOVE action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
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
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param pct
|
||||
* BossBar progress
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, float pct) {
|
||||
this.operation = Action.UPDATE_PCT;
|
||||
this.id = id;
|
||||
this.pct = pct;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@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");
|
||||
this.action = Action.UPDATE_PCT;
|
||||
this.id = id;
|
||||
this.pct = pct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@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) {
|
||||
this.operation = Action.UPDATE_NAME;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@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");
|
||||
this.action = Action.UPDATE_NAME;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_STYLE action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param color
|
||||
* BossBar color
|
||||
* @param overlay
|
||||
* BossBar style
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, BarColor color, BarStyle overlay) {
|
||||
this.operation = Action.UPDATE_STYLE;
|
||||
this.id = id;
|
||||
this.color = color;
|
||||
this.overlay = overlay;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_STYLE action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param color
|
||||
* BossBar color
|
||||
* @param overlay
|
||||
* BossBar style
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, BarColor color, BarStyle overlay) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
Preconditions.checkNotNull(color, "color");
|
||||
Preconditions.checkNotNull(overlay, "style");
|
||||
this.action = Action.UPDATE_STYLE;
|
||||
this.id = id;
|
||||
this.color = color;
|
||||
this.overlay = overlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PROPERTIES action
|
||||
*
|
||||
* @param darkenScreen
|
||||
* Darken screen flag
|
||||
* @param playMusic
|
||||
* Play music flag
|
||||
* @param createWorldFog
|
||||
* Create fog flag
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
|
||||
this.operation = Action.UPDATE_PROPERTIES;
|
||||
this.id = id;
|
||||
this.darkenScreen = darkenScreen;
|
||||
this.playMusic = playMusic;
|
||||
this.createWorldFog = createWorldFog;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PROPERTIES action
|
||||
*
|
||||
* @param darkenScreen
|
||||
* Darken screen flag
|
||||
* @param playMusic
|
||||
* Play music flag
|
||||
* @param createWorldFog
|
||||
* Create fog flag
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
this.action = Action.UPDATE_PROPERTIES;
|
||||
this.id = id;
|
||||
this.darkenScreen = darkenScreen;
|
||||
this.playMusic = playMusic;
|
||||
this.createWorldFog = createWorldFog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns bitmask based on {@link #darkenScreen}, {@link #playMusic} and {@link #darkenScreen} values.
|
||||
* <p>
|
||||
* {@link #darkenScreen} adds {@code 1}, {@link #playMusic} {@code 2} and {@link #darkenScreen} {@code 4}
|
||||
* to the final value.
|
||||
*
|
||||
* @return the bitmask
|
||||
*/
|
||||
public byte getFlags(){
|
||||
byte value = 0;
|
||||
if (darkenScreen) value += 1;
|
||||
if (playMusic) value += 2;
|
||||
if (createWorldFog) value += 4;
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Returns bitmask based on {@link #darkenScreen}, {@link #playMusic} and {@link #darkenScreen} values.
|
||||
* <p>
|
||||
* {@link #darkenScreen} adds {@code 1}, {@link #playMusic} {@code 2} and {@link #darkenScreen} {@code 4}
|
||||
* to the final value.
|
||||
*
|
||||
* @return the bitmask
|
||||
*/
|
||||
public byte getFlags(){
|
||||
byte value = 0;
|
||||
if (darkenScreen) value += 1;
|
||||
if (playMusic) value += 2;
|
||||
if (createWorldFog) value += 4;
|
||||
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, operation, name, pct, color, overlay, darkenScreen, playMusic, createWorldFog);
|
||||
}
|
||||
@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 #color}
|
||||
*
|
||||
* @return color
|
||||
*/
|
||||
public BarColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #overlay}
|
||||
* @return style
|
||||
*/
|
||||
public BarStyle getOverlay() {
|
||||
return overlay;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #overlay}
|
||||
*
|
||||
* @return style
|
||||
*/
|
||||
public BarStyle getOverlay() {
|
||||
return overlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #name}
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #name}
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #id}
|
||||
* @return id
|
||||
*/
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #id}
|
||||
*
|
||||
* @return id
|
||||
*/
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #pct}
|
||||
* @return progress
|
||||
*/
|
||||
public float getPct() {
|
||||
return pct;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #pct}
|
||||
*
|
||||
* @return progress
|
||||
*/
|
||||
public float getPct() {
|
||||
return pct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #operation}
|
||||
* @return packet action
|
||||
*/
|
||||
public Action getOperation() {
|
||||
return operation;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #darkenScreen}
|
||||
* @return darkenScreen
|
||||
*/
|
||||
public boolean isDarkenScreen() {
|
||||
return darkenScreen;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #darkenScreen}
|
||||
*
|
||||
* @return darkenScreen
|
||||
*/
|
||||
public boolean isDarkenScreen() {
|
||||
return darkenScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #createWorldFog}
|
||||
* @return createWorldFog
|
||||
*/
|
||||
public boolean isCreateWorldFog() {
|
||||
return createWorldFog;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #createWorldFog}
|
||||
*
|
||||
* @return createWorldFog
|
||||
*/
|
||||
public boolean isCreateWorldFog() {
|
||||
return createWorldFog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playMusic}
|
||||
* @return playMusic
|
||||
*/
|
||||
public boolean isPlayMusic() {
|
||||
return playMusic;
|
||||
}
|
||||
/**
|
||||
* 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 #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 #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;
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public enum Action {
|
||||
/**
|
||||
* An enum representing all valid boss packet actions.
|
||||
* Calling ordinal() will return action's network ID.
|
||||
*/
|
||||
public enum Action {
|
||||
|
||||
ADD,
|
||||
REMOVE,
|
||||
UPDATE_PCT,
|
||||
UPDATE_NAME,
|
||||
UPDATE_STYLE,
|
||||
UPDATE_PROPERTIES
|
||||
}
|
||||
ADD,
|
||||
REMOVE,
|
||||
UPDATE_PCT,
|
||||
UPDATE_NAME,
|
||||
UPDATE_STYLE,
|
||||
UPDATE_PROPERTIES
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,65 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
public class PacketPlayOutChat implements TabPacket {
|
||||
|
||||
/** Message to be sent */
|
||||
private final IChatBaseComponent message;
|
||||
/** Message to be sent */
|
||||
private final IChatBaseComponent message;
|
||||
|
||||
/** Message position */
|
||||
private final ChatMessageType type;
|
||||
/** 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) {
|
||||
this.message = message;
|
||||
this.type = 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);
|
||||
}
|
||||
@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 #message}
|
||||
*
|
||||
* @return message
|
||||
*/
|
||||
public IChatBaseComponent getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #type}
|
||||
* @return type
|
||||
*/
|
||||
public ChatMessageType getType() {
|
||||
return type;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #type}
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public ChatMessageType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing positions of a chat message
|
||||
* Calling ordinal() will return type's network ID.
|
||||
*/
|
||||
public enum ChatMessageType {
|
||||
/**
|
||||
* An enum representing positions of a chat message
|
||||
* Calling ordinal() will return type's network ID.
|
||||
*/
|
||||
public enum ChatMessageType {
|
||||
|
||||
CHAT,
|
||||
SYSTEM,
|
||||
GAME_INFO
|
||||
}
|
||||
CHAT,
|
||||
SYSTEM,
|
||||
GAME_INFO
|
||||
}
|
||||
}
|
||||
@@ -5,301 +5,327 @@ 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
|
||||
*/
|
||||
public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
/** Packet action */
|
||||
private final EnumPlayerInfoAction action;
|
||||
/** Packet action */
|
||||
private final EnumPlayerInfoAction action;
|
||||
|
||||
/** List of affected entries */
|
||||
private final List<PlayerInfoData> entries;
|
||||
/** List of affected entries */
|
||||
private final List<PlayerInfoData> entries;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param action
|
||||
* Packet action
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, PlayerInfoData... entries) {
|
||||
this.action = action;
|
||||
this.entries = Arrays.asList(entries);
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param action
|
||||
* Packet action
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, PlayerInfoData... entries) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
this.action = action;
|
||||
this.entries = Arrays.asList(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param action
|
||||
* Packet action
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
|
||||
this.action = action;
|
||||
this.entries = entries;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param action
|
||||
* Packet action
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
this.action = action;
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutPlayerInfo{action=%s,entries=%s}", action, entries);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutPlayerInfo{action=%s,entries=%s}", action, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
* @return packet action
|
||||
*/
|
||||
public EnumPlayerInfoAction getAction() {
|
||||
return action;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public EnumPlayerInfoAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entries}
|
||||
* @return affected entries
|
||||
*/
|
||||
public List<PlayerInfoData> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #entries}
|
||||
*
|
||||
* @return affected entries
|
||||
*/
|
||||
public List<PlayerInfoData> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass representing player list entry
|
||||
*/
|
||||
public static class PlayerInfoData {
|
||||
/**
|
||||
* A subclass representing player list entry
|
||||
*/
|
||||
public static class PlayerInfoData {
|
||||
|
||||
/** Latency */
|
||||
private int latency;
|
||||
/** Latency */
|
||||
private int latency;
|
||||
|
||||
/** GameMode */
|
||||
private EnumGamemode gameMode = EnumGamemode.SURVIVAL; //ProtocolLib causes NPE even when action does not use GameMode
|
||||
/** GameMode */
|
||||
private EnumGamemode gameMode = EnumGamemode.SURVIVAL; //ProtocolLib causes NPE even when action does not use GameMode
|
||||
|
||||
/**
|
||||
* Display name displayed in TabList. Using {@code null} results in no display name
|
||||
* and scoreboard team prefix/suffix being visible in TabList instead.
|
||||
*/
|
||||
private IChatBaseComponent displayName;
|
||||
/**
|
||||
* Display name displayed in TabList. Using {@code null} results in no display name
|
||||
* and scoreboard team prefix/suffix being visible in TabList instead.
|
||||
*/
|
||||
private IChatBaseComponent displayName;
|
||||
|
||||
/** Real name of affected player */
|
||||
private String name;
|
||||
/** Real name of affected player */
|
||||
private String name;
|
||||
|
||||
/** Player UUID */
|
||||
private UUID uniqueId;
|
||||
/** Player UUID */
|
||||
private UUID uniqueId;
|
||||
|
||||
/** platform-specific skin object */
|
||||
private Object skin;
|
||||
/** Player's skin, null for empty skin */
|
||||
private Skin skin;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public PlayerInfoData(String name, UUID uniqueId, Object skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName) {
|
||||
this.name = name;
|
||||
this.uniqueId = uniqueId;
|
||||
this.skin = skin;
|
||||
this.latency = latency;
|
||||
this.gameMode = gameMode;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public PlayerInfoData(String name, UUID uniqueId, Skin skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
this.name = name;
|
||||
this.uniqueId = uniqueId;
|
||||
this.skin = skin;
|
||||
this.latency = latency;
|
||||
this.gameMode = gameMode;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.UPDATE_GAME_MODE action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
* @param gameMode
|
||||
* Player's GameMode
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, EnumGamemode gameMode) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.gameMode = gameMode;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@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");
|
||||
this.uniqueId = uniqueId;
|
||||
this.gameMode = gameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.UPDATE_LATENCY action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
* @param latency
|
||||
* Player's ping
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, int latency) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.latency = latency;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@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");
|
||||
this.uniqueId = uniqueId;
|
||||
this.latency = latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.UPDATE_DISPLAY_NAME action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
* @param displayName
|
||||
* Player's display name
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, IChatBaseComponent displayName) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@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");
|
||||
this.uniqueId = uniqueId;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.REMOVE_PLAYER action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameter. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.REMOVE_PLAYER action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
*/
|
||||
public PlayerInfoData(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}",
|
||||
latency, gameMode, displayName, name, uniqueId, skin);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PlayerInfoData{latency=%s,gameMode=%s,displayName=%s,name=%s,uniqueId=%s,skin=%s}",
|
||||
latency, gameMode, displayName, name, uniqueId, skin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #latency}
|
||||
* @return latency
|
||||
*/
|
||||
public int getLatency() {
|
||||
return latency;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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 Object getSkin() {
|
||||
return skin;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #skin}
|
||||
*
|
||||
* @return skin
|
||||
*/
|
||||
public Skin getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #skin} to specified value
|
||||
* @param skin
|
||||
* Skin to use
|
||||
*/
|
||||
public void setSkin(Object skin) {
|
||||
this.skin = skin;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sets {@link #skin} to specified value
|
||||
*
|
||||
* @param skin
|
||||
* Skin to use
|
||||
*/
|
||||
public void setSkin(Skin skin) {
|
||||
this.skin = skin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* En enum representing packet action
|
||||
* Calling ordinal() will return action's network ID.
|
||||
*/
|
||||
public enum EnumPlayerInfoAction {
|
||||
/**
|
||||
* En enum representing packet action
|
||||
* Calling ordinal() will return action's network ID.
|
||||
*/
|
||||
public enum EnumPlayerInfoAction {
|
||||
|
||||
ADD_PLAYER,
|
||||
UPDATE_GAME_MODE,
|
||||
UPDATE_LATENCY,
|
||||
UPDATE_DISPLAY_NAME,
|
||||
REMOVE_PLAYER
|
||||
}
|
||||
ADD_PLAYER,
|
||||
UPDATE_GAME_MODE,
|
||||
UPDATE_LATENCY,
|
||||
UPDATE_DISPLAY_NAME,
|
||||
REMOVE_PLAYER
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing GameMode
|
||||
*/
|
||||
public enum EnumGamemode {
|
||||
/**
|
||||
* An enum representing GameMode
|
||||
*/
|
||||
public enum EnumGamemode {
|
||||
|
||||
NOT_SET,
|
||||
SURVIVAL,
|
||||
CREATIVE,
|
||||
ADVENTURE,
|
||||
SPECTATOR
|
||||
}
|
||||
NOT_SET,
|
||||
SURVIVAL,
|
||||
CREATIVE,
|
||||
ADVENTURE,
|
||||
SPECTATOR;
|
||||
|
||||
/** Value array to iterate over to avoid array creations on each call */
|
||||
public static final EnumGamemode[] VALUES = values();
|
||||
}
|
||||
}
|
||||
+54
-47
@@ -1,63 +1,70 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
|
||||
|
||||
/** TabList header */
|
||||
private final IChatBaseComponent header;
|
||||
/** TabList header */
|
||||
private final IChatBaseComponent header;
|
||||
|
||||
/** TabList footer */
|
||||
private final IChatBaseComponent footer;
|
||||
/** TabList footer */
|
||||
private final IChatBaseComponent footer;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. They are converted to {@link IChatBaseComponent}
|
||||
* using {@link IChatBaseComponent#optimizedComponent(String)} method.
|
||||
*
|
||||
* @param header
|
||||
* TabList header
|
||||
* @param footer
|
||||
* TabList footer
|
||||
*/
|
||||
public PacketPlayOutPlayerListHeaderFooter(String header, String footer) {
|
||||
this.header = IChatBaseComponent.optimizedComponent(header);
|
||||
this.footer = IChatBaseComponent.optimizedComponent(footer);
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters. They are converted to {@link IChatBaseComponent}
|
||||
* using {@link IChatBaseComponent#optimizedComponent(String)} method.
|
||||
*
|
||||
* @param header
|
||||
* TabList header
|
||||
* @param footer
|
||||
* TabList footer
|
||||
*/
|
||||
public PacketPlayOutPlayerListHeaderFooter(String header, String footer) {
|
||||
Preconditions.checkNotNull(header, "header");
|
||||
Preconditions.checkNotNull(footer, "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) {
|
||||
this.header = header;
|
||||
this.footer = 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);
|
||||
}
|
||||
@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 #header}
|
||||
*
|
||||
* @return header
|
||||
*/
|
||||
public IChatBaseComponent getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #footer}
|
||||
* @return footer
|
||||
*/
|
||||
public IChatBaseComponent getFooter() {
|
||||
return footer;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #footer}
|
||||
*
|
||||
* @return footer
|
||||
*/
|
||||
public IChatBaseComponent getFooter() {
|
||||
return footer;
|
||||
}
|
||||
}
|
||||
+48
-43
@@ -1,55 +1,60 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
|
||||
|
||||
/**
|
||||
* Display slot.
|
||||
* 0 = PlayerList,
|
||||
* 1 = SideBar,
|
||||
* 2 = BelowName.
|
||||
*/
|
||||
private final int slot;
|
||||
|
||||
/** Up to 16 characters long objective name */
|
||||
private final String objectiveName;
|
||||
/**
|
||||
* Display slot.
|
||||
* 0 = PlayerList,
|
||||
* 1 = SideBar,
|
||||
* 2 = BelowName.
|
||||
*/
|
||||
private final int slot;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (objectiveName == null) throw new IllegalArgumentException("objectiveName cannot be null");
|
||||
if (objectiveName.length() > 16) throw new IllegalArgumentException("objectiveName cannot be longer than 16 character (is " + objectiveName.length() + ")");
|
||||
this.slot = slot;
|
||||
this.objectiveName = objectiveName;
|
||||
}
|
||||
/** Up to 16 characters long objective name */
|
||||
private final String objectiveName;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardDisplayObjective{slot=%s,objectiveName=%s}", slot, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #slot}
|
||||
* @return slot
|
||||
*/
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardDisplayObjective{slot=%s,objectiveName=%s}", slot, objectiveName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
* @return objectiveName
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #slot}
|
||||
*
|
||||
* @return slot
|
||||
*/
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objectiveName
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
}
|
||||
+99
-89
@@ -1,109 +1,119 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
|
||||
/** Up to 16 characters long objective name */
|
||||
private final String objectiveName;
|
||||
/** Up to 16 characters long objective name */
|
||||
private final String objectiveName;
|
||||
|
||||
/** Display name */
|
||||
private final String displayName;
|
||||
/** Display name */
|
||||
private final String displayName;
|
||||
|
||||
/** Display type, only takes effect in PlayerList */
|
||||
private final EnumScoreboardHealthDisplay renderType;
|
||||
/** Display type, only takes effect in PlayerList */
|
||||
private final EnumScoreboardHealthDisplay renderType;
|
||||
|
||||
/**
|
||||
* Packet action.
|
||||
* 0 = register,
|
||||
* 1 = unregister,
|
||||
* 2 = update title
|
||||
*/
|
||||
private final int method;
|
||||
/**
|
||||
* Packet action.
|
||||
* 0 = register,
|
||||
* 1 = unregister,
|
||||
* 2 = update title
|
||||
*/
|
||||
private final int action;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param method
|
||||
* 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 method, String objectiveName, String displayName, EnumScoreboardHealthDisplay renderType) {
|
||||
if (objectiveName == null) throw new IllegalArgumentException("objectiveName cannot be null");
|
||||
if (objectiveName.length() > 16) throw new IllegalArgumentException("objectiveName cannot be longer than 16 character (is " + objectiveName.length() + ")");
|
||||
this.objectiveName = objectiveName;
|
||||
this.displayName = displayName;
|
||||
this.renderType = renderType;
|
||||
this.method = method;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new packet with given objective name and 1 (unregister) action.
|
||||
*
|
||||
* @param objectiveName
|
||||
* objective name, up to 16 characters long
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardObjective(String objectiveName) {
|
||||
if (objectiveName == null) throw new IllegalArgumentException("objectiveName cannot be null");
|
||||
if (objectiveName.length() > 16) throw new IllegalArgumentException("objectiveName cannot be longer than 16 character (is " + objectiveName.length() + ")");
|
||||
this.objectiveName = objectiveName;
|
||||
this.displayName = ""; //avoiding NPE on <1.7
|
||||
this.method = 1;
|
||||
this.renderType = null;
|
||||
}
|
||||
/**
|
||||
* Constructs new packet with given objective name and 1 (unregister) action.
|
||||
*
|
||||
* @param objectiveName
|
||||
* objective name, up to 16 characters long
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardObjective(String objectiveName) {
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
this.objectiveName = objectiveName;
|
||||
this.displayName = ""; //avoiding NPE on <1.7
|
||||
this.action = 1;
|
||||
this.renderType = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardObjective{objectiveName=%s,displayName=%s,renderType=%s,method=%s}",
|
||||
objectiveName, displayName, renderType, method);
|
||||
}
|
||||
@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 #displayName}
|
||||
*
|
||||
* @return displayName
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
* @return objectiveName
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objectiveName
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #renderType}
|
||||
* @return renderType
|
||||
*/
|
||||
public EnumScoreboardHealthDisplay getRenderType() {
|
||||
return renderType;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #renderType}
|
||||
*
|
||||
* @return renderType
|
||||
*/
|
||||
public EnumScoreboardHealthDisplay getRenderType() {
|
||||
return renderType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #method}
|
||||
* @return packet action
|
||||
*/
|
||||
public int getMethod() {
|
||||
return method;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public int getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing available display types.
|
||||
* They only take effect in PlayerList position.
|
||||
*/
|
||||
public enum EnumScoreboardHealthDisplay {
|
||||
/**
|
||||
* An enum representing available display types.
|
||||
* They only take effect in PlayerList position.
|
||||
*/
|
||||
public enum EnumScoreboardHealthDisplay {
|
||||
|
||||
INTEGER, HEARTS
|
||||
}
|
||||
INTEGER, HEARTS
|
||||
}
|
||||
}
|
||||
@@ -1,89 +1,97 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
public class PacketPlayOutScoreboardScore implements TabPacket {
|
||||
|
||||
/** Packet action */
|
||||
private final Action action;
|
||||
/** Packet action */
|
||||
private final Action action;
|
||||
|
||||
/** Objective name */
|
||||
private final String objectiveName;
|
||||
/** Objective name */
|
||||
private final String objectiveName;
|
||||
|
||||
/** Affected player */
|
||||
private final String player;
|
||||
/** Affected player */
|
||||
private final String player;
|
||||
|
||||
/** Player's score */
|
||||
private final int score;
|
||||
/** 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) {
|
||||
if (objectiveName == null) throw new IllegalArgumentException("objectiveName cannot be null");
|
||||
if (objectiveName.length() > 16) throw new IllegalArgumentException("objectiveName cannot be longer than 16 character (is " + objectiveName.length() + ")");
|
||||
this.action = action;
|
||||
this.objectiveName = objectiveName;
|
||||
this.player = player;
|
||||
this.score = 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);
|
||||
}
|
||||
@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 #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
* @return objective name
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objective name
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #player}
|
||||
* @return player
|
||||
*/
|
||||
public String getPlayer() {
|
||||
return player;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #player}
|
||||
*
|
||||
* @return player
|
||||
*/
|
||||
public String getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #score}
|
||||
* @return score
|
||||
*/
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #score}
|
||||
*
|
||||
* @return score
|
||||
*/
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing action types
|
||||
*/
|
||||
public enum Action {
|
||||
/**
|
||||
* An enum representing action types
|
||||
*/
|
||||
public enum Action {
|
||||
|
||||
CHANGE,
|
||||
REMOVE
|
||||
}
|
||||
CHANGE,
|
||||
REMOVE
|
||||
}
|
||||
}
|
||||
@@ -4,261 +4,282 @@ 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
|
||||
*/
|
||||
public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
|
||||
/**
|
||||
* Up to 16 characters long team name.
|
||||
* It is used to sort players alphabetically in TabList by team names.
|
||||
* Team-less players appear on top, sorted by usernames.
|
||||
* Players with teams are below them, players of the same team are
|
||||
* sorted alphabetically by usernames.
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* Up to 16 characters long team name.
|
||||
* It is used to sort players alphabetically in TabList by team names.
|
||||
* Team-less players appear on top, sorted by usernames.
|
||||
* Players with teams are below them, players of the same team are
|
||||
* sorted alphabetically by usernames.
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/** Team prefix, limited to 16 characters, on <1.13 */
|
||||
private String playerPrefix;
|
||||
/** Team prefix, limited to 16 characters, on <1.13 */
|
||||
private String playerPrefix;
|
||||
|
||||
/** Team suffix, limited to 16 characters, on <1.13 */
|
||||
private String playerSuffix;
|
||||
/** Team suffix, limited to 16 characters, on <1.13 */
|
||||
private String playerSuffix;
|
||||
|
||||
/**
|
||||
* NameTag visibility rule since 1.8. Possible options are:
|
||||
* {@code always}, {@code hideForOtherTeams}, {@code hideForOwnTeam}, {@code never}
|
||||
*/
|
||||
private String nameTagVisibility;
|
||||
/**
|
||||
* NameTag visibility rule since 1.8. Possible options are:
|
||||
* {@code always}, {@code hideForOtherTeams}, {@code hideForOwnTeam}, {@code never}
|
||||
*/
|
||||
private String nameTagVisibility;
|
||||
|
||||
/**
|
||||
* Collision rule added in 1.9. Possible options are:
|
||||
* {@code always}, {@code pushOtherTeams}, {@code pushOwnTeam}, {@code never}
|
||||
*/
|
||||
private String collisionRule;
|
||||
/**
|
||||
* Collision rule added in 1.9. Possible options are:
|
||||
* {@code always}, {@code pushOtherTeams}, {@code pushOwnTeam}, {@code never}
|
||||
*/
|
||||
private String collisionRule;
|
||||
|
||||
/**
|
||||
* Team color field added in 1.13. It determines name color,
|
||||
* prefix and suffix color start.
|
||||
*/
|
||||
private EnumChatFormat color;
|
||||
/**
|
||||
* Team color field added in 1.13. It determines name color,
|
||||
* prefix and suffix color start.
|
||||
*/
|
||||
private EnumChatFormat color;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Entity UUID option was added in 1.8.
|
||||
*/
|
||||
private Collection<String> players = Collections.emptyList();
|
||||
/**
|
||||
* 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.
|
||||
* Entity UUID option was added in 1.8.
|
||||
*/
|
||||
private Collection<String> players = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Packet action.
|
||||
* 0 = create team,
|
||||
* 1 = remove team,
|
||||
* 2 = update team info,
|
||||
* 3 = add entries,
|
||||
* 4 = remove entries
|
||||
*/
|
||||
private final int method;
|
||||
/**
|
||||
* Packet action.
|
||||
* 0 = create team,
|
||||
* 1 = remove team,
|
||||
* 2 = update team info,
|
||||
* 3 = add entries,
|
||||
* 4 = remove entries
|
||||
*/
|
||||
private final int action;
|
||||
|
||||
/**
|
||||
* Bit mask.
|
||||
* 0x01: Allow friendly fire,
|
||||
* 0x02: Can see invisible players on the same team.
|
||||
*/
|
||||
private int options;
|
||||
/**
|
||||
* Bit mask.
|
||||
* 0x01: Allow friendly fire,
|
||||
* 0x02: Can see invisible players on the same team.
|
||||
*/
|
||||
private int options;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Private constructor used
|
||||
* internally to validate team name and set action.
|
||||
*
|
||||
* @param method
|
||||
* Packet action (0 = create team, 1 = remove team, 2 = update team info,
|
||||
* 3 = add entries, 4 = remove entries)
|
||||
* @param name
|
||||
* Team name, up to 16 characters long
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
private PacketPlayOutScoreboardTeam(int method, String name) {
|
||||
if (name == null || name.length() == 0) throw new IllegalArgumentException("Team name cannot be null/empty");
|
||||
if (name.length() > 16) throw new IllegalArgumentException("name cannot be longer than 16 character (is " + name.length() + ")");
|
||||
this.method = method;
|
||||
this.name = name;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters. Private constructor used
|
||||
* internally to validate team name and set action.
|
||||
*
|
||||
* @param action
|
||||
* Packet action (0 = create team, 1 = remove team, 2 = update team info,
|
||||
* 3 = add entries, 4 = remove entries)
|
||||
* @param name
|
||||
* Team name, up to 16 characters long
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
private PacketPlayOutScoreboardTeam(int action, String name) {
|
||||
Preconditions.checkNotNull(name, "team name");
|
||||
Preconditions.checkMaxLength(name, 16, "team name");
|
||||
this.action = action;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and 0 (CREATE) action
|
||||
*
|
||||
* @param team
|
||||
* Team name, up to 16 characters long
|
||||
* @param prefix
|
||||
* Prefix of players in team
|
||||
* @param suffix
|
||||
* Suffix of players in team
|
||||
* @param visibility
|
||||
* NameTag visibility rule
|
||||
* @param collision
|
||||
* Collision rule
|
||||
* @param players
|
||||
* Affected entities
|
||||
* @param options
|
||||
* bitmask of team options
|
||||
* @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) {
|
||||
this(0, team);
|
||||
this.playerPrefix = prefix;
|
||||
this.playerSuffix = suffix;
|
||||
this.nameTagVisibility = visibility;
|
||||
this.collisionRule = collision;
|
||||
this.players = players;
|
||||
this.options = options;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and 0 (CREATE) action
|
||||
*
|
||||
* @param team
|
||||
* Team name, up to 16 characters long
|
||||
* @param prefix
|
||||
* Prefix of players in team
|
||||
* @param suffix
|
||||
* Suffix of players in team
|
||||
* @param visibility
|
||||
* NameTag visibility rule
|
||||
* @param collision
|
||||
* Collision rule
|
||||
* @param players
|
||||
* Affected entities
|
||||
* @param options
|
||||
* bitmask of team options
|
||||
* @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) {
|
||||
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;
|
||||
this.collisionRule = collision;
|
||||
this.players = players;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and 1 (REMOVE) action
|
||||
*
|
||||
* @param team
|
||||
* Team name, up to 16 characters long
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team) {
|
||||
this(1, team);
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and 1 (REMOVE) action
|
||||
*
|
||||
* @param team
|
||||
* Team name, up to 16 characters long
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team) {
|
||||
this(1, team);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and 2 (UPDATE_TEAM_INFO) action
|
||||
*
|
||||
* @param team
|
||||
* Team name, up to 16 characters long
|
||||
* @param prefix
|
||||
* Prefix of players in team
|
||||
* @param suffix
|
||||
* Suffix of players in team
|
||||
* @param visibility
|
||||
* NameTag visibility rule
|
||||
* @param collision
|
||||
* Collision rule
|
||||
* @param options
|
||||
* bitmask of team options
|
||||
* @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) {
|
||||
this(2, team);
|
||||
this.playerPrefix = prefix;
|
||||
this.playerSuffix = suffix;
|
||||
this.nameTagVisibility = visibility;
|
||||
this.collisionRule = collision;
|
||||
this.options = options;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and 2 (UPDATE_TEAM_INFO) action
|
||||
*
|
||||
* @param team
|
||||
* Team name, up to 16 characters long
|
||||
* @param prefix
|
||||
* Prefix of players in team
|
||||
* @param suffix
|
||||
* Suffix of players in team
|
||||
* @param visibility
|
||||
* NameTag visibility rule
|
||||
* @param collision
|
||||
* Collision rule
|
||||
* @param options
|
||||
* bitmask of team options
|
||||
* @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) {
|
||||
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;
|
||||
this.collisionRule = collision;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and 3 (ADD_PLAYERS) if {@code add} is {@code true},
|
||||
* 4 (REMOVE_PLAYERS) if {@code add} is {@code false} action.
|
||||
*
|
||||
* @param team
|
||||
* Team name, up to 16 characters long
|
||||
* @param players
|
||||
* Affected entities
|
||||
* @param add
|
||||
* {@code true} if players should be added, {@code false} if removed from team
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team, Collection<String> players, boolean add) {
|
||||
this(add ? 3 : 4, team);
|
||||
this.players = players;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameters and 3 (ADD_PLAYERS) if {@code add} is {@code true},
|
||||
* 4 (REMOVE_PLAYERS) if {@code add} is {@code false} action.
|
||||
*
|
||||
* @param team
|
||||
* Team name, up to 16 characters long
|
||||
* @param players
|
||||
* Affected entities
|
||||
* @param add
|
||||
* {@code true} if players should be added, {@code false} if removed from team
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team, 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, method, options);
|
||||
}
|
||||
@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 #name}
|
||||
*
|
||||
* @return team name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playerPrefix}
|
||||
* @return playerPrefix
|
||||
*/
|
||||
public String getPlayerPrefix() {
|
||||
return playerPrefix;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #playerPrefix}
|
||||
*
|
||||
* @return playerPrefix
|
||||
*/
|
||||
public String getPlayerPrefix() {
|
||||
return playerPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playerSuffix}
|
||||
* @return playerSuffix
|
||||
*/
|
||||
public String getPlayerSuffix() {
|
||||
return playerSuffix;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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 #color}
|
||||
*
|
||||
* @return color
|
||||
*/
|
||||
public EnumChatFormat getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #players}
|
||||
* @return players
|
||||
*/
|
||||
public Collection<String> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #players}
|
||||
*
|
||||
* @return players
|
||||
*/
|
||||
public Collection<String> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #options}
|
||||
* @return team options
|
||||
*/
|
||||
public int getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #method}
|
||||
* @return packet action
|
||||
*/
|
||||
public int getMethod() {
|
||||
return method;
|
||||
}
|
||||
/**
|
||||
* Returns {@link #options}
|
||||
*
|
||||
* @return team options
|
||||
*/
|
||||
public int getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public int getAction() {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Class representing a minecraft skin as a value - signature pair.
|
||||
*/
|
||||
public class Skin {
|
||||
|
||||
/** Skin value */
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,10 @@ package me.neznamy.tab.api.protocol;
|
||||
*/
|
||||
public interface TabPacket {
|
||||
|
||||
/**
|
||||
* Returns user-friendly string representation of the packet
|
||||
* @return user-friendly string representation of the packet
|
||||
*/
|
||||
String toString();
|
||||
/**
|
||||
* Returns user-friendly string representation of the packet
|
||||
*
|
||||
* @return user-friendly string representation of the packet
|
||||
*/
|
||||
String toString();
|
||||
}
|
||||
@@ -7,23 +7,23 @@ package me.neznamy.tab.api.scoreboard;
|
||||
*/
|
||||
public interface Line {
|
||||
|
||||
/**
|
||||
* Returns configured raw text of this line. Placeholders
|
||||
* remain in raw format.
|
||||
*
|
||||
* @return raw text of this line
|
||||
*/
|
||||
String getText();
|
||||
|
||||
/**
|
||||
* Changes text to new value. Supports placeholders, which are
|
||||
* automatically registered if needed and refreshed periodically
|
||||
* based on configuration. No need to call this method to try to
|
||||
* keep placeholders up to date. If value is identical to previous
|
||||
* one, nothing happens.
|
||||
*
|
||||
* @param text
|
||||
* Raw text to display in the line
|
||||
*/
|
||||
void setText(String text);
|
||||
/**
|
||||
* Returns configured raw text of this line. Placeholders
|
||||
* remain in raw format.
|
||||
*
|
||||
* @return raw text of this line
|
||||
*/
|
||||
String getText();
|
||||
|
||||
/**
|
||||
* Changes text to new value. Supports placeholders, which are
|
||||
* automatically registered if needed and refreshed periodically
|
||||
* based on configuration. No need to call this method to try to
|
||||
* keep placeholders up to date. If value is identical to previous
|
||||
* one, nothing happens.
|
||||
*
|
||||
* @param text
|
||||
* Raw text to display in the line
|
||||
*/
|
||||
void setText(String text);
|
||||
}
|
||||
@@ -10,78 +10,78 @@ import java.util.List;
|
||||
*/
|
||||
public interface Scoreboard {
|
||||
|
||||
/**
|
||||
* Returns internal name of this scoreboard. Name is defined in registration,
|
||||
* scoreboards from config use name they were defined with. This value is
|
||||
* used internally and in /tab announce scoreboard command.
|
||||
*
|
||||
* @return custom name of this scoreboard
|
||||
*/
|
||||
String getName();
|
||||
/**
|
||||
* Returns internal name of this scoreboard. Name is defined in registration,
|
||||
* scoreboards from config use name they were defined with. This value is
|
||||
* used internally and in /tab announce scoreboard command.
|
||||
*
|
||||
* @return custom name of this scoreboard
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns raw title of this scoreboard. Placeholders stay in their
|
||||
* raw format.
|
||||
*
|
||||
* @return scoreboard title
|
||||
* @see #setTitle(String)
|
||||
*/
|
||||
String getTitle();
|
||||
/**
|
||||
* Returns raw title of this scoreboard. Placeholders stay in their
|
||||
* raw format.
|
||||
*
|
||||
* @return scoreboard title
|
||||
* @see #setTitle(String)
|
||||
*/
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* Sets title to specified value. Placeholders are refreshed automatically
|
||||
* with refresh intervals defined in config. No need to call this method
|
||||
* to try to keep placeholders up to date. Supports RGB codes using any of
|
||||
* the supported formats.
|
||||
* <p>
|
||||
* Length is limited to 32 characters on <1.13. If the limit is exceeded,
|
||||
* text will be cut to 32 characters.
|
||||
* <p>
|
||||
* Calling this method with same title as before will not do anything.
|
||||
*
|
||||
* @param title
|
||||
* New title to use with placeholder support
|
||||
* @see #getTitle()
|
||||
*/
|
||||
void setTitle(String title);
|
||||
/**
|
||||
* Sets title to specified value. Placeholders are refreshed automatically
|
||||
* with refresh intervals defined in config. No need to call this method
|
||||
* to try to keep placeholders up to date. Supports RGB codes using any of
|
||||
* the supported formats.
|
||||
* <p>
|
||||
* Length is limited to 32 characters on <1.13. If the limit is exceeded,
|
||||
* text will be cut to 32 characters.
|
||||
* <p>
|
||||
* Calling this method with same title as before will not do anything.
|
||||
*
|
||||
* @param title
|
||||
* New title to use with placeholder support
|
||||
* @see #getTitle()
|
||||
*/
|
||||
void setTitle(String title);
|
||||
|
||||
/**
|
||||
* Returns list of lines in this scoreboard in the order they appear
|
||||
* in game (first line is on top). This list should only be used for reading,
|
||||
* for adding/removing lines see {@link #addLine(String)} and {@link #removeLine(int)}.
|
||||
*
|
||||
* @return list of lines in this scoreboard
|
||||
* @see #addLine(String)
|
||||
* @see #removeLine(int)
|
||||
*/
|
||||
List<Line> getLines();
|
||||
/**
|
||||
* Returns list of lines in this scoreboard in the order they appear
|
||||
* in game (first line is on top). This list should only be used for reading,
|
||||
* for adding/removing lines see {@link #addLine(String)} and {@link #removeLine(int)}.
|
||||
*
|
||||
* @return list of lines in this scoreboard
|
||||
* @see #addLine(String)
|
||||
* @see #removeLine(int)
|
||||
*/
|
||||
List<Line> getLines();
|
||||
|
||||
/**
|
||||
* Adds line with specified text on the bottom of scoreboard. Supports
|
||||
* placeholders, which will automatically be refreshed.
|
||||
*
|
||||
* @param text
|
||||
* Text to display
|
||||
* @see #getLines()
|
||||
* @see #removeLine(int)
|
||||
*/
|
||||
void addLine(String text);
|
||||
/**
|
||||
* Adds line with specified text on the bottom of scoreboard. Supports
|
||||
* placeholders, which will automatically be refreshed.
|
||||
*
|
||||
* @param text
|
||||
* Text to display
|
||||
* @see #getLines()
|
||||
* @see #removeLine(int)
|
||||
*/
|
||||
void addLine(String text);
|
||||
|
||||
/**
|
||||
* Removes line with specified index. Index starts at {@code 0} and ends at
|
||||
* {@link #getLines()}.size()-1.
|
||||
*
|
||||
* @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())
|
||||
* @see #getLines()
|
||||
* @see #addLine(String)
|
||||
*/
|
||||
void removeLine(int index);
|
||||
/**
|
||||
* Removes line with specified index. Index starts at {@code 0} and ends at
|
||||
* {@link #getLines()}.size()-1.
|
||||
*
|
||||
* @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())
|
||||
* @see #getLines()
|
||||
* @see #addLine(String)
|
||||
*/
|
||||
void removeLine(int index);
|
||||
|
||||
/**
|
||||
* Unregisters this scoreboard from all players who can see it.
|
||||
*/
|
||||
void unregister();
|
||||
/**
|
||||
* Unregisters this scoreboard from all players who can see it.
|
||||
*/
|
||||
void unregister();
|
||||
}
|
||||
@@ -15,141 +15,152 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
*/
|
||||
public interface ScoreboardManager {
|
||||
|
||||
/**
|
||||
* Creates new scoreboard, registers and returns it. {@code name}
|
||||
* is internal name of the scoreboard, which it will be registered
|
||||
* with and further work where scoreboard name is used allowed.
|
||||
* <p>
|
||||
* Scoreboard {@code title} is limited to 32 characters on <1.13. If needed,
|
||||
* it will be cut to 32 character for those players (after replacing
|
||||
* placeholders). RGB is supported using any of the supported formats.
|
||||
* Placeholders are supported.
|
||||
* <p>
|
||||
* {@code lines} will display in the same order as contained in the list.
|
||||
* Client can only see up to 15 lines at a time, however you can define
|
||||
* more, if some of them only consist of placeholder that might be empty.
|
||||
*
|
||||
* @param name
|
||||
* Internal name of the scoreboard
|
||||
* @param title
|
||||
* The scoreboard title
|
||||
* @param lines
|
||||
* Lines of text in scoreboard (supports placeholders)
|
||||
* @return The new scoreboard with given parameters
|
||||
* @throws NullPointerException
|
||||
* if {@code name} or {@code lines} is {@code null}
|
||||
*/
|
||||
Scoreboard createScoreboard(String name, String title, List<String> lines);
|
||||
|
||||
/**
|
||||
* Returns map of registered scoreboards via config and API.
|
||||
* Map key is name of scoreboard, which is defined either in config
|
||||
* or using {@link #createScoreboard(String, String, List)}.
|
||||
* Map value is reference to the actual scoreboard.
|
||||
*
|
||||
* @return map of registered scoreboards
|
||||
*/
|
||||
Map<String, Scoreboard> getRegisteredScoreboards();
|
||||
|
||||
/**
|
||||
* Displays scoreboard to defined player. This will disable all display
|
||||
* condition checks until the scoreboard is unregistered using
|
||||
* {@link #resetScoreboard(TabPlayer)}. If the player already sees
|
||||
* this scoreboard, nothing will happen.
|
||||
*
|
||||
* @param player
|
||||
* Player to send scoreboard to
|
||||
* @param scoreboard
|
||||
* Scoreboard to display
|
||||
* @throws NullPointerException
|
||||
* if {@code player} or {@code scoreboard} is {@code null}
|
||||
* @see #hasCustomScoreboard(TabPlayer)
|
||||
* @see #resetScoreboard(TabPlayer)
|
||||
*/
|
||||
void showScoreboard(TabPlayer player, Scoreboard scoreboard);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if player has custom scoreboard set using
|
||||
* {@link #showScoreboard(TabPlayer, Scoreboard)}, {@code false} if not.
|
||||
*
|
||||
* @param player
|
||||
* Player to check
|
||||
* @return {@code true} if player has custom scoreboard set using API,
|
||||
* {@code false} if not
|
||||
* @see #showScoreboard(TabPlayer, Scoreboard)
|
||||
* @see #resetScoreboard(TabPlayer)
|
||||
*/
|
||||
boolean hasCustomScoreboard(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Hides custom scoreboard sent using {@link #showScoreboard(TabPlayer, Scoreboard)}
|
||||
* and re-enables internal display logic with conditions. If player does not
|
||||
* have any forced scoreboard, nothing happens.
|
||||
*
|
||||
* @param player
|
||||
* Player to hide custom scoreboard from
|
||||
* @see #showScoreboard(TabPlayer, Scoreboard)
|
||||
* @see #hasCustomScoreboard(TabPlayer)
|
||||
*/
|
||||
void resetScoreboard(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if player has scoreboard enabled, {@code false}
|
||||
* if disabled (toggled) using either toggle command,
|
||||
* {@link #toggleScoreboard(TabPlayer, boolean)} or
|
||||
* {@link #setScoreboardVisible(TabPlayer, boolean, boolean)}.
|
||||
*
|
||||
* @param player
|
||||
* Player to get visibility status of
|
||||
* @return {@code true} if visible, {@code false} if disabled
|
||||
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
|
||||
* @see #toggleScoreboard(TabPlayer, boolean)
|
||||
*/
|
||||
boolean hasScoreboardVisible(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Sets scoreboard visibility of player to defined value. If visibility status
|
||||
* is same as player had before, nothing happens. If this call changes
|
||||
* visibility, scoreboard is toggled. If {@code sendToggleMessage} is {@code true},
|
||||
* toggle message defined in configuration is sent.
|
||||
*
|
||||
* @param player
|
||||
* Player to set visibility for
|
||||
* @param visible
|
||||
* New visibility status
|
||||
* @param sendToggleMessage
|
||||
* whether to send toggle message if status changed or not
|
||||
* @see #toggleScoreboard(TabPlayer, boolean)
|
||||
* @see #hasScoreboardVisible(TabPlayer)
|
||||
*/
|
||||
void setScoreboardVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Toggles scoreboard for specified player. If player had scoreboard visible,
|
||||
* hides it. If hidden, shows it. If {@code sendToggleMessage} is {@code true},
|
||||
* toggle message defined in configuration is sent.
|
||||
*
|
||||
* @param player
|
||||
* Player to toggle scoreboard for
|
||||
* @param sendToggleMessage
|
||||
* Whether to send toggle message or not
|
||||
* @see #hasScoreboardVisible(TabPlayer)
|
||||
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
|
||||
*/
|
||||
void toggleScoreboard(TabPlayer player, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Temporarily displays scoreboard to all players for specified amount of
|
||||
* time (in milliseconds). Scoreboard name is either defined in config, or
|
||||
* through API in {@link #createScoreboard(String, String, List)}.
|
||||
*
|
||||
* @param scoreboard
|
||||
* Scoreboard from config or registered via API
|
||||
* @param duration
|
||||
* Duration in milliseconds
|
||||
* @throws IllegalArgumentException
|
||||
* if no scoreboard was found with such name or {@code duration}
|
||||
* is < 0.
|
||||
*/
|
||||
void announceScoreboard(String scoreboard, int duration);
|
||||
/**
|
||||
* Creates new scoreboard, registers and returns it. {@code name}
|
||||
* is internal name of the scoreboard, which it will be registered
|
||||
* with and further work where scoreboard name is used allowed.
|
||||
* <p>
|
||||
* Scoreboard {@code title} is limited to 32 characters on <1.13. If needed,
|
||||
* it will be cut to 32 character for those players (after replacing
|
||||
* placeholders). RGB is supported using any of the supported formats.
|
||||
* Placeholders are supported.
|
||||
* <p>
|
||||
* {@code lines} will display in the same order as contained in the list.
|
||||
* Client can only see up to 15 lines at a time, however you can define
|
||||
* more, if some of them only consist of placeholder that might be empty.
|
||||
*
|
||||
* @param name
|
||||
* Internal name of the scoreboard
|
||||
* @param title
|
||||
* The scoreboard title
|
||||
* @param lines
|
||||
* Lines of text in scoreboard (supports placeholders)
|
||||
* @return The new scoreboard with given parameters
|
||||
* @throws NullPointerException
|
||||
* if {@code name} or {@code lines} is {@code null}
|
||||
*/
|
||||
Scoreboard createScoreboard(String name, String title, List<String> lines);
|
||||
|
||||
/**
|
||||
* Returns map of registered scoreboards via config and API.
|
||||
* Map key is name of scoreboard, which is defined either in config
|
||||
* or using {@link #createScoreboard(String, String, List)}.
|
||||
* Map value is reference to the actual scoreboard.
|
||||
*
|
||||
* @return map of registered scoreboards
|
||||
*/
|
||||
Map<String, Scoreboard> getRegisteredScoreboards();
|
||||
|
||||
/**
|
||||
* Displays scoreboard to defined player. This will disable all display
|
||||
* condition checks until the scoreboard is unregistered using
|
||||
* {@link #resetScoreboard(TabPlayer)}. If the player already sees
|
||||
* this scoreboard, nothing will happen.
|
||||
*
|
||||
* @param player
|
||||
* Player to send scoreboard to
|
||||
* @param scoreboard
|
||||
* Scoreboard to display
|
||||
* @throws NullPointerException
|
||||
* if {@code player} or {@code scoreboard} is {@code null}
|
||||
* @see #hasCustomScoreboard(TabPlayer)
|
||||
* @see #resetScoreboard(TabPlayer)
|
||||
*/
|
||||
void showScoreboard(TabPlayer player, Scoreboard scoreboard);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if player has custom scoreboard set using
|
||||
* {@link #showScoreboard(TabPlayer, Scoreboard)}, {@code false} if not.
|
||||
*
|
||||
* @param player
|
||||
* Player to check
|
||||
* @return {@code true} if player has custom scoreboard set using API,
|
||||
* {@code false} if not
|
||||
* @see #showScoreboard(TabPlayer, Scoreboard)
|
||||
* @see #resetScoreboard(TabPlayer)
|
||||
*/
|
||||
boolean hasCustomScoreboard(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Hides custom scoreboard sent using {@link #showScoreboard(TabPlayer, Scoreboard)}
|
||||
* and re-enables internal display logic with conditions. If player does not
|
||||
* have any forced scoreboard, nothing happens.
|
||||
*
|
||||
* @param player
|
||||
* Player to hide custom scoreboard from
|
||||
* @see #showScoreboard(TabPlayer, Scoreboard)
|
||||
* @see #hasCustomScoreboard(TabPlayer)
|
||||
*/
|
||||
void resetScoreboard(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if player has scoreboard enabled, {@code false}
|
||||
* if disabled (toggled) using either toggle command,
|
||||
* {@link #toggleScoreboard(TabPlayer, boolean)} or
|
||||
* {@link #setScoreboardVisible(TabPlayer, boolean, boolean)}.
|
||||
*
|
||||
* @param player
|
||||
* Player to get visibility status of
|
||||
* @return {@code true} if visible, {@code false} if disabled
|
||||
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
|
||||
* @see #toggleScoreboard(TabPlayer, boolean)
|
||||
*/
|
||||
boolean hasScoreboardVisible(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Sets scoreboard visibility of player to defined value. If visibility status
|
||||
* is same as player had before, nothing happens. If this call changes
|
||||
* visibility, scoreboard is toggled. If {@code sendToggleMessage} is {@code true},
|
||||
* toggle message defined in configuration is sent.
|
||||
*
|
||||
* @param player
|
||||
* Player to set visibility for
|
||||
* @param visible
|
||||
* New visibility status
|
||||
* @param sendToggleMessage
|
||||
* whether to send toggle message if status changed or not
|
||||
* @see #toggleScoreboard(TabPlayer, boolean)
|
||||
* @see #hasScoreboardVisible(TabPlayer)
|
||||
*/
|
||||
void setScoreboardVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Toggles scoreboard for specified player. If player had scoreboard visible,
|
||||
* hides it. If hidden, shows it. If {@code sendToggleMessage} is {@code true},
|
||||
* toggle message defined in configuration is sent.
|
||||
*
|
||||
* @param player
|
||||
* Player to toggle scoreboard for
|
||||
* @param sendToggleMessage
|
||||
* Whether to send toggle message or not
|
||||
* @see #hasScoreboardVisible(TabPlayer)
|
||||
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
|
||||
*/
|
||||
void toggleScoreboard(TabPlayer player, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Temporarily displays scoreboard to all players for specified amount of
|
||||
* time (in milliseconds). Scoreboard name is either defined in config, or
|
||||
* through API in {@link #createScoreboard(String, String, List)}.
|
||||
*
|
||||
* @param scoreboard
|
||||
* Scoreboard from config or registered via API
|
||||
* @param duration
|
||||
* Duration in milliseconds
|
||||
* @throws IllegalArgumentException
|
||||
* if no scoreboard was found with such name or {@code duration}
|
||||
* is < 0.
|
||||
*/
|
||||
void announceScoreboard(String scoreboard, int duration);
|
||||
|
||||
/**
|
||||
* Returns player's currently displayed scoreboard. This can be either with
|
||||
* configuration, overridden with commands or the API. Will return {@code null}
|
||||
* if player does not see any scoreboard due to not meeting any display condition.
|
||||
*
|
||||
* @param player
|
||||
* player to get active scoreboard of
|
||||
* @return player's active scoreboard or {@code null} if player has no scoreboard
|
||||
*/
|
||||
Scoreboard getActiveScoreboard(TabPlayer player);
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
package me.neznamy.tab.api.task;
|
||||
|
||||
public interface RepeatingTask {
|
||||
|
||||
int getInterval();
|
||||
|
||||
int getInterval();
|
||||
|
||||
void setInterval(int paramInt);
|
||||
void setInterval(int paramInt);
|
||||
|
||||
void cancel();
|
||||
|
||||
void setRunnable(Runnable paramRunnable);
|
||||
void cancel();
|
||||
}
|
||||
@@ -6,51 +6,66 @@ import me.neznamy.tab.api.TabFeature;
|
||||
|
||||
public interface ThreadManager {
|
||||
|
||||
/**
|
||||
* Starts a task in new thread and measures how long it took to process
|
||||
* @param errorDescription - description to use if this task throws an error
|
||||
* @param feature - feature to add cpu usage to
|
||||
* @param type - usage type to add cpu usage to
|
||||
* @param task - the task
|
||||
*/
|
||||
Future<Void> runMeasuredTask(String errorDescription, TabFeature feature, String type, Runnable task);
|
||||
|
||||
/**
|
||||
* Runs task in a new thread
|
||||
* @param errorDescription - description to use if this task throws an error
|
||||
* @param task - the task
|
||||
*/
|
||||
Future<Void> runTask(String errorDescription, Runnable task);
|
||||
|
||||
/**
|
||||
* Starts a new task with defined repeat interval that measures cpu usage
|
||||
* @param intervalMilliseconds - task interval
|
||||
* @param errorDescription - description to use if this task throws an error
|
||||
* @param feature - feature to add cpu usage to
|
||||
* @param type - usage type to add cpu usage to
|
||||
* @param task - the task
|
||||
*/
|
||||
RepeatingTask startRepeatingMeasuredTask(int intervalMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task);
|
||||
|
||||
/**
|
||||
* Runs task with a delay and measures how long it took to process
|
||||
* @param delayMilliseconds - how long to run the task after
|
||||
* @param errorDescription - description to use if this task throws an error
|
||||
* @param feature - feature to add cpu usage to
|
||||
* @param type - usage type to add cpu usage to
|
||||
* @param task - the task
|
||||
*/
|
||||
Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task);
|
||||
|
||||
/**
|
||||
* Runs task with a delay and measures how long it took to process
|
||||
* @param delayMilliseconds - how long to run the task after
|
||||
* @param errorDescription - description to use if this task throws an error
|
||||
* @param feature - feature to add cpu usage to
|
||||
* @param type - usage type to add cpu usage to
|
||||
* @param task - the task
|
||||
*/
|
||||
Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, String feature, String type, Runnable task);
|
||||
|
||||
Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, Runnable task);
|
||||
/**
|
||||
* Starts a task in new thread and measures how long it took to process
|
||||
*
|
||||
* @param feature
|
||||
* feature to add cpu usage to
|
||||
* @param type
|
||||
* usage type to add cpu usage to
|
||||
* @param task
|
||||
* the task
|
||||
*/
|
||||
Future<Void> runMeasuredTask(TabFeature feature, String type, Runnable task);
|
||||
|
||||
/**
|
||||
* Starts a task in new thread and measures how long it took to process
|
||||
*
|
||||
* @param feature
|
||||
* feature to add cpu usage to
|
||||
* @param type
|
||||
* usage type to add cpu usage to
|
||||
* @param task
|
||||
* the task
|
||||
*/
|
||||
Future<Void> runMeasuredTask(String feature, String type, Runnable task);
|
||||
|
||||
/**
|
||||
* Runs task in a new thread
|
||||
*
|
||||
* @param task
|
||||
* the task
|
||||
*/
|
||||
Future<Void> runTask(Runnable task);
|
||||
|
||||
/**
|
||||
* Starts a new task with defined repeat interval that measures cpu usage
|
||||
*
|
||||
* @param intervalMilliseconds
|
||||
* task interval
|
||||
* @param feature
|
||||
* feature to add cpu usage to
|
||||
* @param type
|
||||
* usage type to add cpu usage to
|
||||
* @param task
|
||||
* the task
|
||||
*/
|
||||
RepeatingTask startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task);
|
||||
|
||||
RepeatingTask startRepeatingTask(int intervalMilliseconds, Runnable task);
|
||||
|
||||
/**
|
||||
* Runs task with a delay and measures how long it took to process
|
||||
*
|
||||
* @param delayMilliseconds
|
||||
* how long to run the task after
|
||||
* @param feature
|
||||
* feature to add cpu usage to
|
||||
* @param type
|
||||
* usage type to add cpu usage to
|
||||
* @param task
|
||||
* the task
|
||||
* @return future allowing to cancel the task
|
||||
*/
|
||||
Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task);
|
||||
}
|
||||
@@ -4,110 +4,179 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
public interface TeamManager {
|
||||
|
||||
/**
|
||||
* Makes player's NameTag globally invisible
|
||||
* @see #showNametag(TabPlayer)
|
||||
* @see #hasHiddenNametag(TabPlayer)
|
||||
*/
|
||||
void hideNametag(TabPlayer player);
|
||||
/**
|
||||
* Makes player's NameTag globally invisible
|
||||
*
|
||||
* @param player
|
||||
* player to hide nametag of
|
||||
* @see #showNametag(TabPlayer)
|
||||
* @see #hasHiddenNametag(TabPlayer)
|
||||
*/
|
||||
void hideNametag(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Hides player's NameTag for specified player until it's shown again
|
||||
* @param viewer - player to hide NameTag for
|
||||
*/
|
||||
void hideNametag(TabPlayer player, TabPlayer viewer);
|
||||
/**
|
||||
* Hides player's NameTag for specified player until it's shown again
|
||||
*
|
||||
* @param player
|
||||
* player to hide nametag of
|
||||
* @param viewer
|
||||
* player to hide NameTag for
|
||||
*/
|
||||
void hideNametag(TabPlayer player, TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Makes player's NameTag visible again
|
||||
* @see #hideNametag(TabPlayer)
|
||||
* @see #hasHiddenNametag(TabPlayer)
|
||||
*/
|
||||
void showNametag(TabPlayer player);
|
||||
/**
|
||||
* Makes player's NameTag visible again
|
||||
*
|
||||
* @param player
|
||||
* player to show nametag of
|
||||
* @see #hideNametag(TabPlayer)
|
||||
* @see #hasHiddenNametag(TabPlayer)
|
||||
*/
|
||||
void showNametag(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Shows player's NameTag for specified viewer if it was hidden before
|
||||
* @param viewer - player to show NameTag back for
|
||||
*/
|
||||
void showNametag(TabPlayer player, TabPlayer viewer);
|
||||
/**
|
||||
* Shows player's NameTag for specified viewer if it was hidden before
|
||||
*
|
||||
* @param player
|
||||
* player to show nametag of
|
||||
* @param viewer
|
||||
* player to show NameTag back for
|
||||
*/
|
||||
void showNametag(TabPlayer player, TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Return whether player has hidden NameTag or not
|
||||
* @return Whether player has hidden NameTag or not
|
||||
* @see #hideNametag(TabPlayer)
|
||||
* @see #showNametag(TabPlayer)
|
||||
*/
|
||||
boolean hasHiddenNametag(TabPlayer player);
|
||||
/**
|
||||
* Return whether player has hidden NameTag or not
|
||||
*
|
||||
* @param player
|
||||
* player to check nametag visibility status of
|
||||
* @return Whether player has hidden NameTag or not
|
||||
* @see #hideNametag(TabPlayer)
|
||||
* @see #showNametag(TabPlayer)
|
||||
*/
|
||||
boolean hasHiddenNametag(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns true if NameTag is hidden for specified viewer, false if not
|
||||
* @param viewer - player to check visibility status for
|
||||
* @return true if hidden, false if not
|
||||
*/
|
||||
boolean hasHiddenNametag(TabPlayer player, TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Unregisters player's team and no longer handles it, as well as disables anti-override for teams.
|
||||
* This can be resumed using resumeTeamHandling(). If team handling was paused already, nothing happens.
|
||||
*/
|
||||
void pauseTeamHandling(TabPlayer player);
|
||||
/**
|
||||
* Returns true if NameTag is hidden for specified viewer, false if not
|
||||
*
|
||||
* @param player
|
||||
* player to check visibility status of
|
||||
* @param viewer
|
||||
* player to check visibility status for
|
||||
* @return true if hidden, false if not
|
||||
*/
|
||||
boolean hasHiddenNametag(TabPlayer player, TabPlayer viewer);
|
||||
|
||||
/**
|
||||
* Resumes team handling if it was before paused using pauseTeamHandling(), if not, nothing happens
|
||||
*/
|
||||
void resumeTeamHandling(TabPlayer player);
|
||||
/**
|
||||
* Unregisters player's team and no longer handles it, as well as disables anti-override for teams.
|
||||
* This can be resumed using resumeTeamHandling(). If team handling was paused already, nothing happens.
|
||||
*
|
||||
* @param player
|
||||
* player to pause team handling of
|
||||
*/
|
||||
void pauseTeamHandling(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns true if team handling is paused for this player using pauseTeamHandling(), false if not, or
|
||||
* it was resumed already using resumeTeamHandling
|
||||
* @return true if paused, false if not
|
||||
*/
|
||||
boolean hasTeamHandlingPaused(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Forces new team name for the player until this method is called again with null argument and
|
||||
* performs all actions to change player's team name
|
||||
* @param teamName - forced team name
|
||||
*/
|
||||
void forceTeamName(TabPlayer player, String teamName);
|
||||
/**
|
||||
* Resumes team handling if it was before paused using pauseTeamHandling(), if not, nothing happens
|
||||
*
|
||||
* @param player
|
||||
* player to resume team handling of
|
||||
*/
|
||||
void resumeTeamHandling(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns forced team name of player or null if not forced
|
||||
* @return forced team name of player or null if not forced
|
||||
*/
|
||||
String getForcedTeamName(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns true if team handling is paused for this player using pauseTeamHandling(), false if not, or
|
||||
* it was resumed already using resumeTeamHandling
|
||||
*
|
||||
* @param player
|
||||
* player to check handling status of
|
||||
* @return true if paused, false if not
|
||||
*/
|
||||
boolean hasTeamHandlingPaused(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Forces collision rule for the player. Setting it to null will remove forced value
|
||||
* @param collision - forced collision rule
|
||||
*/
|
||||
void setCollisionRule(TabPlayer player, Boolean collision);
|
||||
/**
|
||||
* Forces new team name for the player until this method is called again with null argument and
|
||||
* performs all actions to change player's team name
|
||||
*
|
||||
* @param player
|
||||
* player to set team name of
|
||||
* @param teamName
|
||||
* forced team name
|
||||
*/
|
||||
void forceTeamName(TabPlayer player, String teamName);
|
||||
|
||||
/**
|
||||
* Returns forced collision rule or null if collision is not forced using setCollisionRule
|
||||
* @return forced value or null if not forced
|
||||
*/
|
||||
Boolean getCollisionRule(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Sends update team's properties packet of player's team to everyone
|
||||
* @param player - player to update team data of
|
||||
*/
|
||||
void updateTeamData(TabPlayer player);
|
||||
|
||||
void setPrefix(TabPlayer player, String prefix);
|
||||
|
||||
void setSuffix(TabPlayer player, String suffix);
|
||||
|
||||
void resetPrefix(TabPlayer player);
|
||||
|
||||
void resetSuffix(TabPlayer player);
|
||||
|
||||
String getCustomPrefix(TabPlayer player);
|
||||
|
||||
String getCustomSuffix(TabPlayer player);
|
||||
|
||||
String getOriginalPrefix(TabPlayer player);
|
||||
|
||||
String getOriginalSuffix(TabPlayer player);
|
||||
/**
|
||||
* Returns forced team name of player or null if not forced
|
||||
*
|
||||
* @param player
|
||||
* player to check forced team name of
|
||||
* @return forced team name of player or null if not forced
|
||||
*/
|
||||
String getForcedTeamName(TabPlayer player);
|
||||
|
||||
|
||||
/**
|
||||
* Forces collision rule for the player. Setting it to null will remove forced value
|
||||
*
|
||||
* @param player
|
||||
* player to set collision rule of
|
||||
* @param collision
|
||||
* forced collision rule
|
||||
*/
|
||||
void setCollisionRule(TabPlayer player, Boolean collision);
|
||||
|
||||
/**
|
||||
* Returns forced collision rule or null if collision is not forced using setCollisionRule
|
||||
*
|
||||
* @param player
|
||||
* player to get forced collision of
|
||||
* @return forced value or null if not forced
|
||||
*/
|
||||
Boolean getCollisionRule(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Sends update team's properties packet of player's team to everyone
|
||||
*
|
||||
* @param player
|
||||
* player to update team data of
|
||||
*/
|
||||
void updateTeamData(TabPlayer player);
|
||||
|
||||
void setPrefix(TabPlayer player, String prefix);
|
||||
|
||||
void setSuffix(TabPlayer player, String suffix);
|
||||
|
||||
void resetPrefix(TabPlayer player);
|
||||
|
||||
void resetSuffix(TabPlayer player);
|
||||
|
||||
String getCustomPrefix(TabPlayer player);
|
||||
|
||||
String getCustomSuffix(TabPlayer player);
|
||||
|
||||
String getOriginalPrefix(TabPlayer player);
|
||||
|
||||
String getOriginalSuffix(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Toggles nametag visibility view on all players for specified player.
|
||||
* On first call, nametags of all players will become invisible for specified
|
||||
* player. On second call, they will become visible again.
|
||||
*
|
||||
* @param player
|
||||
* player to toggle nametag visibility view for
|
||||
* @param sendToggleMessage
|
||||
* {@code true} if configured toggle message should be sent, {@code false} if not
|
||||
*/
|
||||
void toggleNameTagVisibilityView(TabPlayer player, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if player has hidden nametags by either calling
|
||||
* {@link #toggleNameTagVisibilityView(TabPlayer, boolean)} or using a command,
|
||||
* {@code false} if not.
|
||||
*
|
||||
* @param player
|
||||
* player to check
|
||||
* @return {@code true} if hidden, {@code false} if not
|
||||
*/
|
||||
boolean hasHiddenNameTagVisibilityView(TabPlayer player);
|
||||
}
|
||||
@@ -6,27 +6,27 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
public interface UnlimitedNametagManager extends TeamManager {
|
||||
|
||||
void disableArmorStands(TabPlayer player);
|
||||
|
||||
void enableArmorStands(TabPlayer player);
|
||||
|
||||
boolean hasDisabledArmorStands(TabPlayer player);
|
||||
void disableArmorStands(TabPlayer player);
|
||||
|
||||
void enableArmorStands(TabPlayer player);
|
||||
|
||||
boolean hasDisabledArmorStands(TabPlayer player);
|
||||
|
||||
void setName(TabPlayer player, String customName);
|
||||
|
||||
void setLine(TabPlayer player, String line, String value);
|
||||
void setName(TabPlayer player, String customName);
|
||||
|
||||
void setLine(TabPlayer player, String line, String value);
|
||||
|
||||
void resetName(TabPlayer player);
|
||||
|
||||
void resetLine(TabPlayer player, String line);
|
||||
void resetName(TabPlayer player);
|
||||
|
||||
void resetLine(TabPlayer player, String line);
|
||||
|
||||
String getCustomName(TabPlayer player);
|
||||
|
||||
String getCustomLineValue(TabPlayer player, String line);
|
||||
String getCustomName(TabPlayer player);
|
||||
|
||||
String getCustomLineValue(TabPlayer player, String line);
|
||||
|
||||
String getOriginalName(TabPlayer player);
|
||||
|
||||
String getOriginalLineValue(TabPlayer player, String line);
|
||||
|
||||
List<String> getDefinedLines();
|
||||
String getOriginalName(TabPlayer player);
|
||||
|
||||
String getOriginalLineValue(TabPlayer player, String line);
|
||||
|
||||
List<String> getDefinedLines();
|
||||
}
|
||||
@@ -5,14 +5,14 @@ package me.neznamy.tab.api.util;
|
||||
* 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>
|
||||
* First function argument
|
||||
* @param <B>
|
||||
* Second function argument
|
||||
* @param <C>
|
||||
* Function return type
|
||||
* @param <A>
|
||||
* First function argument
|
||||
* @param <B>
|
||||
* Second function argument
|
||||
* @param <C>
|
||||
* Function return type
|
||||
*/
|
||||
public interface BiFunctionWithException<A, B, C> {
|
||||
|
||||
C apply(A a, B b) throws ReflectiveOperationException;
|
||||
C apply(A a, B b) throws ReflectiveOperationException;
|
||||
}
|
||||
@@ -1,13 +1,26 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
public class Preconditions {
|
||||
|
||||
public static void checkNotNull(Object obj, String exceptionMessage){
|
||||
if (obj == null) throw new IllegalArgumentException(exceptionMessage);
|
||||
private Preconditions(){}
|
||||
|
||||
public static void checkNotNull(Object obj, String name){
|
||||
if (obj == null) throw new IllegalArgumentException(name + " cannot be null");
|
||||
}
|
||||
|
||||
public static void checkRange(Number number, Number min, Number max){
|
||||
public static void checkMaxLength(String string, int maxLength, String name) {
|
||||
checkNotNull(string, name);
|
||||
if (string.length() > maxLength) throw new IllegalArgumentException(name + " is longer than " + maxLength + " characters (" + string.length() + ")");
|
||||
}
|
||||
|
||||
public static void checkRange(Number number, Number min, Number max, String variable){
|
||||
if (number.doubleValue() < min.doubleValue() || number.doubleValue() > max.doubleValue())
|
||||
throw new IllegalArgumentException("Number index out of range (" + min + " - " + max + ")");
|
||||
throw new IllegalArgumentException(variable + " index out of range (" + min + " - " + max + ")");
|
||||
}
|
||||
|
||||
public static void checkLoaded(TabPlayer player) {
|
||||
if (!player.isLoaded()) throw new IllegalStateException("Player is not loaded yet. Try again later.");
|
||||
}
|
||||
}
|
||||
@@ -8,5 +8,6 @@ tasks {
|
||||
archiveFileName.set("TAB-${project.name}-${project.version}.jar")
|
||||
relocate("org.bstats", "me.neznamy.tab.libs.org.bstats")
|
||||
relocate("org.json.simple", "me.neznamy.tab.libs.org.json.simple")
|
||||
relocate("net.kyori.event", "me.neznamy.tab.libs.net.kyori.event")
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -11,8 +11,8 @@ allprojects {
|
||||
val platforms = setOf(
|
||||
projects.bukkit,
|
||||
projects.bungeecord,
|
||||
projects.krypton,
|
||||
projects.velocity
|
||||
// projects.krypton,
|
||||
// projects.velocity
|
||||
).map { it.dependencyProject }
|
||||
|
||||
val special = setOf(
|
||||
|
||||
@@ -7,5 +7,7 @@ dependencies {
|
||||
compileOnly(libs.via)
|
||||
compileOnly(libs.authlib)
|
||||
compileOnly(libs.libsDisguises)
|
||||
compileOnly(libs.essentials)
|
||||
compileOnly(libs.essentials) {
|
||||
exclude("org.spigotmc", "spigot-api")
|
||||
}
|
||||
}
|
||||
@@ -15,49 +15,64 @@ import me.neznamy.tab.shared.TAB;
|
||||
*/
|
||||
public class BukkitEventListener implements Listener {
|
||||
|
||||
private final Main main;
|
||||
|
||||
public BukkitEventListener(Main main) {
|
||||
this.main = main;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerQuitEvent to remove player data and forward the event to features
|
||||
* @param e quit event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onQuit(PlayerQuitEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask("processing PlayerQuitEvent", () -> TAB.getInstance().getFeatureManager().onQuit(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerJoinEvent to create player data and forward the event to features
|
||||
* @param e join event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask("processing PlayerJoinEvent", () -> TAB.getInstance().getFeatureManager().onJoin(new BukkitTabPlayer(e.getPlayer(), main.getProtocolVersion(e.getPlayer()))));
|
||||
}
|
||||
/** Platform instance */
|
||||
private final BukkitPlatform platform;
|
||||
|
||||
/**
|
||||
* Listener to PlayerChangedWorldEvent to forward the event to features
|
||||
* @param e world changed event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onWorldChange(PlayerChangedWorldEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask("processing PlayerChangedWorldEvent", () -> TAB.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName()));
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @param e
|
||||
* quit event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onQuit(PlayerQuitEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask(() -> TAB.getInstance().getFeatureManager().onQuit(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerJoinEvent to create player data and forward the event to features
|
||||
*
|
||||
* @param e
|
||||
* join event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask(() -> TAB.getInstance().getFeatureManager().onJoin(new BukkitTabPlayer(e.getPlayer(), platform.getProtocolVersion(e.getPlayer()))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerChangedWorldEvent to forward the event to features
|
||||
* @param e command preprocess event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onCommand(PlayerCommandPreprocessEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
if (TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
}
|
||||
/**
|
||||
* Listener to PlayerChangedWorldEvent to forward the event to features
|
||||
*
|
||||
* @param e
|
||||
* world changed event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onWorldChange(PlayerChangedWorldEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask(() -> TAB.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerChangedWorldEvent to forward the event to features
|
||||
*
|
||||
* @param e
|
||||
* command preprocess event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onCommand(PlayerCommandPreprocessEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
if (TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
+123
-105
@@ -2,6 +2,7 @@ package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -11,118 +12,135 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.NickCompatibility;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Pipeline injection for bukkit
|
||||
*/
|
||||
public class BukkitPipelineInjector extends PipelineInjector {
|
||||
|
||||
//nms storage
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
/** NMS data storage */
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public BukkitPipelineInjector(){
|
||||
super("packet_handler");
|
||||
channelFunction = BukkitChannelDuplexHandler::new;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public BukkitPipelineInjector(){
|
||||
super("packet_handler");
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
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 Function<TabPlayer, ChannelDuplexHandler> getChannelFunction() {
|
||||
return BukkitChannelDuplexHandler::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext context, Object packet) {
|
||||
try {
|
||||
if (TAB.getInstance().getFeatureManager().onPacketReceive(player, packet)) return;
|
||||
super.channelRead(context, packet);
|
||||
} catch (Exception e){
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
public class BukkitChannelDuplexHandler extends ChannelDuplexHandler {
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
|
||||
try {
|
||||
if (nms.PacketPlayOutPlayerInfo.isInstance(packet)) {
|
||||
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
|
||||
return;
|
||||
}
|
||||
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam != null && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
|
||||
modifyPlayers(packet);
|
||||
super.write(context, packet, channelPromise);
|
||||
return;
|
||||
}
|
||||
if (nms.PacketPlayOutScoreboardDisplayObjective.isInstance(packet)){
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, packet);
|
||||
}
|
||||
if (nms.PacketPlayOutScoreboardObjective.isInstance(packet)) {
|
||||
TAB.getInstance().getFeatureManager().onObjective(player, packet);
|
||||
}
|
||||
TAB.getInstance().getFeatureManager().onPacketSend(player, packet);
|
||||
} catch (Exception e){
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
|
||||
}
|
||||
try {
|
||||
super.write(context, packet, channelPromise);
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to forward packet " + packet.getClass().getSimpleName() + " to " + player.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all real players from team if packet does not come from TAB and reports this to override log
|
||||
* @param packetPlayOutScoreboardTeam - team packet
|
||||
* @throws ReflectiveOperationException
|
||||
* nmsGameMode
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyPlayers(Object packetPlayOutScoreboardTeam) throws ReflectiveOperationException {
|
||||
long time = System.nanoTime();
|
||||
Collection<String> players = (Collection<String>) nms.PacketPlayOutScoreboardTeam_PLAYERS.get(packetPlayOutScoreboardTeam);
|
||||
String teamName = (String) nms.PacketPlayOutScoreboardTeam_NAME.get(packetPlayOutScoreboardTeam);
|
||||
if (players == null) return;
|
||||
//creating a new list to prevent NoSuchFieldException in minecraft packet encoder when a player is removed
|
||||
Collection<String> newList = new ArrayList<>();
|
||||
for (String entry : players) {
|
||||
TabPlayer p = getPlayer(entry);
|
||||
if (p == null) {
|
||||
newList.add(entry);
|
||||
continue;
|
||||
}
|
||||
if (!((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
|
||||
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(p.getTeamName())) {
|
||||
logTeamOverride(teamName, p.getName(), p.getTeamName());
|
||||
} else {
|
||||
newList.add(entry);
|
||||
}
|
||||
}
|
||||
nms.setField(packetPlayOutScoreboardTeam, nms.PacketPlayOutScoreboardTeam_PLAYERS, newList);
|
||||
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
private TabPlayer getPlayer(String name) {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (getName(p).equals(name)) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/** Injected player */
|
||||
private final TabPlayer player;
|
||||
|
||||
|
||||
private String getName(TabPlayer p) {
|
||||
return ((NickCompatibility) TAB.getInstance().getFeatureManager().getFeature("nick")).getNickname(p);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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 {
|
||||
if (TAB.getInstance().getFeatureManager().onPacketReceive(player, packet)) return;
|
||||
super.channelRead(context, packet);
|
||||
} catch (Exception e){
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
|
||||
try {
|
||||
if (nms.PacketPlayOutPlayerInfo.isInstance(packet)) {
|
||||
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
|
||||
return;
|
||||
}
|
||||
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam != null && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
|
||||
long time = System.nanoTime();
|
||||
modifyPlayers(packet);
|
||||
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
super.write(context, packet, channelPromise);
|
||||
return;
|
||||
}
|
||||
if (nms.PacketPlayOutScoreboardDisplayObjective.isInstance(packet)){
|
||||
TAB.getInstance().getFeatureManager().onDisplayObjective(player, packet);
|
||||
}
|
||||
if (nms.PacketPlayOutScoreboardObjective.isInstance(packet)) {
|
||||
TAB.getInstance().getFeatureManager().onObjective(player, packet);
|
||||
}
|
||||
TAB.getInstance().getFeatureManager().onPacketSend(player, packet);
|
||||
} catch (Exception e){
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
|
||||
}
|
||||
try {
|
||||
super.write(context, packet, channelPromise);
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to forward packet " + packet.getClass().getSimpleName() + " to " + player.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all real players from team if packet does not come from TAB and reports this to override log
|
||||
*
|
||||
* @param packetPlayOutScoreboardTeam
|
||||
* team packet
|
||||
* @throws ReflectiveOperationException
|
||||
* nmsGameMode
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyPlayers(Object packetPlayOutScoreboardTeam) throws ReflectiveOperationException {
|
||||
int action = nms.PacketPlayOutScoreboardTeam_ACTION.getInt(packetPlayOutScoreboardTeam);
|
||||
if (action == 1 || action == 2 || action == 4) return;
|
||||
Collection<String> players = (Collection<String>) nms.PacketPlayOutScoreboardTeam_PLAYERS.get(packetPlayOutScoreboardTeam);
|
||||
String teamName = (String) nms.PacketPlayOutScoreboardTeam_NAME.get(packetPlayOutScoreboardTeam);
|
||||
if (players == null) return;
|
||||
//creating a new list to prevent NoSuchFieldException in minecraft packet encoder when a player is removed
|
||||
Collection<String> newList = new ArrayList<>();
|
||||
for (String entry : players) {
|
||||
TabPlayer p = getPlayer(entry);
|
||||
if (p == null) {
|
||||
newList.add(entry);
|
||||
continue;
|
||||
}
|
||||
if (!((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
|
||||
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(p.getTeamName())) {
|
||||
logTeamOverride(teamName, p.getName(), p.getTeamName());
|
||||
} else {
|
||||
newList.add(entry);
|
||||
}
|
||||
}
|
||||
nms.setField(packetPlayOutScoreboardTeam, nms.PacketPlayOutScoreboardTeam_PLAYERS, newList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns player with matching game profile name. This is different from
|
||||
* real name when a nick plugin changing names of players is used. If no
|
||||
* player was found, returns {@code null}.
|
||||
*
|
||||
* @param name
|
||||
* Game profile name
|
||||
* @return Player with matching game profile name
|
||||
*/
|
||||
private TabPlayer getPlayer(String name) {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p.getNickname().equals(name)) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
-178
@@ -1,201 +1,100 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
import com.earth2me.essentials.Essentials;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager;
|
||||
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.placeholders.PlaceholderRegistry;
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
|
||||
/**
|
||||
* Bukkit registry to register bukkit-only placeholders
|
||||
* Bukkit registry to register bukkit-only and universal placeholders
|
||||
*/
|
||||
public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
|
||||
|
||||
//formatter for 2 decimal places
|
||||
public final DecimalFormat decimal2 = ((DecimalFormat)NumberFormat.getNumberInstance(Locale.US));
|
||||
/** Number formatter for 2 decimal places */
|
||||
private final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
|
||||
|
||||
private final Plugin plugin;
|
||||
private Object chat; // Object because Fabric
|
||||
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
|
||||
private Object server;
|
||||
private Field recentTps;
|
||||
private Method paperTps;
|
||||
private Method paperMspt;
|
||||
private Method purpurIsAfk;
|
||||
|
||||
private Object chat;
|
||||
private final Plugin essentials = Bukkit.getPluginManager().getPlugin("Essentials");
|
||||
private Object server;
|
||||
private Field recentTps;
|
||||
private boolean paperTps;
|
||||
private boolean paperMspt;
|
||||
private boolean purpur;
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*/
|
||||
public BukkitPlaceholderRegistry() {
|
||||
numberFormat.setMaximumFractionDigits(2);
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
|
||||
RegisteredServiceProvider<?> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
|
||||
if (rspChat != null) chat = rspChat.getProvider();
|
||||
}
|
||||
try {
|
||||
server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
|
||||
recentTps = server.getClass().getField("recentTps");
|
||||
} catch (ReflectiveOperationException e) {
|
||||
//not spigot
|
||||
}
|
||||
try { paperTps = Bukkit.class.getMethod("getTPS"); } catch (NoSuchMethodException ignored) {}
|
||||
try { paperMspt = Bukkit.class.getMethod("getAverageTickTime"); } catch (NoSuchMethodException ignored) {}
|
||||
try { purpurIsAfk = Player.class.getMethod("isAfk"); } catch (NoSuchMethodException ignored) {}
|
||||
}
|
||||
|
||||
private Listener healthListener = null;
|
||||
@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]));
|
||||
} else if (recentTps != null) {
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> {
|
||||
try {
|
||||
return formatTPS(((double[]) recentTps.get(server))[0]);
|
||||
} catch (IllegalAccessException e) {
|
||||
return -1;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, -1, () -> -1);
|
||||
}
|
||||
if (paperMspt != null) {
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.MSPT, 1000, () -> numberFormat.format(Bukkit.getAverageTickTime()));
|
||||
}
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.AFK, 500, p -> {
|
||||
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
|
||||
return purpurIsAfk != null && ((Player)p.getPlayer()).isAfk();
|
||||
});
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, 1000, p -> {
|
||||
String nickname = null;
|
||||
if (essentials != null)
|
||||
nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
|
||||
return nickname == null || nickname.length() == 0 ? p.getName() : nickname;
|
||||
});
|
||||
if (chat != null) {
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, 1000, p -> ((Chat) chat).getPlayerPrefix((Player) p.getPlayer()));
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, 1000, p -> ((Chat) chat).getPlayerSuffix((Player) p.getPlayer()));
|
||||
} else {
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, -1, () -> "");
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, -1, () -> "");
|
||||
}
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.HEALTH, 100, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* @param plugin - plugin instance
|
||||
*/
|
||||
public BukkitPlaceholderRegistry(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
decimal2.applyPattern("#.##");
|
||||
try {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
|
||||
RegisteredServiceProvider<?> rspChat = Bukkit.getServicesManager().getRegistration(Class.forName("net.milkbowl.vault.chat.Chat"));
|
||||
if (rspChat != null) chat = rspChat.getProvider();
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
//modded server without vault
|
||||
}
|
||||
try {
|
||||
server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
|
||||
recentTps = server.getClass().getField("recentTps");
|
||||
} catch (ReflectiveOperationException e) {
|
||||
//not spigot
|
||||
}
|
||||
try {
|
||||
Bukkit.class.getMethod("getTPS");
|
||||
paperTps = true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
//not paper
|
||||
}
|
||||
try {
|
||||
Bukkit.class.getMethod("getAverageTickTime");
|
||||
paperMspt = true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
//not paper
|
||||
}
|
||||
try {
|
||||
Player.class.getMethod("isAfk");
|
||||
purpur = true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
//not purpur
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void registerPlaceholders(PlaceholderManager manager) {
|
||||
NumberFormat roundDown = NumberFormat.getInstance();
|
||||
roundDown.setRoundingMode(RoundingMode.DOWN);
|
||||
roundDown.setMaximumFractionDigits(2);
|
||||
manager.registerPlayerPlaceholder("%displayname%", 500, p -> ((Player) p.getPlayer()).getDisplayName());
|
||||
manager.registerPlayerPlaceholder("%vanished%", 1000, TabPlayer::isVanished);
|
||||
if (paperTps) {
|
||||
manager.registerServerPlaceholder("%tps%", 1000, () -> formatTPS(Bukkit.getTPS()[0]));
|
||||
} else if (recentTps != null) {
|
||||
manager.registerServerPlaceholder("%tps%", 1000, () -> {
|
||||
try {
|
||||
return formatTPS(((double[]) recentTps.get(server))[0]);
|
||||
} catch (IllegalAccessException e) {
|
||||
return "-1";
|
||||
}
|
||||
});
|
||||
} else {
|
||||
manager.registerServerPlaceholder("%tps%", -1, () -> "-1").enableTriggerMode();
|
||||
}
|
||||
if (paperMspt) {
|
||||
manager.registerServerPlaceholder("%mspt%", 1000, () -> roundDown.format(Bukkit.getAverageTickTime()));
|
||||
}
|
||||
manager.registerPlayerPlaceholder("%afk%", 500, p -> {
|
||||
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
|
||||
return purpur && ((Player)p.getPlayer()).isAfk();
|
||||
});
|
||||
manager.registerPlayerPlaceholder("%essentialsnick%", 1000, p -> {
|
||||
String nickname = null;
|
||||
if (essentials != null)
|
||||
nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
|
||||
return nickname == null || nickname.length() == 0 ? p.getName() : nickname;
|
||||
});
|
||||
if (chat != null) {
|
||||
manager.registerPlayerPlaceholder("%vault-prefix%", 500, p -> ((Chat) chat).getPlayerPrefix((Player) p.getPlayer()));
|
||||
manager.registerPlayerPlaceholder("%vault-suffix%", 500, p -> ((Chat) chat).getPlayerSuffix((Player) p.getPlayer()));
|
||||
} else {
|
||||
manager.registerServerPlaceholder("%vault-prefix%", -1, () -> "").enableTriggerMode();
|
||||
manager.registerServerPlaceholder("%vault-suffix%", -1, () -> "").enableTriggerMode();
|
||||
}
|
||||
registerOnlinePlaceholders(manager);
|
||||
registerHealthPlaceholder(manager);
|
||||
}
|
||||
|
||||
private String formatTPS(double tps) {
|
||||
return decimal2.format(Math.min(20, tps));
|
||||
}
|
||||
|
||||
private void registerOnlinePlaceholders(PlaceholderManager manager) {
|
||||
manager.registerPlayerPlaceholder("%online%", 1000, p -> {
|
||||
int count = 0;
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()){
|
||||
if (((Player) p.getPlayer()).canSee((Player) all.getPlayer())) count++;
|
||||
}
|
||||
return count;
|
||||
});
|
||||
manager.registerPlayerPlaceholder("%staffonline%", 2000, p -> {
|
||||
int count = 0;
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()){
|
||||
if (all.hasPermission(TabConstants.Permission.STAFF) && ((Player) p.getPlayer()).canSee((Player) all.getPlayer())) count++;
|
||||
}
|
||||
return count;
|
||||
});
|
||||
manager.registerPlayerPlaceholder("%nonstaffonline%", 2000, p -> {
|
||||
int count = 0;
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()){
|
||||
if (!all.hasPermission(TabConstants.Permission.STAFF) && ((Player) p.getPlayer()).canSee((Player) all.getPlayer())) count++;
|
||||
}
|
||||
return count;
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void registerHealthPlaceholder(PlaceholderManager manager) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 6) {
|
||||
PlayerPlaceholder health = manager.registerPlayerPlaceholder("%health%", -1, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
|
||||
health.enableTriggerMode(() -> {
|
||||
healthListener = new Listener() {
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onDamage(EntityDamageEvent e) {
|
||||
if (e.getEntity() instanceof Player) {
|
||||
Player p = (Player) e.getEntity();
|
||||
TabPlayer tabp = TAB.getInstance().getPlayer(e.getEntity().getUniqueId());
|
||||
if (tabp == null) return;
|
||||
health.updateValue(tabp, (int) Math.ceil(Math.max(p.getHealth() - e.getFinalDamage(), 0)));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onRegain(EntityRegainHealthEvent e) {
|
||||
if (e.getEntity() instanceof Player) {
|
||||
Player p = (Player) e.getEntity();
|
||||
TabPlayer tabp = TAB.getInstance().getPlayer(e.getEntity().getUniqueId());
|
||||
if (tabp == null) return;
|
||||
health.updateValue(tabp, (int) Math.ceil(Math.min(p.getHealth() + e.getAmount(), p.getMaxHealth())));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onRespawn(PlayerRespawnEvent e) {
|
||||
health.updateValue(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getPlayer().getMaxHealth());
|
||||
}
|
||||
};
|
||||
Bukkit.getPluginManager().registerEvents(healthListener, plugin);
|
||||
}, () -> HandlerList.unregisterAll(healthListener));
|
||||
}
|
||||
}
|
||||
}
|
||||
private String formatTPS(double tps) {
|
||||
return numberFormat.format(Math.min(20, tps));
|
||||
}
|
||||
}
|
||||
@@ -1,266 +1,311 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.earth2me.essentials.Essentials;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.protocol.PacketBuilder;
|
||||
import me.neznamy.tab.platforms.bukkit.event.TabLoadEvent;
|
||||
import me.neznamy.tab.platforms.bukkit.event.TabPlayerLoadEvent;
|
||||
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.TabExpansion;
|
||||
import me.neznamy.tab.platforms.bukkit.features.WitherBossBar;
|
||||
import me.neznamy.tab.platforms.bukkit.features.unlimitedtags.NameTagX;
|
||||
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.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.permission.LuckPerms;
|
||||
import me.neznamy.tab.shared.permission.None;
|
||||
import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
import me.neznamy.tab.shared.permission.UltraPermissions;
|
||||
import me.neznamy.tab.shared.placeholders.PlayerPlaceholderImpl;
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Bukkit's implementation of Platform interface
|
||||
* Implementation of Platform interface for Bukkit platform
|
||||
*/
|
||||
public class BukkitPlatform implements Platform {
|
||||
public class BukkitPlatform extends Platform {
|
||||
|
||||
//plugin instance
|
||||
private final Main plugin;
|
||||
/** Plugin instance for registering tasks and events */
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
private final BukkitPacketBuilder packetBuilder = new BukkitPacketBuilder();
|
||||
/** Variables checking presence of other plugins to hook into */
|
||||
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API);
|
||||
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
|
||||
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
|
||||
private Plugin viaVersion;
|
||||
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
|
||||
|
||||
//booleans to check plugin presence
|
||||
private boolean placeholderAPI;
|
||||
private boolean libsDisguises;
|
||||
private Plugin essentials;
|
||||
/**
|
||||
* Constructs new instance with given plugin parameter
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
*/
|
||||
public BukkitPlatform(JavaPlugin plugin) {
|
||||
super(new BukkitPacketBuilder());
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
* @param plugin - plugin instance
|
||||
*/
|
||||
public BukkitPlatform(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@Override
|
||||
public PermissionPlugin detectPermissionPlugin() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LUCKPERMS)) {
|
||||
return new LuckPerms(getPluginVersion(TabConstants.Plugin.LUCKPERMS));
|
||||
} else if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
|
||||
RegisteredServiceProvider<Permission> provider = Bukkit.getServicesManager().getRegistration(Permission.class);
|
||||
if (provider == null) return new None();
|
||||
return new Vault(provider.getProvider(), getPluginVersion(TabConstants.Plugin.VAULT));
|
||||
} else {
|
||||
return new None();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionPlugin detectPermissionPlugin() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("LuckPerms")) {
|
||||
return new LuckPerms(getPluginVersion("LuckPerms"));
|
||||
} else if (Bukkit.getPluginManager().isPluginEnabled("UltraPermissions")) {
|
||||
return new UltraPermissions(getPluginVersion("UltraPermissions"));
|
||||
} else if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
|
||||
return new Vault(Bukkit.getServicesManager().getRegistration(Permission.class).getProvider(), getPluginVersion("Vault"));
|
||||
} else {
|
||||
return new None();
|
||||
}
|
||||
}
|
||||
@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().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)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFeatures() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
|
||||
try {
|
||||
Class.forName("com.viaversion.viaversion.api.Via");
|
||||
} catch (ClassNotFoundException e) {
|
||||
TAB.getInstance().sendConsoleMessage("&c[TAB] An outdated version of ViaVersion (" + getPluginVersion("ViaVersion") + ") was detected.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&c[TAB] TAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&c[TAB] This might cause problems, such as limitations still being present for latest MC clients as well as RGB not working.", true);
|
||||
}
|
||||
}
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Tablisknu")) {
|
||||
TAB.getInstance().sendConsoleMessage("&c[TAB] Detected plugin \"Tablisknu\", which causes TAB to not work properly. Consider removing the plugin.", true);
|
||||
}
|
||||
placeholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
|
||||
libsDisguises = Bukkit.getPluginManager().isPluginEnabled("LibsDisguises");
|
||||
essentials = Bukkit.getPluginManager().getPlugin("Essentials");
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.getConfiguration().isPipelineInjection()) tab.getFeatureManager().registerFeature("injection", new BukkitPipelineInjector());
|
||||
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
|
||||
new UniversalPlaceholderRegistry().registerPlaceholders(tab.getPlaceholderManager());
|
||||
loadNameTagFeature(tab);
|
||||
tab.loadUniversalFeatures();
|
||||
if (tab.getConfiguration().getConfig().getBoolean("bossbar.enabled", false)) {
|
||||
if (tab.getServerVersion().getMinorVersion() < 9) {
|
||||
tab.getFeatureManager().registerFeature("bossbar", new WitherBossBar(plugin));
|
||||
} else {
|
||||
tab.getFeatureManager().registerFeature("bossbar", new BossBarManagerImpl());
|
||||
}
|
||||
}
|
||||
if (tab.getServerVersion().getMinorVersion() >= 9 && tab.getConfiguration().getConfig().getBoolean("fix-pet-names.enabled", false)) tab.getFeatureManager().registerFeature("petfix", new PetFix());
|
||||
if (tab.getConfiguration().getConfig().getBoolean("per-world-playerlist.enabled", false)) tab.getFeatureManager().registerFeature("pwp", new PerWorldPlayerList(plugin));
|
||||
if (placeholderAPI) {
|
||||
new TabExpansion(plugin);
|
||||
}
|
||||
for (Player p : getOnlinePlayers()) {
|
||||
tab.addPlayer(new BukkitTabPlayer(p, plugin.getProtocolVersion(p)));
|
||||
}
|
||||
}
|
||||
|
||||
private String getPluginVersion(String plugin) {
|
||||
return Bukkit.getPluginManager().getPlugin(plugin).getDescription().getVersion();
|
||||
}
|
||||
@Override
|
||||
public String getPluginVersion(String plugin) {
|
||||
Plugin pl = Bukkit.getPluginManager().getPlugin(plugin);
|
||||
return pl == null ? null : pl.getDescription().getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads NameTag feature from config
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
private void loadNameTagFeature(TAB tab) {
|
||||
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("nametagx", new NameTagX(plugin));
|
||||
} else {
|
||||
tab.getFeatureManager().registerFeature("nametag16", new NameTag());
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns online players from Bukkit API
|
||||
*
|
||||
* @return online players from Bukkit API
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Player[] getOnlinePlayers() {
|
||||
try {
|
||||
Object players = Bukkit.class.getMethod("getOnlinePlayers").invoke(null);
|
||||
if (players instanceof Player[]) {
|
||||
//1.7-
|
||||
return (Player[]) players;
|
||||
} else {
|
||||
//1.8+
|
||||
return ((Collection<Player>)players).toArray(new Player[0]);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get online players", e);
|
||||
return new Player[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of online players from Bukkit API
|
||||
* @return list of online players from Bukkit API
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Player[] getOnlinePlayers() {
|
||||
try {
|
||||
Object players = Bukkit.class.getMethod("getOnlinePlayers").invoke(null);
|
||||
if (players instanceof Player[]) {
|
||||
//1.7.x
|
||||
return (Player[]) players;
|
||||
} else {
|
||||
//1.8+
|
||||
return ((Collection<Player>)players).toArray(new Player[0]);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get online players", e);
|
||||
return new Player[0];
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void registerUnknownPlaceholder(String identifier) {
|
||||
PlaceholderManagerImpl pl = TAB.getInstance().getPlaceholderManager();
|
||||
if (identifier.startsWith("%rel_")) {
|
||||
//relational placeholder
|
||||
TAB.getInstance().getPlaceholderManager().registerRelationalPlaceholder(identifier, pl.getRelationalRefresh(identifier), (viewer, target) ->
|
||||
placeholderAPI ? PlaceholderAPI.setRelationalPlaceholders((Player) viewer.getPlayer(), (Player) target.getPlayer(), identifier) : identifier);
|
||||
} else {
|
||||
//normal placeholder
|
||||
if (identifier.startsWith("%sync:")) {
|
||||
int refresh = pl.getServerPlaceholderRefreshIntervals().getOrDefault(identifier,
|
||||
pl.getPlayerPlaceholderRefreshIntervals().getOrDefault(identifier, pl.getDefaultRefresh()));
|
||||
String syncedPlaceholder = "%" + identifier.substring(6, identifier.length()-1) + "%";
|
||||
pl.registerPlaceholder(new PlayerPlaceholderImpl(identifier, refresh, null) {
|
||||
|
||||
@Override
|
||||
public Object request(TabPlayer p) {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
long time = System.nanoTime();
|
||||
updateValue(p, placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), syncedPlaceholder) : identifier);
|
||||
TAB.getInstance().getCPUManager().addPlaceholderTime(getIdentifier(), System.nanoTime()-time);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (pl.getServerPlaceholderRefreshIntervals().containsKey(identifier)) {
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, pl.getServerPlaceholderRefreshIntervals().get(identifier), () ->
|
||||
placeholderAPI ? PlaceholderAPI.setPlaceholders(null, identifier) : identifier);
|
||||
} else {
|
||||
int refresh = pl.getPlayerPlaceholderRefreshIntervals().getOrDefault(identifier, pl.getDefaultRefresh());
|
||||
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, refresh, p ->
|
||||
placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), identifier) : identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendConsoleMessage(String message, boolean translateColors) {
|
||||
Bukkit.getConsoleSender().sendMessage(translateColors ? EnumChatFormat.color(message) : message);
|
||||
}
|
||||
@Override
|
||||
public void callLoadEvent() {
|
||||
Bukkit.getPluginManager().callEvent(new TabLoadEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerUnknownPlaceholder(String identifier) {
|
||||
PlaceholderManagerImpl pl = TAB.getInstance().getPlaceholderManager();
|
||||
if (identifier.startsWith("%rel_")) {
|
||||
//relational placeholder
|
||||
TAB.getInstance().getPlaceholderManager().registerRelationalPlaceholder(identifier, pl.getRelationalRefresh(identifier), (viewer, target) ->
|
||||
placeholderAPI ? PlaceholderAPI.setRelationalPlaceholders((Player) viewer.getPlayer(), (Player) target.getPlayer(), identifier) : identifier);
|
||||
} else {
|
||||
//normal placeholder
|
||||
if (identifier.startsWith("%sync:")) {
|
||||
int refresh = pl.getServerPlaceholderRefreshIntervals().getOrDefault(identifier,
|
||||
pl.getPlayerPlaceholderRefreshIntervals().getOrDefault(identifier, pl.getDefaultRefresh()));
|
||||
pl.registerPlaceholder(new PlayerPlaceholderImpl(identifier, refresh, null) {
|
||||
|
||||
@Override
|
||||
public Object request(TabPlayer p) {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
@Override
|
||||
public void callLoadEvent(TabPlayer player) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> Bukkit.getPluginManager().callEvent(new TabPlayerLoadEvent(player)));
|
||||
}
|
||||
|
||||
long time = System.nanoTime();
|
||||
String syncedPlaceholder = identifier.substring(6, identifier.length()-1);
|
||||
String value = placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), "%" + syncedPlaceholder + "%") : identifier;
|
||||
getLastValues().put(p.getName(), value);
|
||||
if (!getForceUpdate().contains(p.getName())) getForceUpdate().add(p.getName());
|
||||
TAB.getInstance().getCPUManager().addPlaceholderTime(getIdentifier(), System.nanoTime()-time);
|
||||
});
|
||||
String value = getLastValues().get(p.getName());
|
||||
return value == null ? identifier : value;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (pl.getServerPlaceholderRefreshIntervals().containsKey(identifier)) {
|
||||
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, pl.getServerPlaceholderRefreshIntervals().get(identifier), () ->
|
||||
placeholderAPI ? PlaceholderAPI.setPlaceholders(null, identifier) : identifier);
|
||||
} else {
|
||||
int refresh = pl.getPlayerPlaceholderRefreshIntervals().getOrDefault(identifier, pl.getDefaultRefresh());
|
||||
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, refresh, p ->
|
||||
placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), identifier) : identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns status of LibsDisguises plugin presence
|
||||
*
|
||||
* @return {@code true} if plugin is enabled, {@code false} if not
|
||||
*/
|
||||
public boolean isLibsDisguisesEnabled() {
|
||||
return libsDisguises;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerVersion() {
|
||||
return Bukkit.getBukkitVersion().split("-")[0] + " (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")";
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return plugin.getDataFolder();
|
||||
}
|
||||
/**
|
||||
* 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 void callLoadEvent() {
|
||||
Bukkit.getPluginManager().callEvent(new TabLoadEvent());
|
||||
}
|
||||
@Override
|
||||
public String getConfigName() {
|
||||
return "bukkitconfig.yml";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callLoadEvent(TabPlayer player) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> Bukkit.getPluginManager().callEvent(new TabPlayerLoadEvent(player)));
|
||||
}
|
||||
/**
|
||||
* Gets protocol version of requested player and returns it.
|
||||
*
|
||||
* @param player
|
||||
* Player to get protocol version of
|
||||
* @return protocol version of the player
|
||||
*/
|
||||
public int getProtocolVersion(Player player) {
|
||||
if (protocolSupport){
|
||||
int version = getProtocolVersionPS(player);
|
||||
//some PS versions return -1 on unsupported server versions instead of throwing exception
|
||||
if (version != -1 && version < TAB.getInstance().getServerVersion().getNetworkId()) return version;
|
||||
}
|
||||
if (viaVersion != null) {
|
||||
return getProtocolVersionVia(player, 0);
|
||||
}
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers() {
|
||||
return Bukkit.getMaxPlayers();
|
||||
}
|
||||
/**
|
||||
* Returns protocol version of requested player using ProtocolSupport
|
||||
*
|
||||
* @param player
|
||||
* Player to get protocol version of
|
||||
* @return protocol version of the player using ProtocolSupport
|
||||
*/
|
||||
private int getProtocolVersionPS(Player player){
|
||||
try {
|
||||
Object protocolVersion = Class.forName("protocolsupport.api.ProtocolSupportAPI").getMethod("getProtocolVersion", Player.class).invoke(null, player);
|
||||
int version = (int) protocolVersion.getClass().getMethod("getId").invoke(protocolVersion);
|
||||
TAB.getInstance().debug("ProtocolSupport returned protocol version " + version + " for " + player.getName() + " (online=" + player.isOnline() + ")");
|
||||
return version;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ProtocolSupport", player.getName()), e);
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLibsDisguisesEnabled() {
|
||||
return libsDisguises;
|
||||
}
|
||||
|
||||
public void setLibsDisguisesEnabled(boolean enabled) {
|
||||
libsDisguises = enabled;
|
||||
}
|
||||
/**
|
||||
* Returns protocol version of requested player using ViaVersion
|
||||
*
|
||||
* @param player
|
||||
* Player to get protocol version of
|
||||
* @return protocol version of the player using ViaVersion
|
||||
*/
|
||||
private int getProtocolVersionVia(Player player, int retryLevel){
|
||||
try {
|
||||
if (retryLevel == 10) {
|
||||
TAB.getInstance().debug("Failed to get protocol version of " + player.getName() + " after 10 retries");
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
int version = Via.getAPI().getPlayerVersion(player.getUniqueId());
|
||||
if (version == -1) {
|
||||
if (!player.isOnline()) return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
Thread.sleep(5);
|
||||
return getProtocolVersionVia(player, retryLevel + 1);
|
||||
}
|
||||
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + player.getName() + " (online=" + player.isOnline() + ")");
|
||||
return version;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return -1;
|
||||
} catch (Exception | LinkageError e) {
|
||||
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ViaVersion v%s", player.getName(), viaVersion.getDescription().getVersion()), e);
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
}
|
||||
|
||||
public Essentials getEssentials() {
|
||||
return (Essentials) essentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketBuilder getPacketBuilder() {
|
||||
return packetBuilder;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object getSkin(List<String> properties) {
|
||||
PropertyMap map = new PropertyMap();
|
||||
map.put("textures", new Property("textures", properties.get(0), properties.get(1)));
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProxy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginEnabled(String plugin) {
|
||||
return Bukkit.getPluginManager().isPluginEnabled(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigName() {
|
||||
return "bukkitconfig.yml";
|
||||
}
|
||||
/**
|
||||
* Sends console message using ConsoleCommandSender, due to
|
||||
* Paper not translating colors correctly in Logger messages
|
||||
*
|
||||
* @param message
|
||||
* Message to send
|
||||
* @param translateColors
|
||||
* Whether color codes should be translated or not
|
||||
*/
|
||||
@Override
|
||||
public void sendConsoleMessage(String message, boolean translateColors) {
|
||||
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ? EnumChatFormat.color(message) : message));
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutChat;
|
||||
import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.boss.BarFlag;
|
||||
@@ -23,7 +24,6 @@ import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.libraryaddict.disguise.DisguiseAPI;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
@@ -31,230 +31,273 @@ import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* TabPlayer for Bukkit
|
||||
* TabPlayer implementation for Bukkit platform
|
||||
*/
|
||||
public class BukkitTabPlayer extends ITabPlayer {
|
||||
|
||||
//nms handle
|
||||
private Object handle;
|
||||
/** Player's NMS handle (EntityPlayer), preloading for speed */
|
||||
private Object handle;
|
||||
|
||||
//nms player connection
|
||||
private Object playerConnection;
|
||||
|
||||
//player's visible boss bars
|
||||
private final Map<UUID, BossBar> bossBars = new HashMap<>();
|
||||
private final Map<UUID, com.viaversion.viaversion.api.legacy.bossbar.BossBar> viaBossBars = new HashMap<>();
|
||||
/** Player's connection for sending packets, preloading for speed */
|
||||
private Object playerConnection;
|
||||
|
||||
/** Bukkit BossBars the player can currently see */
|
||||
private final Map<UUID, BossBar> bossBars = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* @param p - bukkit player
|
||||
*/
|
||||
public BukkitTabPlayer(Player p, int protocolVersion){
|
||||
super(p, p.getUniqueId(), p.getName(), "N/A", p.getWorld().getName());
|
||||
try {
|
||||
handle = NMSStorage.getInstance().getHandle.invoke(player);
|
||||
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
|
||||
if (NMSStorage.getInstance().CHANNEL != null)
|
||||
channel = (Channel) NMSStorage.getInstance().CHANNEL.get(NMSStorage.getInstance().NETWORK_MANAGER.get(playerConnection));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get playerConnection or channel of " + p.getName(), e);
|
||||
}
|
||||
version = ProtocolVersion.fromNetworkId(protocolVersion);
|
||||
}
|
||||
/** ViaVersion BossBars this 1.9+ player can see on 1.8 server */
|
||||
private final Map<UUID, com.viaversion.viaversion.api.legacy.bossbar.BossBar> viaBossBars = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
long time = System.nanoTime();
|
||||
boolean value = getPlayer().hasPermission(permission);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("hasPermission", System.nanoTime()-time);
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given bukkit player and protocol version
|
||||
*
|
||||
* @param p
|
||||
* bukkit player
|
||||
* @param protocolVersion
|
||||
* Player's protocol network id
|
||||
*/
|
||||
public BukkitTabPlayer(Player p, int protocolVersion){
|
||||
super(p, p.getUniqueId(), p.getName(), "N/A", p.getWorld().getName(), protocolVersion);
|
||||
try {
|
||||
handle = NMSStorage.getInstance().getHandle.invoke(player);
|
||||
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get playerConnection of " + p.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPing() {
|
||||
try {
|
||||
int ping = NMSStorage.getInstance().PING.getInt(handle);
|
||||
if (ping > 10000 || ping < 0) ping = -1;
|
||||
return ping;
|
||||
} catch (IllegalAccessException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
Preconditions.checkNotNull(permission, "permission");
|
||||
long time = System.nanoTime();
|
||||
boolean value = getPlayer().hasPermission(permission);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("hasPermission", System.nanoTime()-time);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Object nmsPacket) {
|
||||
if (nmsPacket == null || !getPlayer().isOnline()) return;
|
||||
long time = System.nanoTime();
|
||||
try {
|
||||
if (nmsPacket instanceof PacketPlayOutBoss) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
handle((PacketPlayOutBoss) nmsPacket);
|
||||
} else {
|
||||
handleVia((PacketPlayOutBoss) nmsPacket);
|
||||
}
|
||||
} else {
|
||||
NMSStorage.getInstance().sendPacket.invoke(playerConnection, nmsPacket);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when sending " + nmsPacket.getClass().getSimpleName(), e);
|
||||
}
|
||||
TAB.getInstance().getCPUManager().addMethodTime("sendPacket", System.nanoTime()-time);
|
||||
}
|
||||
|
||||
private void handle(PacketPlayOutBoss packet) {
|
||||
BossBar bar = bossBars.get(packet.getId());
|
||||
if (bar == null && packet.getOperation() != PacketPlayOutBoss.Action.ADD) return; //no idea how
|
||||
switch (packet.getOperation()) {
|
||||
case ADD:
|
||||
if (bossBars.containsKey(packet.getId())) return;
|
||||
bar = Bukkit.createBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16),
|
||||
BarColor.valueOf(packet.getColor().name()),
|
||||
BarStyle.valueOf(packet.getOverlay().getBukkitName()));
|
||||
if (packet.isCreateWorldFog()) bar.addFlag(BarFlag.CREATE_FOG);
|
||||
if (packet.isDarkenScreen()) bar.addFlag(BarFlag.DARKEN_SKY);
|
||||
if (packet.isPlayMusic()) bar.addFlag(BarFlag.PLAY_BOSS_MUSIC);
|
||||
bar.setProgress(packet.getPct());
|
||||
bossBars.put(packet.getId(), bar);
|
||||
bar.addPlayer(getPlayer());
|
||||
break;
|
||||
case REMOVE:
|
||||
bar.removePlayer(getPlayer());
|
||||
bossBars.remove(packet.getId());
|
||||
break;
|
||||
case UPDATE_PCT:
|
||||
bar.setProgress(packet.getPct());
|
||||
break;
|
||||
case UPDATE_NAME:
|
||||
bar.setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
bar.setColor(BarColor.valueOf(packet.getColor().name()));
|
||||
bar.setStyle(BarStyle.valueOf(packet.getOverlay().getBukkitName()));
|
||||
break;
|
||||
case UPDATE_PROPERTIES:
|
||||
bar = bossBars.get(packet.getId());
|
||||
processFlag(bar, packet.isCreateWorldFog(), BarFlag.CREATE_FOG);
|
||||
processFlag(bar, packet.isDarkenScreen(), BarFlag.DARKEN_SKY);
|
||||
processFlag(bar, packet.isPlayMusic(), BarFlag.PLAY_BOSS_MUSIC);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleVia(PacketPlayOutBoss packet) {
|
||||
com.viaversion.viaversion.api.legacy.bossbar.BossBar bar;
|
||||
switch (packet.getOperation()) {
|
||||
case ADD:
|
||||
if (viaBossBars.containsKey(packet.getId())) return;
|
||||
bar = Via.getAPI().legacyAPI().createLegacyBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16),
|
||||
packet.getPct(),
|
||||
BossColor.valueOf(packet.getColor().name()),
|
||||
BossStyle.valueOf(packet.getOverlay().getBukkitName()));
|
||||
//fog missing from via API
|
||||
if (packet.isDarkenScreen()) bar.addFlag(BossFlag.DARKEN_SKY);
|
||||
if (packet.isPlayMusic()) bar.addFlag(BossFlag.PLAY_BOSS_MUSIC);
|
||||
viaBossBars.put(packet.getId(), bar);
|
||||
bar.addPlayer(getPlayer().getUniqueId());
|
||||
break;
|
||||
case REMOVE:
|
||||
viaBossBars.get(packet.getId()).removePlayer(getPlayer().getUniqueId());
|
||||
viaBossBars.remove(packet.getId());
|
||||
break;
|
||||
case UPDATE_PCT:
|
||||
viaBossBars.get(packet.getId()).setHealth(packet.getPct());
|
||||
break;
|
||||
case UPDATE_NAME:
|
||||
viaBossBars.get(packet.getId()).setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
viaBossBars.get(packet.getId()).setColor(BossColor.valueOf(packet.getColor().name()));
|
||||
viaBossBars.get(packet.getId()).setStyle(BossStyle.valueOf(packet.getOverlay().getBukkitName()));
|
||||
break;
|
||||
case UPDATE_PROPERTIES:
|
||||
bar = viaBossBars.get(packet.getId());
|
||||
//fog missing from via API
|
||||
processFlagVia(bar, packet.isDarkenScreen(), BossFlag.DARKEN_SKY);
|
||||
processFlagVia(bar, packet.isPlayMusic(), BossFlag.PLAY_BOSS_MUSIC);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void processFlag(BossBar bar, boolean targetValue, BarFlag flag) {
|
||||
if (targetValue) {
|
||||
if (!bar.hasFlag(flag)) {
|
||||
bar.addFlag(flag);
|
||||
}
|
||||
} else {
|
||||
if (bar.hasFlag(flag)) {
|
||||
bar.removeFlag(flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processFlagVia(com.viaversion.viaversion.api.legacy.bossbar.BossBar bar, boolean targetValue, BossFlag flag) {
|
||||
if (targetValue) {
|
||||
if (!bar.hasFlag(flag)) {
|
||||
bar.addFlag(flag);
|
||||
}
|
||||
} else {
|
||||
if (bar.hasFlag(flag)) {
|
||||
bar.removeFlag(flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int getPing() {
|
||||
try {
|
||||
int ping = NMSStorage.getInstance().PING.getInt(handle);
|
||||
if (ping > 10000 || ping < 0) ping = -1;
|
||||
return ping;
|
||||
} catch (IllegalAccessException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInvisibilityPotion() {
|
||||
return getPlayer().hasPotionEffect(PotionEffectType.INVISIBILITY);
|
||||
}
|
||||
@Override
|
||||
public void sendPacket(Object nmsPacket) {
|
||||
if (nmsPacket == null || !getPlayer().isOnline()) return;
|
||||
long time = System.nanoTime();
|
||||
try {
|
||||
if (nmsPacket instanceof PacketPlayOutBoss) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
handle((PacketPlayOutBoss) nmsPacket);
|
||||
} else {
|
||||
handleVia((PacketPlayOutBoss) nmsPacket);
|
||||
}
|
||||
} else if (nmsPacket instanceof PacketPlayOutChat) {
|
||||
getPlayer().sendMessage(((PacketPlayOutChat) nmsPacket).getMessage().toLegacyText());
|
||||
} else {
|
||||
NMSStorage.getInstance().sendPacket.invoke(playerConnection, nmsPacket);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when sending " + nmsPacket.getClass().getSimpleName(), e);
|
||||
}
|
||||
TAB.getInstance().getCPUManager().addMethodTime("sendPacket", System.nanoTime()-time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisguised() {
|
||||
try {
|
||||
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).isLibsDisguisesEnabled()) return false;
|
||||
return DisguiseAPI.isDisguised(getPlayer());
|
||||
} catch (NoClassDefFoundError | ExceptionInInitializerError e) {
|
||||
//java.lang.NoClassDefFoundError: Could not initialize class me.libraryaddict.disguise.DisguiseAPI
|
||||
TAB.getInstance().getErrorManager().printError("Failed to check disguise status using LibsDisguises", e);
|
||||
((BukkitPlatform)TAB.getInstance().getPlatform()).setLibsDisguisesEnabled(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handles PacketPlayOutBoss packet send request using Bukkit API,
|
||||
* since the API offers everything we need and makes us not need to
|
||||
* deal with NMS code at all.
|
||||
*
|
||||
* @param packet
|
||||
* packet request to handle using Bukkit API
|
||||
*/
|
||||
private void handle(PacketPlayOutBoss packet) {
|
||||
BossBar bar = bossBars.get(packet.getId());
|
||||
if (packet.getAction() == PacketPlayOutBoss.Action.ADD) {
|
||||
if (bossBars.containsKey(packet.getId())) return;
|
||||
bar = Bukkit.createBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16),
|
||||
BarColor.valueOf(packet.getColor().name()),
|
||||
BarStyle.valueOf(packet.getOverlay().getBukkitName()));
|
||||
if (packet.isCreateWorldFog()) bar.addFlag(BarFlag.CREATE_FOG);
|
||||
if (packet.isDarkenScreen()) bar.addFlag(BarFlag.DARKEN_SKY);
|
||||
if (packet.isPlayMusic()) bar.addFlag(BarFlag.PLAY_BOSS_MUSIC);
|
||||
bar.setProgress(packet.getPct());
|
||||
bossBars.put(packet.getId(), bar);
|
||||
bar.addPlayer(getPlayer());
|
||||
return;
|
||||
}
|
||||
if (bar == null) return; //no idea how
|
||||
switch (packet.getAction()) {
|
||||
case REMOVE:
|
||||
bar.removePlayer(getPlayer());
|
||||
bossBars.remove(packet.getId());
|
||||
break;
|
||||
case UPDATE_PCT:
|
||||
bar.setProgress(packet.getPct());
|
||||
break;
|
||||
case UPDATE_NAME:
|
||||
bar.setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
bar.setColor(BarColor.valueOf(packet.getColor().name()));
|
||||
bar.setStyle(BarStyle.valueOf(packet.getOverlay().getBukkitName()));
|
||||
break;
|
||||
case UPDATE_PROPERTIES:
|
||||
bar = bossBars.get(packet.getId());
|
||||
processFlag(bar, packet.isCreateWorldFog(), BarFlag.CREATE_FOG);
|
||||
processFlag(bar, packet.isDarkenScreen(), BarFlag.DARKEN_SKY);
|
||||
processFlag(bar, packet.isPlayMusic(), BarFlag.PLAY_BOSS_MUSIC);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSkin() {
|
||||
try {
|
||||
return ((GameProfile)NMSStorage.getInstance().getProfile.invoke(handle)).getProperties();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get skin of " + getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handles PacketPlayOutBoss packet request for 1.9+ clients on
|
||||
* 1.8 servers using ViaVersion API instead of using Wither.
|
||||
*
|
||||
* @param packet
|
||||
* packet request to handle using ViaVersion API
|
||||
*/
|
||||
private void handleVia(PacketPlayOutBoss packet) {
|
||||
com.viaversion.viaversion.api.legacy.bossbar.BossBar bar;
|
||||
switch (packet.getAction()) {
|
||||
case ADD:
|
||||
if (viaBossBars.containsKey(packet.getId())) return;
|
||||
bar = Via.getAPI().legacyAPI().createLegacyBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16),
|
||||
packet.getPct(),
|
||||
BossColor.valueOf(packet.getColor().name()),
|
||||
BossStyle.valueOf(packet.getOverlay().getBukkitName()));
|
||||
//fog missing from via API
|
||||
if (packet.isDarkenScreen()) bar.addFlag(BossFlag.DARKEN_SKY);
|
||||
if (packet.isPlayMusic()) bar.addFlag(BossFlag.PLAY_BOSS_MUSIC);
|
||||
viaBossBars.put(packet.getId(), bar);
|
||||
bar.addPlayer(getPlayer().getUniqueId());
|
||||
break;
|
||||
case REMOVE:
|
||||
viaBossBars.get(packet.getId()).removePlayer(getPlayer().getUniqueId());
|
||||
viaBossBars.remove(packet.getId());
|
||||
break;
|
||||
case UPDATE_PCT:
|
||||
viaBossBars.get(packet.getId()).setHealth(packet.getPct());
|
||||
break;
|
||||
case UPDATE_NAME:
|
||||
viaBossBars.get(packet.getId()).setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
viaBossBars.get(packet.getId()).setColor(BossColor.valueOf(packet.getColor().name()));
|
||||
viaBossBars.get(packet.getId()).setStyle(BossStyle.valueOf(packet.getOverlay().getBukkitName()));
|
||||
break;
|
||||
case UPDATE_PROPERTIES:
|
||||
bar = viaBossBars.get(packet.getId());
|
||||
//fog missing from via API
|
||||
processFlagVia(bar, packet.isDarkenScreen(), BossFlag.DARKEN_SKY);
|
||||
processFlagVia(bar, packet.isPlayMusic(), BossFlag.PLAY_BOSS_MUSIC);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return (Player) player;
|
||||
}
|
||||
/**
|
||||
* Sets BossBar flag to requested target value.
|
||||
*
|
||||
* @param bar
|
||||
* BossBar to set flag of
|
||||
* @param targetValue
|
||||
* Target value of the flag
|
||||
* @param flag
|
||||
* Flag to set value of
|
||||
*/
|
||||
private void processFlag(BossBar bar, boolean targetValue, BarFlag flag) {
|
||||
if (targetValue && !bar.hasFlag(flag)) bar.addFlag(flag);
|
||||
if (!targetValue && bar.hasFlag(flag)) bar.removeFlag(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return getPlayer().isOnline();
|
||||
}
|
||||
/**
|
||||
* Sets BossBar flag to requested target value.
|
||||
*
|
||||
* @param bar
|
||||
* BossBar to set flag of
|
||||
* @param targetValue
|
||||
* Target value of the flag
|
||||
* @param flag
|
||||
* Flag to set value of
|
||||
*/
|
||||
private void processFlagVia(com.viaversion.viaversion.api.legacy.bossbar.BossBar bar, boolean targetValue, BossFlag flag) {
|
||||
if (targetValue && !bar.hasFlag(flag)) bar.addFlag(flag);
|
||||
if (!targetValue && bar.hasFlag(flag)) bar.removeFlag(flag);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
@Override
|
||||
public boolean hasInvisibilityPotion() {
|
||||
return getPlayer().hasPotionEffect(PotionEffectType.INVISIBILITY);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public int getGamemode() {
|
||||
return getPlayer().getGameMode().getValue();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean isDisguised() {
|
||||
try {
|
||||
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).isLibsDisguisesEnabled()) return false;
|
||||
return DisguiseAPI.isDisguised(getPlayer());
|
||||
} catch (LinkageError e) {
|
||||
//java.lang.NoClassDefFoundError: Could not initialize class me.libraryaddict.disguise.DisguiseAPI
|
||||
TAB.getInstance().getErrorManager().printError("Failed to check disguise status using LibsDisguises", e);
|
||||
((BukkitPlatform)TAB.getInstance().getPlatform()).setLibsDisguisesEnabled(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Skin getSkin() {
|
||||
try {
|
||||
Collection<Property> col = ((GameProfile)NMSStorage.getInstance().getProfile.invoke(handle)).getProperties().get("textures");
|
||||
if (col.isEmpty()) return null; //offline mode
|
||||
Property property = col.iterator().next();
|
||||
return new Skin(property.getValue(), property.getSignature());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get skin of " + getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return (Player) player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return getPlayer().isOnline();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public int getGamemode() {
|
||||
return getPlayer().getGameMode().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel() {
|
||||
try {
|
||||
if (NMSStorage.getInstance().CHANNEL != null)
|
||||
return (Channel) NMSStorage.getInstance().CHANNEL.get(NMSStorage.getInstance().NETWORK_MANAGER.get(playerConnection));
|
||||
} catch (IllegalAccessException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get channel of " + getName(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,14 @@ package me.neznamy.tab.platforms.bukkit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
@@ -28,161 +23,105 @@ import org.jetbrains.annotations.NotNull;
|
||||
* Main class for Bukkit platform
|
||||
*/
|
||||
public class Main extends JavaPlugin {
|
||||
|
||||
private boolean viaVersion;
|
||||
private boolean protocolSupport;
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&7[TAB] Server version: " + Bukkit.getBukkitVersion().split("-")[0] + " (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")"));
|
||||
if (!isVersionSupported()){
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
viaVersion = Bukkit.getPluginManager().isPluginEnabled("ViaVersion");
|
||||
protocolSupport = Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport");
|
||||
TAB.setInstance(new TAB(new BukkitPlatform(this), ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0])));
|
||||
if (TAB.getInstance().getServerVersion() == ProtocolVersion.UNKNOWN) {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Unknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
|
||||
}
|
||||
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(this), this);
|
||||
TABCommand command = new TABCommand();
|
||||
Bukkit.getPluginCommand("tab").setExecutor(command);
|
||||
Bukkit.getPluginCommand("tab").setTabCompleter(command);
|
||||
TAB.getInstance().load();
|
||||
Metrics metrics = new Metrics(this, 5304);
|
||||
metrics.addCustomChart(new SimplePie("unlimited_nametag_mode_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx") ? "Yes" : "No"));
|
||||
metrics.addCustomChart(new SimplePie("placeholderapi", () -> Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI") ? "Yes" : "No"));
|
||||
metrics.addCustomChart(new SimplePie("permission_system", () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
|
||||
metrics.addCustomChart(new SimplePie("server_version", () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
//null check due to L23 return making L30 not run
|
||||
if (TAB.getInstance() != null) TAB.getInstance().unload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all used NMS classes, constructors, fields and methods and returns true if everything went successfully and version is marked as compatible
|
||||
* @return true if compatible, false if not
|
||||
*/
|
||||
private boolean isVersionSupported(){
|
||||
List<String> supportedVersions = Arrays.asList(
|
||||
"v1_5_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3",
|
||||
"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");
|
||||
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
NMSStorage.setInstance(new NMSStorage());
|
||||
if (supportedVersions.contains(serverPackage)) {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&7[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 version. This jar has only been tested on 1.5.x - 1.18. Disabling just to stay safe."));
|
||||
}
|
||||
} catch (Exception 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)"));
|
||||
Bukkit.getConsoleSender().sendMessage(ex.getClass().getName() + ": " + ex.getMessage());
|
||||
for (StackTraceElement e : ex.getStackTrace()) {
|
||||
Bukkit.getConsoleSender().sendMessage("\t" + e.toString());
|
||||
}
|
||||
} else {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.x - 1.18. Disabling."));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets protocol version and returns it
|
||||
* @return protocol version of this player
|
||||
*/
|
||||
public int getProtocolVersion(Player player) {
|
||||
if (protocolSupport){
|
||||
int version = getProtocolVersionPS(player);
|
||||
//some PS versions return -1 on unsupported server versions instead of throwing exception
|
||||
if (version != -1 && version < TAB.getInstance().getServerVersion().getNetworkId()) return version;
|
||||
}
|
||||
if (viaVersion) {
|
||||
return getProtocolVersionVia(player, 0);
|
||||
}
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
@Override
|
||||
public void onEnable(){
|
||||
String version = Bukkit.getBukkitVersion().split("-")[0];
|
||||
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Server version: " + version + " (" + serverPackage + ")"));
|
||||
if (!isVersionSupported()){
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
BukkitPlatform platform = new BukkitPlatform(this);
|
||||
TAB.setInstance(new TAB(platform, ProtocolVersion.fromFriendlyName(version), version + " (" + serverPackage + ")", getDataFolder(), getLogger()));
|
||||
if (TAB.getInstance().getServerVersion() == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Unknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
|
||||
}
|
||||
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(platform), this);
|
||||
TAB.getInstance().load();
|
||||
Metrics metrics = new Metrics(this, 5304);
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.UNLIMITED_NAME_TAG_MODE_ENABLED, () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PLACEHOLDER_API, () -> Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API) ? "Yes" : "No"));
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM, () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.SERVER_VERSION, () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
|
||||
PluginCommand cmd = Bukkit.getPluginCommand("tab");
|
||||
if (cmd == null) return;
|
||||
TABCommand command = new TABCommand();
|
||||
cmd.setExecutor(command);
|
||||
cmd.setTabCompleter(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns protocol version of this player using ProtocolSupport
|
||||
* @return protocol version of this player using ProtocolSupport
|
||||
*/
|
||||
private int getProtocolVersionPS(Player player){
|
||||
try {
|
||||
Object protocolVersion = Class.forName("protocolsupport.api.ProtocolSupportAPI").getMethod("getProtocolVersion", Player.class).invoke(null, player);
|
||||
int version = (int) protocolVersion.getClass().getMethod("getId").invoke(protocolVersion);
|
||||
TAB.getInstance().debug("ProtocolSupport returned protocol version " + version + " for " + player.getName() + "(online=" + player.isOnline() + ")");
|
||||
return version;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ProtocolSupport", player.getName()), e);
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onDisable() {
|
||||
//null check due to L34 return making L37 not run
|
||||
if (TAB.getInstance() != null) TAB.getInstance().unload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all used NMS classes, constructors, fields and methods.
|
||||
* Returns {@code true} if everything went successfully and version is marked as compatible,
|
||||
* {@code false} if anything went wrong or version is not marked as compatible.
|
||||
*
|
||||
* @return {@code true} if server version is compatible, {@code false} if not
|
||||
*/
|
||||
private boolean isVersionSupported(){
|
||||
List<String> supportedVersions = Arrays.asList(
|
||||
"v1_5_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3",
|
||||
"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");
|
||||
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
NMSStorage.setInstance(new NMSStorage());
|
||||
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.18.2. Disabling just to stay safe."));
|
||||
}
|
||||
} catch (Exception 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);
|
||||
} else {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.x - 1.18.2. Disabling."));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns protocol version of this player using ViaVersion
|
||||
* @return protocol version of this player using ViaVersion
|
||||
*/
|
||||
private int getProtocolVersionVia(Player player, int retryLevel){
|
||||
try {
|
||||
if (retryLevel == 10) {
|
||||
TAB.getInstance().debug("Failed to get protocol version of " + player.getName() + " after 10 retries");
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
int version = Via.getAPI().getPlayerVersion(player.getUniqueId());
|
||||
if (version == -1) {
|
||||
if (!player.isOnline()) return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
Thread.sleep(5);
|
||||
return getProtocolVersionVia(player, retryLevel + 1);
|
||||
}
|
||||
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + player.getName() + "(online=" + player.isOnline() + ")");
|
||||
return version;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return -1;
|
||||
} catch (Exception | NoClassDefFoundError e) {
|
||||
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ViaVersion v%s", player.getName(), Bukkit.getPluginManager().getPlugin("ViaVersion").getDescription().getVersion()), e);
|
||||
return TAB.getInstance().getServerVersion().getNetworkId();
|
||||
}
|
||||
}
|
||||
|
||||
public static class TABCommand implements CommandExecutor, TabCompleter {
|
||||
/**
|
||||
* Command handler for /tab command
|
||||
*/
|
||||
private static class TABCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
if (TAB.getInstance().isDisabled()) {
|
||||
for (String message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
|
||||
sender.sendMessage(EnumChatFormat.color(message));
|
||||
}
|
||||
} else {
|
||||
TabPlayer p = null;
|
||||
if (sender instanceof Player) {
|
||||
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
|
||||
if (p == null) return true; //player not loaded correctly
|
||||
}
|
||||
TAB.getInstance().getCommand().execute(p, args);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
if (TAB.getInstance().isDisabled()) {
|
||||
for (String message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
|
||||
sender.sendMessage(EnumChatFormat.color(message));
|
||||
}
|
||||
} else {
|
||||
TabPlayer p = null;
|
||||
if (sender instanceof Player) {
|
||||
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
|
||||
if (p == null) return true; //player not loaded correctly
|
||||
}
|
||||
TAB.getInstance().getCommand().execute(p, args);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
TabPlayer p = null;
|
||||
if (sender instanceof Player) {
|
||||
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
|
||||
if (p == null) return new ArrayList<>(); //player not loaded correctly
|
||||
}
|
||||
return TAB.getInstance().getCommand().complete(p, args);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
|
||||
TabPlayer p = null;
|
||||
if (sender instanceof Player) {
|
||||
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
|
||||
if (p == null) return new ArrayList<>(); //player not loaded correctly
|
||||
}
|
||||
return TAB.getInstance().getCommand().complete(p, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,22 +2,24 @@ package me.neznamy.tab.platforms.bukkit.event;
|
||||
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers(){
|
||||
return getHandlerList();
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public HandlerList getHandlers(){
|
||||
return getHandlerList();
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
+23
-21
@@ -4,31 +4,33 @@ import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* 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 static final HandlerList handlers = new HandlerList();
|
||||
|
||||
private final TabPlayer player;
|
||||
|
||||
public TabPlayerLoadEvent(TabPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers(){
|
||||
return getHandlerList();
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public TabPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* TAB's expansion for PlaceholderAPI
|
||||
*/
|
||||
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion {
|
||||
|
||||
/** Map holding all placeholder values for all players */
|
||||
private final Map<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean persist(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getAuthor(){
|
||||
return "NEZNAMY";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getIdentifier(){
|
||||
return "tab";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getVersion() {
|
||||
return TabConstants.PLUGIN_VERSION;
|
||||
}
|
||||
|
||||
@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 TAB.getInstance().getPlaceholderManager().findReplacement(placeholder, output);
|
||||
}
|
||||
if (identifier.startsWith("placeholder_")) {
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", TAB.getInstance().getPlaceholderManager());
|
||||
}
|
||||
if (player == null) return "<Player cannot be null>";
|
||||
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
|
||||
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
|
||||
return values.get(p).get(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(TabPlayer player, String key, String value) {
|
||||
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
|
||||
}
|
||||
}
|
||||
+73
-71
@@ -23,76 +23,78 @@ import me.neznamy.tab.shared.TAB;
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PerWorldPlayerList extends TabFeature implements Listener {
|
||||
|
||||
//plugin instance
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
//config options
|
||||
private final boolean allowBypass = TAB.getInstance().getConfiguration().getConfig().getBoolean("per-world-playerlist.allow-bypass-permission", false);
|
||||
private final List<String> ignoredWorlds = TAB.getInstance().getConfiguration().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
|
||||
private final Map<String, List<String>> sharedWorlds = TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
* @param plugin - plugin instance
|
||||
*/
|
||||
public PerWorldPlayerList(JavaPlugin plugin) {
|
||||
super("Per world PlayerList", null);
|
||||
this.plugin = plugin;
|
||||
TAB.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getOnlinePlayers().forEach(this::checkPlayer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(){
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
for (Player pl : Bukkit.getOnlinePlayers()) {
|
||||
p.showPlayer(pl);
|
||||
}
|
||||
}
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
long time = System.nanoTime();
|
||||
checkPlayer(e.getPlayer());
|
||||
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldChange(PlayerChangedWorldEvent e) {
|
||||
long time = System.nanoTime();
|
||||
checkPlayer(e.getPlayer());
|
||||
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
private void checkPlayer(Player p) {
|
||||
for (Player all : Bukkit.getOnlinePlayers()){
|
||||
if (all == p) continue;
|
||||
if (!shouldSee(p, all) && p.canSee(all)) p.hidePlayer(all);
|
||||
if (shouldSee(p, all) && !p.canSee(all)) p.showPlayer(all);
|
||||
if (!shouldSee(all, p) && all.canSee(p)) all.hidePlayer(p);
|
||||
if (shouldSee(all, p) && !all.canSee(p)) all.showPlayer(p);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
String viewerWorldGroup = viewer.getWorld().getName() + "-default"; //preventing unwanted behavior when some group is called exactly like a world
|
||||
String targetWorldGroup = target.getWorld().getName() + "-default";
|
||||
for (Entry<String, List<String>> group : sharedWorlds.entrySet()) {
|
||||
if (group.getValue() != null) {
|
||||
if (group.getValue().contains(viewer.getWorld().getName())) viewerWorldGroup = group.getKey();
|
||||
if (group.getValue().contains(target.getWorld().getName())) targetWorldGroup = group.getKey();
|
||||
}
|
||||
}
|
||||
return viewerWorldGroup.equals(targetWorldGroup);
|
||||
}
|
||||
//plugin instance
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
//config options
|
||||
private final boolean allowBypass = TAB.getInstance().getConfiguration().getConfig().getBoolean("per-world-playerlist.allow-bypass-permission", false);
|
||||
private final List<String> ignoredWorlds = TAB.getInstance().getConfiguration().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
|
||||
private final Map<String, List<String>> sharedWorlds = TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
*/
|
||||
public PerWorldPlayerList(JavaPlugin plugin) {
|
||||
super("Per world PlayerList", null);
|
||||
this.plugin = plugin;
|
||||
TAB.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getOnlinePlayers().forEach(this::checkPlayer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(){
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
for (Player pl : Bukkit.getOnlinePlayers()) {
|
||||
p.showPlayer(pl);
|
||||
}
|
||||
}
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
long time = System.nanoTime();
|
||||
checkPlayer(e.getPlayer());
|
||||
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldChange(PlayerChangedWorldEvent e) {
|
||||
long time = System.nanoTime();
|
||||
checkPlayer(e.getPlayer());
|
||||
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
private void checkPlayer(Player p) {
|
||||
for (Player all : Bukkit.getOnlinePlayers()){
|
||||
if (all == p) continue;
|
||||
if (!shouldSee(p, all) && p.canSee(all)) p.hidePlayer(all);
|
||||
if (shouldSee(p, all) && !p.canSee(all)) p.showPlayer(all);
|
||||
if (!shouldSee(all, p) && all.canSee(p)) all.hidePlayer(p);
|
||||
if (shouldSee(all, p) && !all.canSee(p)) all.showPlayer(p);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
String viewerWorldGroup = viewer.getWorld().getName() + "-default"; //preventing unwanted behavior when some group is called exactly like a world
|
||||
String targetWorldGroup = target.getWorld().getName() + "-default";
|
||||
for (Entry<String, List<String>> group : sharedWorlds.entrySet()) {
|
||||
if (group.getValue() != null) {
|
||||
if (group.getValue().contains(viewer.getWorld().getName())) viewerWorldGroup = group.getKey();
|
||||
if (group.getValue().contains(target.getWorld().getName())) targetWorldGroup = group.getKey();
|
||||
}
|
||||
}
|
||||
return viewerWorldGroup.equals(targetWorldGroup);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -13,123 +10,131 @@ import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* A feature to disable minecraft 1.9+ feature making tamed animals with custom names copy NameTag properties of their owner
|
||||
* This is achieved by listening to entity spawn (<1.15) / entity metadata packets and removing owner field from the DataWatcher list
|
||||
* Since 1.16 this results in client sending entity use packet twice, so we must cancel the 2nd one to prevent double toggle
|
||||
* A feature to disable minecraft 1.9+ feature making tamed animals
|
||||
* with custom names copy NameTag properties of their owner.
|
||||
* This is achieved by listening to entity spawn (<1.15) / entity metadata
|
||||
* packets and removing owner field from the DataWatcher list.
|
||||
* Since 1.16 this results in client sending entity use packet twice,
|
||||
* so we must cancel the 2nd one to prevent double toggle.
|
||||
*/
|
||||
public class PetFix extends TabFeature {
|
||||
|
||||
//nms storage
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
//nms storage
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
|
||||
//DataWatcher position of pet owner field
|
||||
private final int petOwnerPosition = getPetOwnerPosition();
|
||||
//DataWatcher position of pet owner field
|
||||
private final int petOwnerPosition = getPetOwnerPosition();
|
||||
|
||||
//logger of last interacts to prevent feature not working on 1.16
|
||||
private final Map<String, Long> lastInteractFix = new HashMap<>();
|
||||
//logger of last interacts to prevent feature not working on 1.16
|
||||
private final WeakHashMap<TabPlayer, Long> lastInteractFix = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*/
|
||||
public PetFix() {
|
||||
super("Pet name fix", null);
|
||||
TAB.getInstance().debug("Loaded PetFix feature");
|
||||
}
|
||||
/**
|
||||
* Since 1.16, client sends interact packet twice for entities affected
|
||||
* by removed owner field. Because of that, we need to cancel the duplicated
|
||||
* packet to avoid double toggle and preventing the entity from getting
|
||||
* its pose changed. The duplicated packet is usually sent instantly in the
|
||||
* same millisecond, however, when installing ProtocolLib with MyPet, the delay
|
||||
* is up to 3 ticks. When holding right-click on an entity, interact is sent every
|
||||
* 200 milliseconds, which is the value we should not go above. Optimal value is
|
||||
* therefore between 150 and 200 milliseconds.
|
||||
*/
|
||||
private static final int INTERACT_COOLDOWN = 160;
|
||||
|
||||
/**
|
||||
* Returns position of pet owner field based on server version
|
||||
* @return position of pet owner field based on server version
|
||||
*/
|
||||
private int getPetOwnerPosition() {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
//1.17.x, 1.18.x
|
||||
return 18;
|
||||
} else if (nms.getMinorVersion() >= 15) {
|
||||
//1.15.x, 1.16.x
|
||||
return 17;
|
||||
} else if (nms.getMinorVersion() >= 14) {
|
||||
//1.14.x
|
||||
return 16;
|
||||
} else if (nms.getMinorVersion() >= 10) {
|
||||
//1.10.x - 1.13.x
|
||||
return 14;
|
||||
} else {
|
||||
//1.9.x
|
||||
return 13;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*/
|
||||
public PetFix() {
|
||||
super("Pet name fix", null);
|
||||
TAB.getInstance().debug("Loaded PetFix feature");
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a packet if previous one arrived with no delay to prevent double toggle on 1.16
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
@Override
|
||||
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
|
||||
if (nms.PacketPlayInUseEntity.isInstance(packet)) {
|
||||
if (lastInteractFix.containsKey(sender.getName()) && (System.currentTimeMillis() - lastInteractFix.get(sender.getName()) < 5)) {
|
||||
//last interact packet was sent right now, cancelling to prevent double-toggle due to this feature enabled
|
||||
return true;
|
||||
}
|
||||
if (isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
|
||||
//this is the first packet, saving player so the next packet can be cancelled
|
||||
lastInteractFix.put(sender.getName(), System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Returns position of pet owner field based on server version
|
||||
*
|
||||
* @return position of pet owner field based on server version
|
||||
*/
|
||||
private int getPetOwnerPosition() {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
//1.17.x, 1.18.x
|
||||
return 18;
|
||||
} else if (nms.getMinorVersion() >= 15) {
|
||||
//1.15.x, 1.16.x
|
||||
return 17;
|
||||
} else if (nms.getMinorVersion() >= 14) {
|
||||
//1.14.x
|
||||
return 16;
|
||||
} else if (nms.getMinorVersion() >= 10) {
|
||||
//1.10.x - 1.13.x
|
||||
return 14;
|
||||
} else {
|
||||
//1.9.x
|
||||
return 13;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInteract(Object action) {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
return nms.PacketPlayInUseEntity$d.isInstance(action);
|
||||
} else {
|
||||
return action.toString().equals("INTERACT");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Cancels a packet if previous one arrived with no delay to prevent double toggle since 1.16
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
@Override
|
||||
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
|
||||
if (nms.PacketPlayInUseEntity.isInstance(packet) && isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
|
||||
if (System.currentTimeMillis() - lastInteractFix.getOrDefault(sender, 0L) < INTERACT_COOLDOWN) {
|
||||
//last interact packet was sent right now, cancelling to prevent double-toggle due to this feature enabled
|
||||
return true;
|
||||
}
|
||||
//this is the first packet, saving player so the next packet can be cancelled
|
||||
lastInteractFix.put(sender, System.currentTimeMillis());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes pet owner field from DataWatcher
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
if (nms.PacketPlayOutEntityMetadata.isInstance(packet)) {
|
||||
Object removedEntry = null;
|
||||
List<Object> items = (List<Object>) nms.PacketPlayOutEntityMetadata_LIST.get(packet);
|
||||
if (items == null) return;
|
||||
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);
|
||||
if (value instanceof java.util.Optional || value instanceof com.google.common.base.Optional) {
|
||||
removedEntry = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ConcurrentModificationException e) {
|
||||
//no idea how can this list change in another thread since it's created for the packet but whatever, try again
|
||||
onPacketSend(receiver, packet);
|
||||
}
|
||||
if (removedEntry != null) items.remove(removedEntry);
|
||||
} else if (nms.PacketPlayOutSpawnEntityLiving.isInstance(packet) && nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER != null) {
|
||||
//<1.15
|
||||
DataWatcher watcher = DataWatcher.fromNMS(nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.get(packet));
|
||||
DataWatcherItem petOwner = watcher.getItem(petOwnerPosition);
|
||||
if (petOwner != null && (petOwner.getValue() instanceof java.util.Optional || petOwner.getValue() instanceof com.google.common.base.Optional)) {
|
||||
watcher.removeValue(petOwnerPosition);
|
||||
nms.setField(packet, nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER, watcher.toNMS());
|
||||
}
|
||||
}
|
||||
}
|
||||
private boolean isInteract(Object action) {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
return nms.PacketPlayInUseEntity$d.isInstance(action);
|
||||
} else {
|
||||
return action.toString().equals("INTERACT");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removing player data to not have a memory leak
|
||||
*/
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
lastInteractFix.remove(disconnectedPlayer.getName());
|
||||
}
|
||||
/**
|
||||
* Removes pet owner field from DataWatcher
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
if (nms.PacketPlayOutEntityMetadata.isInstance(packet)) {
|
||||
Object removedEntry = null;
|
||||
List<Object> items = (List<Object>) nms.PacketPlayOutEntityMetadata_LIST.get(packet);
|
||||
if (items == null) return;
|
||||
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);
|
||||
if (value instanceof Optional || value instanceof com.google.common.base.Optional) {
|
||||
removedEntry = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ConcurrentModificationException e) {
|
||||
//no idea how can this list change in another thread since it's created for the packet but whatever, try again
|
||||
onPacketSend(receiver, packet);
|
||||
}
|
||||
if (removedEntry != null) items.remove(removedEntry);
|
||||
} else if (nms.PacketPlayOutSpawnEntityLiving.isInstance(packet) && nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER != null) {
|
||||
//<1.15
|
||||
DataWatcher watcher = DataWatcher.fromNMS(nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.get(packet));
|
||||
DataWatcherItem petOwner = watcher.getItem(petOwnerPosition);
|
||||
if (petOwner != null && (petOwner.getValue() instanceof Optional || petOwner.getValue() instanceof com.google.common.base.Optional)) {
|
||||
watcher.removeValue(petOwnerPosition);
|
||||
nms.setField(packet, nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER, watcher.toNMS());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.neznamy.tab.api.Property;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.bossbar.BossBarManager;
|
||||
import me.neznamy.tab.api.scoreboard.Scoreboard;
|
||||
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* TAB's expansion for PlaceholderAPI
|
||||
*/
|
||||
public class TabExpansion extends PlaceholderExpansion {
|
||||
|
||||
//plugin to take plugin.yml data from
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
/**
|
||||
* Constructs new instance of the class and registers it
|
||||
* @param plugin - plugin to take plugin.yml data from
|
||||
*/
|
||||
public TabExpansion(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
register();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRegister(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getAuthor(){
|
||||
return plugin.getDescription().getAuthors().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getIdentifier(){
|
||||
return "tab";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getVersion(){
|
||||
return plugin.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onPlaceholderRequest(Player player, @NotNull String identifier){
|
||||
if (player == null) return "";
|
||||
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
|
||||
if ("scoreboard_visible".equals(identifier)) {
|
||||
return translate(hasScoreboardVisible(p));
|
||||
}
|
||||
if ("scoreboard_name".equals(identifier)) {
|
||||
return getActiveScoreboard(p);
|
||||
}
|
||||
if ("bossbar_visible".equals(identifier)) {
|
||||
return translate(hasBossBarVisible(p));
|
||||
}
|
||||
if ("ntpreview".equals(identifier)) {
|
||||
return translate(p.isPreviewingNametag());
|
||||
}
|
||||
if (identifier.startsWith("replace_")) {
|
||||
return findReplacement("%" + identifier.substring(8) + "%", player);
|
||||
}
|
||||
if (identifier.startsWith("placeholder_")) {
|
||||
return TAB.getInstance().getPlaceholderManager().getPlaceholder("%" + identifier.substring(12) + "%").getLastValue(p);
|
||||
}
|
||||
return getProperty(identifier, p);
|
||||
}
|
||||
|
||||
private boolean hasBossBarVisible(TabPlayer p) {
|
||||
BossBarManager boss = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature("bossbar");
|
||||
if (boss == null) return false;
|
||||
return boss.hasBossBarVisible(p);
|
||||
}
|
||||
|
||||
private boolean hasScoreboardVisible(TabPlayer p) {
|
||||
ScoreboardManager scoreboard = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
|
||||
if (scoreboard == null) return false;
|
||||
return scoreboard.hasScoreboardVisible(p);
|
||||
}
|
||||
|
||||
private String getActiveScoreboard(TabPlayer p) {
|
||||
ScoreboardManagerImpl sb = (ScoreboardManagerImpl) TabAPI.getInstance().getScoreboardManager();
|
||||
if (sb == null) return "";
|
||||
Scoreboard active = sb.getActiveScoreboards().get(p);
|
||||
if (active == null) return "";
|
||||
return active.getName();
|
||||
}
|
||||
|
||||
private String translate(boolean b) {
|
||||
return b ? "Enabled" : "Disabled";
|
||||
}
|
||||
|
||||
/**
|
||||
* Find replacement for specified placeholder
|
||||
* @param placeholder - placeholder to find replacement for
|
||||
* @param player - player to set placeholder for
|
||||
* @return replacement
|
||||
*/
|
||||
private String findReplacement(String placeholder, Player player) {
|
||||
String output = PlaceholderAPI.setPlaceholders(player, placeholder);
|
||||
return TAB.getInstance().getPlaceholderManager().findReplacement(placeholder, output).replace("%value%", output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value of defined property if found
|
||||
* @param name - property name
|
||||
* @param player - player to get property of
|
||||
* @return value of specified property
|
||||
*/
|
||||
private String getProperty(String name, TabPlayer player) {
|
||||
String propName = name.replace("_raw", "");
|
||||
Property prop = player.getProperty(propName);
|
||||
if (prop != null) {
|
||||
if (name.endsWith("_raw")) {
|
||||
return prop.getCurrentRawValue();
|
||||
}
|
||||
return prop.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -19,54 +17,57 @@ import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
|
||||
/**
|
||||
* An additional class with additional code for <1.9 servers due to an entity being required
|
||||
* An additional class with additional code for <1.9 servers due to an entity being required
|
||||
*/
|
||||
public class WitherBossBar extends BossBarManagerImpl implements Listener {
|
||||
|
||||
//distance of wither in blocks
|
||||
private static final int WITHER_DISTANCE = 60;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the class
|
||||
* @param plugin - plugin instance
|
||||
*/
|
||||
public WitherBossBar(JavaPlugin plugin) {
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
//when MC is on fullscreen, BossBar disappears after 1 second of not being seen
|
||||
//when in a small window, it's about 100ms
|
||||
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%location%", 100, p -> ((Player)p.getPlayer()).getLocation());
|
||||
addUsedPlaceholders(Collections.singletonList("%location%"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
refresh(p, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer p, boolean force) {
|
||||
for (BossBar line : getRegisteredBossBars().values()) {
|
||||
if (p.getVersion().getMinorVersion() > 8) continue; //sending VV packets to those
|
||||
Location loc = ((Player) p.getPlayer()).getEyeLocation().add(((Player) p.getPlayer()).getEyeLocation().getDirection().normalize().multiply(WITHER_DISTANCE));
|
||||
if (loc.getY() < 1) loc.setY(1);
|
||||
p.sendCustomPacket(new PacketPlayOutEntityTeleport(line.getUniqueId().hashCode(), loc), TabConstants.PacketCategory.BOSSBAR_WITHER_TELEPORT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respawning wither as respawn screen destroys all entities in client
|
||||
*/
|
||||
@EventHandler
|
||||
public void onRespawn(PlayerRespawnEvent e) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerRespawnEvent", this, TabConstants.CpuUsageCategory.PLAYER_RESPAWN, () -> detectBossBarsAndSend(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
}
|
||||
//distance of wither in blocks
|
||||
private static final int WITHER_DISTANCE = 60;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the class
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
*/
|
||||
public WitherBossBar(JavaPlugin plugin) {
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
//when MC is on fullscreen, BossBar disappears after 1 second of not being seen
|
||||
//when in a small window, it's about 100ms
|
||||
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(100,
|
||||
this, TabConstants.CpuUsageCategory.TELEPORTING_WITHER, this::teleport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
teleport();
|
||||
}
|
||||
|
||||
private void teleport() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p.getVersion().getMinorVersion() > 8) continue; //sending VV packets to those
|
||||
for (BossBar line : getRegisteredBossBars().values()) {
|
||||
if (!line.containsPlayer(p)) continue;
|
||||
Location loc = ((Player) p.getPlayer()).getEyeLocation().add(((Player) p.getPlayer()).getEyeLocation().getDirection().normalize().multiply(WITHER_DISTANCE));
|
||||
if (loc.getY() < 1) loc.setY(1);
|
||||
p.sendCustomPacket(new PacketPlayOutEntityTeleport(line.getUniqueId().hashCode(), loc), TabConstants.PacketCategory.BOSSBAR_WITHER_TELEPORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respawning wither as respawn screen destroys all entities in client
|
||||
*/
|
||||
@EventHandler
|
||||
public void onRespawn(PlayerRespawnEvent e) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(this, TabConstants.CpuUsageCategory.PLAYER_RESPAWN,
|
||||
() -> detectBossBarsAndSend(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
}
|
||||
}
|
||||
+276
-271
@@ -27,313 +27,318 @@ import me.neznamy.tab.shared.TAB;
|
||||
*/
|
||||
public class BukkitArmorStand implements ArmorStand {
|
||||
|
||||
private final boolean alwaysVisible = TAB.getInstance().getConfiguration().isArmorStandsAlwaysVisible();
|
||||
//entity id counter to pick unique entity IDs
|
||||
private static int idCounter = 2000000000;
|
||||
//entity id counter to pick unique entity IDs
|
||||
private static int idCounter = 2000000000;
|
||||
|
||||
//NameTag feature
|
||||
private final NameTagX manager = (NameTagX) TAB.getInstance().getFeatureManager().getFeature("nametagx");
|
||||
//NameTag feature
|
||||
private final BukkitNameTagX manager = (BukkitNameTagX) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
|
||||
|
||||
//owner of the armor stand
|
||||
private final TabPlayer owner;
|
||||
private final BukkitArmorStandManager asm;
|
||||
|
||||
//owner of the armor stand
|
||||
private final TabPlayer owner;
|
||||
|
||||
//instance of Bukkit player
|
||||
private final Player player;
|
||||
//instance of Bukkit player
|
||||
private final Player player;
|
||||
|
||||
//offset in blocks, 0 for original height
|
||||
private double yOffset;
|
||||
//offset in blocks, 0 for original height
|
||||
private double yOffset;
|
||||
|
||||
//entity ID of this armor stand
|
||||
private final int entityId = idCounter++;
|
||||
//entity ID of this armor stand
|
||||
private final int entityId = idCounter++;
|
||||
|
||||
//unique ID of this armor stand
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
//unique ID of this armor stand
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
|
||||
//sneaking flag of armor stands
|
||||
private boolean sneaking;
|
||||
//sneaking flag of armor stands
|
||||
private boolean sneaking;
|
||||
|
||||
//armor stand visibility
|
||||
private boolean visible;
|
||||
//armor stand visibility
|
||||
private boolean visible;
|
||||
|
||||
//property dedicated to this armor stand
|
||||
private final Property property;
|
||||
//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;
|
||||
//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);
|
||||
//entity destroy packet
|
||||
private final PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(entityId);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
* @param owner - armor stand owner
|
||||
* @param property - property for armor stand's name
|
||||
* @param yOffset - offset in blocks
|
||||
* @param staticOffset - if offset is static or not
|
||||
*/
|
||||
public BukkitArmorStand(TabPlayer owner, Property property, double yOffset, boolean staticOffset) {
|
||||
this.owner = owner;
|
||||
this.staticOffset = staticOffset;
|
||||
player = (Player) owner.getPlayer();
|
||||
this.yOffset = yOffset;
|
||||
this.property = property;
|
||||
visible = getVisibility();
|
||||
}
|
||||
public BukkitArmorStand(BukkitArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
|
||||
this.asm = asm;
|
||||
this.owner = owner;
|
||||
this.staticOffset = staticOffset;
|
||||
player = (Player) owner.getPlayer();
|
||||
this.yOffset = yOffset;
|
||||
this.property = owner.getProperty(propertyName);
|
||||
visible = getVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
visible = getVisibility();
|
||||
updateMetadata();
|
||||
}
|
||||
@Override
|
||||
public void refresh() {
|
||||
visible = getVisibility();
|
||||
updateMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property getProperty() {
|
||||
return property;
|
||||
}
|
||||
@Override
|
||||
public Property getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStaticOffset() {
|
||||
return staticOffset;
|
||||
}
|
||||
@Override
|
||||
public boolean hasStaticOffset() {
|
||||
return staticOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getOffset() {
|
||||
return yOffset;
|
||||
}
|
||||
@Override
|
||||
public double getOffset() {
|
||||
return yOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffset(double offset) {
|
||||
if (yOffset == offset) return;
|
||||
yOffset = offset;
|
||||
for (TabPlayer all : owner.getArmorStandManager().getNearbyPlayers()) {
|
||||
all.sendCustomPacket(getTeleportPacket(all), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_OFFSET_CHANGE);
|
||||
}
|
||||
}
|
||||
@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)) {
|
||||
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SPAWN);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void spawn(TabPlayer viewer) {
|
||||
for (TabPacket packet : getSpawnPackets(viewer)) {
|
||||
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SPAWN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) 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 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 : owner.getArmorStandManager().getNearbyPlayers()) {
|
||||
all.sendCustomPacket(getTeleportPacket(all), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
}
|
||||
@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 (!owner.getArmorStandManager().isNearby(viewer) && viewer != owner) {
|
||||
owner.getArmorStandManager().spawn(viewer);
|
||||
} else {
|
||||
viewer.sendCustomPacket(getTeleportPacket(viewer), 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 : owner.getArmorStandManager().getNearbyPlayers()) {
|
||||
if (viewer.getVersion().getMinorVersion() == 14 && !alwaysVisible) {
|
||||
//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 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) {
|
||||
visible = visibility;
|
||||
updateMetadata();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void updateVisibility(boolean force) {
|
||||
boolean visibility = getVisibility();
|
||||
if (visible != visibility || force) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
@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));
|
||||
}
|
||||
/**
|
||||
* 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 : owner.getArmorStandManager().getNearbyPlayers()) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(entityId, createDataWatcher(property.getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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 (owner.isDisguised() || manager.getVehicleManager().isOnBoat(owner)) return false;
|
||||
if (alwaysVisible) return true;
|
||||
return !owner.hasInvisibilityPotion() && owner.getGamemode() != 3 && !manager.hasHiddenNametag(owner) && property.get().length() > 0;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
* @return Location where armor stand should be for everyone
|
||||
*/
|
||||
public Location getLocation() {
|
||||
double x = player.getLocation().getX();
|
||||
double y = getY() + yOffset + 2;
|
||||
double z = player.getLocation().getZ();
|
||||
if (player.isSleeping()) {
|
||||
y -= 1.76;
|
||||
} else {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
y -= (sneaking ? 0.45 : 0.18);
|
||||
} else {
|
||||
y -= (sneaking ? 0.30 : 0.18);
|
||||
}
|
||||
}
|
||||
return new Location(null,x,y,z);
|
||||
}
|
||||
/**
|
||||
* Returns general location where armor stand should be at time of calling
|
||||
*
|
||||
* @return Location where armor stand should be for everyone
|
||||
*/
|
||||
public Location getLocation() {
|
||||
double x = player.getLocation().getX();
|
||||
double y = getY() + yOffset + 2;
|
||||
double z = player.getLocation().getZ();
|
||||
if (player.isSleeping()) {
|
||||
y -= 1.76;
|
||||
} else {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
y -= (sneaking ? 0.45 : 0.18);
|
||||
} else {
|
||||
y -= (sneaking ? 0.30 : 0.18);
|
||||
}
|
||||
}
|
||||
return new Location(null,x,y,z);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("DONKEY")) { //1.11+
|
||||
return vehicle.getLocation().getY() + 0.525;
|
||||
}
|
||||
if (vehicle.getType() == EntityType.PIG) {
|
||||
return vehicle.getLocation().getY() + 0.325;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("STRIDER")) { //1.16+
|
||||
return vehicle.getLocation().getY() + 1.15;
|
||||
}
|
||||
}
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (TAB.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
return player.getLocation().getY()-1.22;
|
||||
}
|
||||
return player.getLocation().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;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("DONKEY")) { //1.11+
|
||||
return vehicle.getLocation().getY() + 0.525;
|
||||
}
|
||||
if (vehicle.getType() == EntityType.PIG) {
|
||||
return vehicle.getLocation().getY() + 0.325;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("STRIDER")) { //1.16+
|
||||
return vehicle.getLocation().getY() + 1.15;
|
||||
}
|
||||
}
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (TAB.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
return player.getLocation().getY()-1.22;
|
||||
}
|
||||
return player.getLocation().getY();
|
||||
}
|
||||
|
||||
private boolean isSwimming() {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
|
||||
return TAB.getInstance().getServerVersion().getMinorVersion() == 13 && player.isSwimming();
|
||||
}
|
||||
private boolean isSwimming() {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
|
||||
return TAB.getInstance().getServerVersion().getMinorVersion() == 13 && player.isSwimming();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates data watcher with specified display name for viewer
|
||||
* @param displayName - armor stand name
|
||||
* @param viewer - player to apply checks against
|
||||
* @return DataWatcher for viewer
|
||||
*/
|
||||
public DataWatcher createDataWatcher(String displayName, TabPlayer viewer) {
|
||||
DataWatcher datawatcher = new DataWatcher();
|
||||
/**
|
||||
* Creates data watcher with specified display name for viewer
|
||||
*
|
||||
* @param displayName
|
||||
* armor stand name
|
||||
* @param viewer
|
||||
* player to apply checks against
|
||||
* @return DataWatcher for viewer
|
||||
*/
|
||||
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());
|
||||
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)) {
|
||||
visibility = false;
|
||||
} else {
|
||||
visibility = visible;
|
||||
}
|
||||
datawatcher.helper().setCustomNameVisible(visibility);
|
||||
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);
|
||||
return datawatcher;
|
||||
}
|
||||
if (viewer.getVersion().getMinorVersion() > 8 || manager.isMarkerFor18x()) datawatcher.helper().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 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 (TAB.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 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 (TAB.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().clone().add(0,-2,0) : getLocation();
|
||||
}
|
||||
/**
|
||||
* 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().clone().add(0,-2,0) : getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respawn(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
Runnable spawn = () -> spawn(viewer);
|
||||
if (viewer.getVersion().getMinorVersion() == 8) {
|
||||
//1.8.0 client sided bug
|
||||
TAB.getInstance().getCPUManager().runTaskLater(50, "compensating for 1.8.0 bugs", manager, TabConstants.CpuUsageCategory.V1_8_0_BUG_COMPENSATION, spawn);
|
||||
} else {
|
||||
spawn.run();
|
||||
}
|
||||
}
|
||||
@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);
|
||||
}
|
||||
}
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.ArmorStandManager;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A helper class for easy management of armor stands of a player
|
||||
*/
|
||||
public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
|
||||
private final NameTagX nameTagX;
|
||||
//map of registered armor stands
|
||||
private final Map<String, ArmorStand> armorStands = new LinkedHashMap<>();
|
||||
|
||||
//players in entity tracking range
|
||||
private final List<TabPlayer> nearbyPlayers = new ArrayList<>();
|
||||
|
||||
//array to iterate over to avoid concurrent modification and slightly boost performance & memory
|
||||
private ArmorStand[] armorStandArray = new ArmorStand[0];
|
||||
|
||||
private TabPlayer[] nearbyPlayerArray = new TabPlayer[0];
|
||||
|
||||
public BukkitArmorStandManager(NameTagX nameTagX, TabPlayer owner) {
|
||||
this.nameTagX = nameTagX;
|
||||
owner.setProperty(nameTagX, TabConstants.Property.NAMETAG, owner.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue()
|
||||
+ owner.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue()
|
||||
+ owner.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
|
||||
double height = 0;
|
||||
for (String line : nameTagX.getDynamicLines()) {
|
||||
addArmorStand(line, new BukkitArmorStand(this, owner, line, height, false));
|
||||
height += nameTagX.getSpaceBetweenLines();
|
||||
}
|
||||
for (Map.Entry<String, Object> line : nameTagX.getStaticLines().entrySet()) {
|
||||
addArmorStand(line.getKey(), new BukkitArmorStand(this, owner, line.getKey(), Double.parseDouble(line.getValue().toString()), true));
|
||||
}
|
||||
fixArmorStandHeights();
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports armor stands to player's current location for specified viewer
|
||||
*
|
||||
* @param viewer
|
||||
* player to teleport armor stands for
|
||||
*/
|
||||
public void teleport(TabPlayer viewer) {
|
||||
for (ArmorStand a : armorStandArray) a.teleport(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports armor stands to player's current location for all nearby players
|
||||
*/
|
||||
public void teleport() {
|
||||
for (ArmorStand a : armorStandArray) a.teleport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of nearby players
|
||||
* @return array of nearby players
|
||||
*/
|
||||
public TabPlayer[] getNearbyPlayers(){
|
||||
return nearbyPlayerArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if requested player is nearby, {@code false} if not
|
||||
*
|
||||
* @param viewer
|
||||
* Player to check for
|
||||
* @return {@code true} if player nearby, {@code false} if not
|
||||
*/
|
||||
public boolean isNearby(TabPlayer viewer) {
|
||||
return nearbyPlayers.contains(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if manager contains armor stand with specified entity id, false if not
|
||||
*
|
||||
* @param entityId
|
||||
* entity id
|
||||
* @return true if armor stand with specified entity id exists, false if not
|
||||
*/
|
||||
public boolean hasArmorStandWithID(int entityId) {
|
||||
for (ArmorStand a : armorStandArray) {
|
||||
if (a.getEntityId() == entityId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sneak value of armor stands to specified value
|
||||
*
|
||||
* @param sneaking
|
||||
* new sneaking status
|
||||
*/
|
||||
public void sneak(boolean sneaking) {
|
||||
for (ArmorStand a : armorStandArray) a.sneak(sneaking);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs respawn operation on all armor stands to skip teleport animation
|
||||
*/
|
||||
public void respawn() {
|
||||
for (ArmorStand a : armorStandArray) {
|
||||
for (TabPlayer viewer : nearbyPlayerArray) {
|
||||
a.respawn(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns all armor stands for specified viewer and adds them into nearby players
|
||||
*
|
||||
* @param viewer
|
||||
* player to spawn armor stands for
|
||||
*/
|
||||
public void spawn(TabPlayer viewer) {
|
||||
nearbyPlayers.add(viewer);
|
||||
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
|
||||
if (viewer.getVersion().getMinorVersion() < 8) return;
|
||||
for (ArmorStand a : armorStandArray) a.spawn(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes heights of all armor stands due to dynamic lines
|
||||
*/
|
||||
public void fixArmorStandHeights() {
|
||||
double currentY = -nameTagX.getSpaceBetweenLines();
|
||||
for (ArmorStand as : armorStandArray) {
|
||||
if (as.hasStaticOffset()) continue;
|
||||
if (as.getProperty().get().length() != 0) {
|
||||
currentY += nameTagX.getSpaceBetweenLines();
|
||||
as.setOffset(currentY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds armor stand to list and registers it to all nearby players
|
||||
*
|
||||
* @param name
|
||||
* Unique identifier of the text line
|
||||
* @param as
|
||||
* Armor stand to add
|
||||
*/
|
||||
public void addArmorStand(String name, ArmorStand as) {
|
||||
armorStands.put(name, as);
|
||||
armorStandArray = armorStands.values().toArray(new ArmorStand[0]);
|
||||
for (TabPlayer p : nearbyPlayerArray) as.spawn(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes specified player from list of nearby players
|
||||
*
|
||||
* @param viewer
|
||||
* player to remove
|
||||
*/
|
||||
public void unregisterPlayer(TabPlayer viewer) {
|
||||
if (nearbyPlayers.remove(viewer)) nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
|
||||
}
|
||||
|
||||
public void updateVisibility(boolean force) {
|
||||
for (ArmorStand a : armorStandArray) a.updateVisibility(force);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends destroy packet of all armor stands to specified player and removes them from nearby players list
|
||||
*
|
||||
* @param viewer
|
||||
* player to destroy armor stands for
|
||||
*/
|
||||
public void destroy(TabPlayer viewer) {
|
||||
for (ArmorStand a : armorStandArray) a.destroy(viewer);
|
||||
unregisterPlayer(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (ArmorStand a : armorStandArray) a.destroy();
|
||||
nearbyPlayers.clear();
|
||||
nearbyPlayerArray = new TabPlayer[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(boolean force) {
|
||||
boolean fix = false;
|
||||
for (ArmorStand as : armorStandArray) {
|
||||
if (as.getProperty().update() || force) {
|
||||
as.refresh();
|
||||
fix = true;
|
||||
}
|
||||
}
|
||||
if (fix) fixArmorStandHeights();
|
||||
}
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
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 {
|
||||
|
||||
//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
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
*/
|
||||
public BukkitNameTagX(JavaPlugin plugin) {
|
||||
super(BukkitArmorStandManager::new);
|
||||
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, new PacketListener(this));
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(all)) continue;
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, all);
|
||||
}
|
||||
}
|
||||
startVisibilityRefreshTask();
|
||||
}
|
||||
|
||||
private void startVisibilityRefreshTask() {
|
||||
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(500, this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
|
||||
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(p)) continue;
|
||||
getArmorStandManager(p).updateVisibility(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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 : TAB.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 (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 : TAB.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseArmorStands(TabPlayer player) {
|
||||
getArmorStandManager(player).destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNameTagVisibilityView(TabPlayer player) {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).updateVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
super.onQuit(disconnectedPlayer);
|
||||
for (TabPlayer all : TAB.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 : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getWorld().equals(from)) {
|
||||
getArmorStandManager(p).destroy(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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 VehicleRefresher getVehicleManager() {
|
||||
return vehicleManager;
|
||||
}
|
||||
}
|
||||
+40
-64
@@ -3,7 +3,6 @@ package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||
|
||||
@@ -15,68 +14,45 @@ import me.neznamy.tab.shared.TAB;
|
||||
* The event listener part for securing proper functionality of armor stands
|
||||
*/
|
||||
public class EventListener implements Listener {
|
||||
|
||||
//the NameTag feature handler
|
||||
private final NameTagX feature;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
* @param feature - NameTag feature handler
|
||||
*/
|
||||
public EventListener(NameTagX feature) {
|
||||
this.feature = feature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sneak event listener to de-spawn & spawn armor stands to skip animation
|
||||
* @param e - sneak event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onSneak(PlayerToggleSneakEvent e) {
|
||||
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (p == null || !p.isLoaded() || feature.isPlayerDisabled(p)) return;
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerToggleSneakEvent", feature, TabConstants.CpuUsageCategory.PLAYER_SNEAK, () -> {
|
||||
if (p.getArmorStandManager() != null) p.getArmorStandManager().sneak(e.isSneaking());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Respawning armor stands as respawn screen destroys all entities in client
|
||||
* @param e - respawn event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onRespawn(PlayerRespawnEvent e) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerRespawnEvent", feature, TabConstants.CpuUsageCategory.PLAYER_RESPAWN, () -> {
|
||||
TabPlayer respawned = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (feature.isPlayerDisabled(respawned)) return;
|
||||
respawned.getArmorStandManager().teleport();
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldChange(PlayerChangedWorldEvent e) {
|
||||
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (p == null) return;
|
||||
long time = System.nanoTime();
|
||||
String to = e.getPlayer().getWorld().getName();
|
||||
if (feature.isDisabled(to)) {
|
||||
if (!feature.getPlayersInDisabledUnlimitedWorlds().contains(p))
|
||||
feature.getPlayersInDisabledUnlimitedWorlds().add(p);
|
||||
} else {
|
||||
feature.getPlayersInDisabledUnlimitedWorlds().remove(p);
|
||||
}
|
||||
//TODO delete the block below
|
||||
TabPlayer[] nearby = p.getArmorStandManager().getNearbyPlayers();
|
||||
p.getArmorStandManager().destroy();
|
||||
feature.loadArmorStands(p);
|
||||
feature.getVehicleManager().loadPassengers(p);
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getArmorStandManager() != null) viewer.getArmorStandManager().destroy(p);
|
||||
if (!to.equals(viewer.getWorld())) continue;
|
||||
for (TabPlayer player : nearby) {
|
||||
if (player == viewer) feature.spawnArmorStands(p, viewer, true);
|
||||
}
|
||||
}
|
||||
TAB.getInstance().getCPUManager().addTime(feature, TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
|
||||
}
|
||||
//the NameTag feature handler
|
||||
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
|
||||
*
|
||||
* @param e
|
||||
* sneak event
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onSneak(PlayerToggleSneakEvent e) {
|
||||
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (p == null || feature.isPlayerDisabled(p)) return;
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(feature, TabConstants.CpuUsageCategory.PLAYER_SNEAK, () -> feature.getArmorStandManager(p).sneak(e.isSneaking()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Respawning armor stands as respawn screen destroys all entities in client
|
||||
*
|
||||
* @param e
|
||||
* respawn event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onRespawn(PlayerRespawnEvent e) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(feature, TabConstants.CpuUsageCategory.PLAYER_RESPAWN, () -> {
|
||||
TabPlayer respawned = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (feature.isPlayerDisabled(respawned)) return;
|
||||
feature.getArmorStandManager(respawned).teleport();
|
||||
});
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
public class LocationRefresher extends TabFeature {
|
||||
|
||||
private final NameTagX feature;
|
||||
|
||||
public LocationRefresher(NameTagX feature) {
|
||||
super(feature.getFeatureName(), "Processing passengers / preview");
|
||||
this.feature = feature;
|
||||
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%location0%", 50, p -> {
|
||||
|
||||
if (!feature.getVehicleManager().getPlayersInVehicle().containsKey(p) && !p.isPreviewingNametag()) return null;
|
||||
Location l = ((Player)p.getPlayer()).getLocation();
|
||||
return l.getX()+l.getY()+l.getZ(); //less cpu usage than literal Location#toString() and then .equals() when comparing last value
|
||||
});
|
||||
addUsedPlaceholders(Collections.singletonList("%location0%")); //making sure it's not the same one as in WitherBossBar
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer p, boolean force) {
|
||||
if (feature.getVehicleManager().getPlayersInVehicle().containsKey(p)) {
|
||||
feature.getVehicleManager().processPassengers((Entity) p.getPlayer());
|
||||
}
|
||||
if (p.isPreviewingNametag()) {
|
||||
p.getArmorStandManager().teleport(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
-418
@@ -1,418 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.ArmorStandManager;
|
||||
import me.neznamy.tab.api.Property;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.team.UnlimitedNametagManager;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
|
||||
/**
|
||||
* The core class for unlimited NameTag mode
|
||||
*/
|
||||
public class NameTagX extends NameTag implements UnlimitedNametagManager {
|
||||
|
||||
//config options
|
||||
private final boolean markerFor18x = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.use-marker-tag-for-1-8-x-clients", false);
|
||||
private final boolean disableOnBoats = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.disable-on-boats", true);
|
||||
private final double spaceBetweenLines = TAB.getInstance().getConfiguration().getConfig().getDouble("scoreboard-teams.unlimited-nametag-mode.space-between-lines", 0.22);
|
||||
protected final List<String> disabledUnlimitedWorlds = TAB.getInstance().getConfiguration().getConfig().getStringList("scoreboard-teams.unlimited-nametag-mode.disable-in-worlds", new ArrayList<>());
|
||||
private final List<String> dynamicLines = new ArrayList<>(TAB.getInstance().getConfiguration().getConfig().getStringList("scoreboard-teams.unlimited-nametag-mode.dynamic-lines", Arrays.asList(TabConstants.Property.ABOVENAME, TabConstants.Property.NAMETAG, TabConstants.Property.BELOWNAME, "another")));
|
||||
private final Map<String, Object> staticLines = TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("scoreboard-teams.unlimited-nametag-mode.static-lines");
|
||||
|
||||
//player data by entityId, used for better performance
|
||||
private final Map<Integer, TabPlayer> entityIdMap = new ConcurrentHashMap<>();
|
||||
|
||||
//bukkit event listener
|
||||
private final EventListener eventListener = new EventListener(this);
|
||||
|
||||
private final List<TabPlayer> playersInDisabledUnlimitedWorlds = new ArrayList<>();
|
||||
private final String[] disabledUnlimitedWorldsArray = disabledUnlimitedWorlds.toArray(new String[0]);
|
||||
private final boolean unlimitedWorldWhitelistMode = disabledUnlimitedWorlds.contains("WHITELIST");
|
||||
|
||||
private final List<TabPlayer> playersDisabledWithAPI = new ArrayList<>();
|
||||
|
||||
private final VehicleRefresher vehicleManager = new VehicleRefresher(this);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
* @param plugin - plugin instance
|
||||
*/
|
||||
public NameTagX(JavaPlugin plugin) {
|
||||
Collections.reverse(dynamicLines);
|
||||
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
|
||||
TAB.getInstance().getFeatureManager().registerFeature("NameTagX-packet", new PacketListener(this));
|
||||
TAB.getInstance().getFeatureManager().registerFeature("NameTagX-vehicle", vehicleManager);
|
||||
TAB.getInstance().getFeatureManager().registerFeature("NameTagX-location", new LocationRefresher(this));
|
||||
TAB.getInstance().debug(String.format("Loaded Unlimited NameTag feature with parameters markerFor18x=%s, disableOnBoats=%s, spaceBetweenLines=%s, disabledUnlimitedWorlds=%s",
|
||||
markerFor18x, disableOnBoats, spaceBetweenLines, disabledUnlimitedWorlds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
getEntityIdMap().put(((Player) all.getPlayer()).getEntityId(), all);
|
||||
updateProperties(all);
|
||||
loadArmorStands(all);
|
||||
if (isDisabled(all.getWorld())) {
|
||||
playersInDisabledUnlimitedWorlds.add(all);
|
||||
}
|
||||
if (isPlayerDisabled(all)) continue;
|
||||
vehicleManager.loadPassengers(all);
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(all, viewer, false);
|
||||
}
|
||||
}
|
||||
super.load();
|
||||
startVisibilityRefreshTask();
|
||||
}
|
||||
|
||||
private void startVisibilityRefreshTask() {
|
||||
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(500, "refreshing NameTag visibility", this, TabConstants.CpuUsageCategory.REFRESHING_NAMETAG_VISIBILITY, () -> {
|
||||
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (!p.isLoaded() || isPlayerDisabled(p)) continue;
|
||||
p.getArmorStandManager().updateVisibility(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
HandlerList.unregisterAll(eventListener);
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p.getArmorStandManager() != null) p.getArmorStandManager().destroy();
|
||||
}
|
||||
getEntityIdMap().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
if (isDisabled(connectedPlayer.getWorld()) && !playersInDisabledUnlimitedWorlds.contains(connectedPlayer))
|
||||
playersInDisabledUnlimitedWorlds.add(connectedPlayer);
|
||||
super.onJoin(connectedPlayer);
|
||||
getEntityIdMap().put(((Player) connectedPlayer.getPlayer()).getEntityId(), connectedPlayer);
|
||||
loadArmorStands(connectedPlayer);
|
||||
if (isPlayerDisabled(connectedPlayer)) return;
|
||||
vehicleManager.loadPassengers(connectedPlayer);
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(connectedPlayer, viewer, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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 void onQuit(TabPlayer disconnectedPlayer) {
|
||||
super.onQuit(disconnectedPlayer);
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (all.getArmorStandManager() != null) all.getArmorStandManager().unregisterPlayer(disconnectedPlayer);
|
||||
}
|
||||
getEntityIdMap().remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
|
||||
playersInDisabledUnlimitedWorlds.remove(disconnectedPlayer);
|
||||
playersDisabledWithAPI.remove(disconnectedPlayer);
|
||||
if (disconnectedPlayer.getArmorStandManager() != null) { //player was not loaded yet
|
||||
disconnectedPlayer.getArmorStandManager().destroy();
|
||||
TAB.getInstance().getCPUManager().runTaskLater(500, "processing onQuit", this, TabConstants.CpuUsageCategory.PLAYER_QUIT, () -> disconnectedPlayer.getArmorStandManager().destroy());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns armor stands of specified owner to viewer and, mutually if set
|
||||
* @param owner - armor stand owner
|
||||
* @param viewer - player to spawn armor stands for
|
||||
* @param sendMutually - if packets should be sent both ways
|
||||
*/
|
||||
public void spawnArmorStands(TabPlayer owner, TabPlayer viewer, boolean sendMutually) {
|
||||
if (owner == viewer) return; //not displaying own armor stands
|
||||
if (((Player) viewer.getPlayer()).getWorld() != ((Player) owner.getPlayer()).getWorld()) return; //different world
|
||||
if (isPlayerDisabled(owner)) return;
|
||||
if (getDistance(viewer, owner) <= 48) {
|
||||
if (((Player)viewer.getPlayer()).canSee((Player)owner.getPlayer()) && !owner.isVanished()) owner.getArmorStandManager().spawn(viewer);
|
||||
if (sendMutually && viewer.getArmorStandManager() != null && ((Player)owner.getPlayer()).canSee((Player)viewer.getPlayer())
|
||||
&& !viewer.isVanished()) viewer.getArmorStandManager().spawn(owner);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restarts and loads armor stands from config
|
||||
* @param pl - player to load
|
||||
*/
|
||||
public void loadArmorStands(TabPlayer pl) {
|
||||
pl.setArmorStandManager(new ArmorStandManager());
|
||||
pl.setProperty(this, TabConstants.Property.NAMETAG, pl.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue()
|
||||
+ pl.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue()
|
||||
+ pl.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
|
||||
double height = 0;
|
||||
for (String line : dynamicLines) {
|
||||
Property p = pl.getProperty(line);
|
||||
if (p.getCurrentRawValue().length() == 0) continue;
|
||||
pl.getArmorStandManager().addArmorStand(line, new BukkitArmorStand(pl, p, height, false));
|
||||
height += spaceBetweenLines;
|
||||
}
|
||||
for (Entry<String, Object> line : staticLines.entrySet()) {
|
||||
Property p = pl.getProperty(line.getKey());
|
||||
if (p.getCurrentRawValue().length() == 0) continue;
|
||||
pl.getArmorStandManager().addArmorStand(line.getKey(), new BukkitArmorStand(pl, p, Double.parseDouble(line.getValue().toString()), true));
|
||||
}
|
||||
fixArmorStandHeights(pl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes heights of all armor stands of specified player due to dynamic lines
|
||||
* @param p - player to fix armor stands heights for
|
||||
*/
|
||||
public void fixArmorStandHeights(TabPlayer p) {
|
||||
double currentY = -spaceBetweenLines;
|
||||
for (ArmorStand as : p.getArmorStandManager().getArmorStands()) {
|
||||
if (as.hasStaticOffset()) continue;
|
||||
if (as.getProperty().get().length() != 0) {
|
||||
currentY += spaceBetweenLines;
|
||||
as.setOffset(currentY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
super.refresh(refreshed, force);
|
||||
if (isPlayerDisabled(refreshed)) return;
|
||||
if (force) {
|
||||
refreshed.getArmorStandManager().destroy();
|
||||
loadArmorStands(refreshed);
|
||||
vehicleManager.loadPassengers(refreshed);
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (viewer == refreshed) continue;
|
||||
if (viewer.getWorld().equals(refreshed.getWorld())) {
|
||||
refreshed.getArmorStandManager().spawn(viewer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
boolean fix = false;
|
||||
for (ArmorStand as : refreshed.getArmorStandManager().getArmorStands()) {
|
||||
if (as.getProperty().update()) {
|
||||
as.refresh();
|
||||
fix = true;
|
||||
}
|
||||
}
|
||||
if (fix) fixArmorStandHeights(refreshed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates raw values of properties for specified player
|
||||
* @param p - player to update
|
||||
*/
|
||||
@Override
|
||||
public void updateProperties(TabPlayer p) {
|
||||
super.updateProperties(p);
|
||||
p.loadPropertyFromConfig(this, TabConstants.Property.CUSTOMTAGNAME, p.getName());
|
||||
p.setProperty(this, TabConstants.Property.NAMETAG, p.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue() + p.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue() + p.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
|
||||
for (String property : dynamicLines) {
|
||||
if (!property.equals(TabConstants.Property.NAMETAG)) p.loadPropertyFromConfig(this, property);
|
||||
}
|
||||
for (String property : staticLines.keySet()) {
|
||||
if (!property.equals(TabConstants.Property.NAMETAG)) p.loadPropertyFromConfig(this, property);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureName() {
|
||||
return "Unlimited NameTags";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTeamVisibility(TabPlayer p, TabPlayer viewer) {
|
||||
if (p.hasInvisibilityPotion()) return false; //1.8.x client sided bug
|
||||
if (vehicleManager == null) return false; //class is still being initialized and this was called from super constructor
|
||||
return vehicleManager.isOnBoat(p) || isPlayerDisabled(p);
|
||||
}
|
||||
|
||||
public Map<Integer, TabPlayer> getEntityIdMap() {
|
||||
return entityIdMap;
|
||||
}
|
||||
|
||||
public boolean isDisabled(String world) {
|
||||
boolean contains = contains(disabledUnlimitedWorldsArray, world);
|
||||
if (unlimitedWorldWhitelistMode) contains = !contains;
|
||||
return contains;
|
||||
}
|
||||
|
||||
public boolean isPlayerDisabled(TabPlayer p) {
|
||||
return isDisabledPlayer(p) || playersInDisabledUnlimitedWorlds.contains(p) || hasTeamHandlingPaused(p) || hasDisabledArmorStands(p);
|
||||
}
|
||||
|
||||
public boolean isMarkerFor18x() {
|
||||
return markerFor18x;
|
||||
}
|
||||
|
||||
public List<TabPlayer> getPlayersInDisabledUnlimitedWorlds() {
|
||||
return playersInDisabledUnlimitedWorlds;
|
||||
}
|
||||
|
||||
public VehicleRefresher getVehicleManager() {
|
||||
return vehicleManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseTeamHandling(TabPlayer player) {
|
||||
if (teamHandlingPaused.contains(player)) return;
|
||||
if (!isDisabledPlayer(player)) unregisterTeam(player);
|
||||
teamHandlingPaused.add(player); //adding after, so unregisterTeam method runs
|
||||
player.getArmorStandManager().destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeTeamHandling(TabPlayer player) {
|
||||
if (!teamHandlingPaused.contains(player)) return;
|
||||
teamHandlingPaused.remove(player); //removing before, so registerTeam method runs
|
||||
if (!isDisabledPlayer(player)) registerTeam(player);
|
||||
if (!isPlayerDisabled(player)) {
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(player, viewer, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableArmorStands(TabPlayer player) {
|
||||
if (playersDisabledWithAPI.contains(player)) return;
|
||||
playersDisabledWithAPI.add(player);
|
||||
player.getArmorStandManager().destroy();
|
||||
updateTeamData(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableArmorStands(TabPlayer player) {
|
||||
if (!playersDisabledWithAPI.contains(player)) return;
|
||||
playersDisabledWithAPI.remove(player);
|
||||
if (!isPlayerDisabled(player)) {
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(player, viewer, false);
|
||||
}
|
||||
}
|
||||
updateTeamData(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDisabledArmorStands(TabPlayer player) {
|
||||
return playersDisabledWithAPI.contains(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrefix(TabPlayer player, String prefix) {
|
||||
player.getProperty(TabConstants.Property.TAGPREFIX).setTemporaryValue(prefix);
|
||||
rebuildNameTagLine(player);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSuffix(TabPlayer player, String suffix) {
|
||||
player.getProperty(TabConstants.Property.TAGSUFFIX).setTemporaryValue(suffix);
|
||||
rebuildNameTagLine(player);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetPrefix(TabPlayer player) {
|
||||
player.getProperty(TabConstants.Property.TAGPREFIX).setTemporaryValue(null);
|
||||
rebuildNameTagLine(player);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetSuffix(TabPlayer player) {
|
||||
player.getProperty(TabConstants.Property.TAGSUFFIX).setTemporaryValue(null);
|
||||
rebuildNameTagLine(player);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(TabPlayer player, String customName) {
|
||||
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).setTemporaryValue(customName);
|
||||
rebuildNameTagLine(player);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLine(TabPlayer player, String line, String value) {
|
||||
player.getProperty(line).setTemporaryValue(value);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetName(TabPlayer player) {
|
||||
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).setTemporaryValue(null);
|
||||
rebuildNameTagLine(player);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLine(TabPlayer player, String line) {
|
||||
player.getProperty(line).setTemporaryValue(null);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCustomName(TabPlayer player) {
|
||||
return player.getProperty(TabConstants.Property.CUSTOMTAGNAME).getTemporaryValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCustomLineValue(TabPlayer player, String line) {
|
||||
return player.getProperty(line).getTemporaryValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalName(TabPlayer player) {
|
||||
return player.getProperty(TabConstants.Property.CUSTOMTAGNAME).getOriginalRawValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalLineValue(TabPlayer player, String line) {
|
||||
return player.getProperty(line).getOriginalRawValue();
|
||||
}
|
||||
|
||||
private void rebuildNameTagLine(TabPlayer player) {
|
||||
player.setProperty(this, TabConstants.Property.NAMETAG, player.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue() +
|
||||
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue() + player.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
|
||||
}
|
||||
|
||||
public boolean isDisableOnBoats() {
|
||||
return disableOnBoats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDefinedLines() {
|
||||
List<String> lines = new ArrayList<>(dynamicLines);
|
||||
lines.addAll(staticLines.keySet());
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
+134
-101
@@ -1,6 +1,8 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -17,110 +19,141 @@ import me.neznamy.tab.shared.TAB;
|
||||
*/
|
||||
public class PacketListener extends TabFeature {
|
||||
|
||||
//main feature
|
||||
private final NameTagX nameTagX;
|
||||
|
||||
//nms storage
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
//main feature
|
||||
private final BukkitNameTagX nameTagX;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
* @param nameTagX - main feature
|
||||
*/
|
||||
public PacketListener(NameTagX nameTagX) {
|
||||
super(nameTagX.getFeatureName(), null);
|
||||
this.nameTagX = 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();
|
||||
|
||||
@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 : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (all.isLoaded() && all.getArmorStandManager().hasArmorStandWithID(entityId)) {
|
||||
attacked = all;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attacked != null && attacked != sender) {
|
||||
nms.setField(packet, nms.PacketPlayInUseEntity_ENTITY, ((Player) attacked.getPlayer()).getEntityId());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@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.getPlayersInDisabledUnlimitedWorlds().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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
entityIdMap.put(((Player) all.getPlayer()).getEntityId(), all);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes entity move packet
|
||||
* @param receiver - packet receiver
|
||||
* @param entityId - entity that moved
|
||||
*/
|
||||
private void onEntityMove(TabPlayer receiver, int entityId) {
|
||||
TabPlayer pl = nameTagX.getEntityIdMap().get(entityId);
|
||||
List<Entity> vehicleList;
|
||||
if (pl != null) {
|
||||
//player moved
|
||||
if (nameTagX.isPlayerDisabled(pl)) return;
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask("processing EntityMove", nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE, () -> pl.getArmorStandManager().teleport(receiver));
|
||||
} else if ((vehicleList = nameTagX.getVehicleManager().getVehicles().get(entityId)) != null){
|
||||
//a vehicle carrying something moved
|
||||
for (Entity entity : vehicleList) {
|
||||
TabPlayer passenger = nameTagX.getEntityIdMap().get(entity.getEntityId());
|
||||
if (passenger != null && passenger.getArmorStandManager() != null) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask("processing EntityMove", nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE_PASSENGER, () -> passenger.getArmorStandManager().teleport(receiver));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onEntitySpawn(TabPlayer receiver, int entityId) {
|
||||
TabPlayer spawnedPlayer = nameTagX.getEntityIdMap().get(entityId);
|
||||
if (spawnedPlayer != null && spawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(spawnedPlayer)) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask("processing NamedEntitySpawn", nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_SPAWN, () -> spawnedPlayer.getArmorStandManager().spawn(receiver));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
entityIdMap.put(((Player) connectedPlayer.getPlayer()).getEntityId(), connectedPlayer);
|
||||
}
|
||||
|
||||
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 = nameTagX.getEntityIdMap().get(entity);
|
||||
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(deSpawnedPlayer))
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask("processing EntityDestroy", nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY, () -> deSpawnedPlayer.getArmorStandManager().destroy(receiver));
|
||||
}
|
||||
@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 : TAB.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;
|
||||
TAB.getInstance().getCPUManager().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) {
|
||||
TAB.getInstance().getCPUManager().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)) {
|
||||
TAB.getInstance().getCPUManager().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);
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY,
|
||||
() -> asm.destroy(receiver));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 296 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user