mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-13 12:42:06 -05:00
Compare commits
374
Commits
2.8.11-pre6
..
2.9.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09dc8cd437 | ||
|
|
c613ba401b | ||
|
|
866df0b5cf | ||
|
|
8f2b56ba05 | ||
|
|
2049885f11 | ||
|
|
4caef337d5 | ||
|
|
a644792bf6 | ||
|
|
54c7f8fc6a | ||
|
|
2e59b87bbe | ||
|
|
f89242ad8f | ||
|
|
845903788c | ||
|
|
53a9047c20 | ||
|
|
a2b74bbc30 | ||
|
|
50ef93cc45 | ||
|
|
e2a5da9c14 | ||
|
|
bf31116d0d | ||
|
|
d79fa8c4f3 | ||
|
|
87e0a7bea3 | ||
|
|
b34cabd55c | ||
|
|
704bd36c77 | ||
|
|
5c2c4d0f4b | ||
|
|
80cca5cb34 | ||
|
|
dcd603f2f5 | ||
|
|
1bee3b49bf | ||
|
|
11bcccb8f9 | ||
|
|
7425493706 | ||
|
|
dca0cfd424 | ||
|
|
21314def89 | ||
|
|
90e544d279 | ||
|
|
89e21558a1 | ||
|
|
b28cc51457 | ||
|
|
2d0a203b5f | ||
|
|
2948666c1d | ||
|
|
b2a2659546 | ||
|
|
e2eb7102c9 | ||
|
|
56301e653b | ||
|
|
1d825e08d2 | ||
|
|
c81a27709c | ||
|
|
2b143172f8 | ||
|
|
84330ec6eb | ||
|
|
0bd703de7f | ||
|
|
e6a55b7d5c | ||
|
|
b6ec7631d2 | ||
|
|
5abbd00d63 | ||
|
|
515c94976e | ||
|
|
912adad8f6 | ||
|
|
70fdd193a3 | ||
|
|
9d83e968fc | ||
|
|
04329da133 | ||
|
|
19ccfbb1a6 | ||
|
|
c53ab09abc | ||
|
|
a4d8daac1d | ||
|
|
99d566d521 | ||
|
|
aa37f73161 | ||
|
|
087518378d | ||
|
|
913d27929b | ||
|
|
530d39bec5 | ||
|
|
cf70dc3845 | ||
|
|
d1421fee3f | ||
|
|
3ec490b36e | ||
|
|
ee42685179 | ||
|
|
e02d687771 | ||
|
|
dddb592253 | ||
|
|
d0ad88d317 | ||
|
|
3d3ce0151f | ||
|
|
f017b0e562 | ||
|
|
4ba2bb669b | ||
|
|
9fc2424736 | ||
|
|
980c01ea70 | ||
|
|
758edcaf5c | ||
|
|
950f825e31 | ||
|
|
0a985f5c78 | ||
|
|
58353f4e6d | ||
|
|
c3d2b5c4b0 | ||
|
|
c2764b4b8a | ||
|
|
9bc8ad0d23 | ||
|
|
488c3540a8 | ||
|
|
1d03950a64 | ||
|
|
e24ea7df2e | ||
|
|
fe5e3d6405 | ||
|
|
db8584e40b | ||
|
|
2e4db38d1a | ||
|
|
f82470c1b1 | ||
|
|
650cc93fc0 | ||
|
|
e61070307a | ||
|
|
2fa46867e5 | ||
|
|
636b2e760a | ||
|
|
8cecf46310 | ||
|
|
a7bcda7eba | ||
|
|
a610dd2c70 | ||
|
|
df98c77a48 | ||
|
|
331dc33c86 | ||
|
|
8493b37171 | ||
|
|
2449c46165 | ||
|
|
a642c2d344 | ||
|
|
88c7b988e7 | ||
|
|
36651260c6 | ||
|
|
6c0072dc54 | ||
|
|
e38f5a77c0 | ||
|
|
6443926a79 | ||
|
|
489deb9ee6 | ||
|
|
a2039110e0 | ||
|
|
f1666b64ee | ||
|
|
d7e419c88b | ||
|
|
de8f8053be | ||
|
|
7aadef5c21 | ||
|
|
f8384dfbdf | ||
|
|
b91cbf827c | ||
|
|
096619d9f7 | ||
|
|
7b8006406c | ||
|
|
90483d549a | ||
|
|
5473055c1c | ||
|
|
f3b082b8e1 | ||
|
|
52fe99a080 | ||
|
|
68b1e941d1 | ||
|
|
109fce9b1a | ||
|
|
014d73dd3f | ||
|
|
ad82744e91 | ||
|
|
638f66ade4 | ||
|
|
c190efa7c4 | ||
|
|
b071eb3366 | ||
|
|
a7c7a86a90 | ||
|
|
0c21519cf6 | ||
|
|
d7f805b26e | ||
|
|
4f702feab3 | ||
|
|
46175ceb1f | ||
|
|
c206860736 | ||
|
|
b97c222490 | ||
|
|
53ad5e3cec | ||
|
|
e67ce8dbf4 | ||
|
|
ac661be5c4 | ||
|
|
ab4e386491 | ||
|
|
aa125b535e | ||
|
|
29df3bb5e2 | ||
|
|
80ee30a532 | ||
|
|
a20b1bcd89 | ||
|
|
4269abd2ca | ||
|
|
a73397065c | ||
|
|
5805a6c5a3 | ||
|
|
e813077c92 | ||
|
|
b1ae68e8c2 | ||
|
|
b6162b2d4a | ||
|
|
0da7854f7f | ||
|
|
a073747e66 | ||
|
|
0975e31371 | ||
|
|
a1231f1708 | ||
|
|
3581da0fe7 | ||
|
|
531810e633 | ||
|
|
36a1db78e2 | ||
|
|
0e58009ced | ||
|
|
ecdcc6894b | ||
|
|
677a2f9396 | ||
|
|
b14f30d4d1 | ||
|
|
4396df55ae | ||
|
|
de6ce93a31 | ||
|
|
b449b7cdfc | ||
|
|
de4412fcff | ||
|
|
8e9b34125d | ||
|
|
024b3175fa | ||
|
|
9fadc6df3b | ||
|
|
f51f8f0f2d | ||
|
|
d805df4887 | ||
|
|
d161c6c074 | ||
|
|
6603c9e682 | ||
|
|
157360c26c | ||
|
|
e16199b360 | ||
|
|
df42424970 | ||
|
|
22ce6ce75e | ||
|
|
e8dde142d2 | ||
|
|
44fe272ce5 | ||
|
|
23a8f32910 | ||
|
|
d523c0408e | ||
|
|
0c2edba316 | ||
|
|
7cfc643522 | ||
|
|
98b446efd2 | ||
|
|
bbf9aaed64 | ||
|
|
13d36457af | ||
|
|
4f850b0f2e | ||
|
|
7118e0d9d3 | ||
|
|
900edef407 | ||
|
|
a7e4b99ce6 | ||
|
|
0463667270 | ||
|
|
d179bc20ae | ||
|
|
bc658a9223 | ||
|
|
e32e3baf3a | ||
|
|
4436f4cf42 | ||
|
|
1dc7b40ee9 | ||
|
|
fb2221d281 | ||
|
|
b5a295970b | ||
|
|
485a453b27 | ||
|
|
03d9ba9579 | ||
|
|
ead7adea31 | ||
|
|
9793fdfc06 | ||
|
|
60b73e5c0d | ||
|
|
ed374c83d1 | ||
|
|
f53828e01a | ||
|
|
91ab50fa6a | ||
|
|
bee0d9d086 | ||
|
|
b38f09f118 | ||
|
|
65f57e8bf1 | ||
|
|
e424e42e05 | ||
|
|
a22cd5338d | ||
|
|
f921737d1c | ||
|
|
2f760afc70 | ||
|
|
4ce56433f9 | ||
|
|
f8ace5fbb5 | ||
|
|
f89778e969 | ||
|
|
99f74d3824 | ||
|
|
dec9c42edc | ||
|
|
34365c13cb | ||
|
|
8a4c780fb1 | ||
|
|
bed49408da | ||
|
|
5dd400bdc5 | ||
|
|
07d5a03a95 | ||
|
|
d53e7665d7 | ||
|
|
4021d7cbac | ||
|
|
2634ca5f28 | ||
|
|
aa887fe27b | ||
|
|
c9077e1254 | ||
|
|
fcf5636d9a | ||
|
|
1c4713ef1a | ||
|
|
d48da82f55 | ||
|
|
7128ab26d8 | ||
|
|
58155041d1 | ||
|
|
e96453eac9 | ||
|
|
a1d98c54ad | ||
|
|
fd423481cb | ||
|
|
48da7d0c2e | ||
|
|
949577fb07 | ||
|
|
dc876efff8 | ||
|
|
c39b650e9b | ||
|
|
12c1cb871d | ||
|
|
c75e86812d | ||
|
|
cc1fffbe8b | ||
|
|
e6313a5e52 | ||
|
|
2a278dcff9 | ||
|
|
8539529061 | ||
|
|
5a4b7d93c0 | ||
|
|
5e459effc0 | ||
|
|
5f0cf2ecb5 | ||
|
|
44c133a68c | ||
|
|
f842f15cd4 | ||
|
|
04c78a37d7 | ||
|
|
cba4dbdf84 | ||
|
|
2fbb162bbf | ||
|
|
a1be962997 | ||
|
|
a3c3f5d990 | ||
|
|
c785d4fdca | ||
|
|
6b78707bd8 | ||
|
|
a1565ed481 | ||
|
|
ae056b312f | ||
|
|
b20c1158b0 | ||
|
|
210f567b74 | ||
|
|
82b91c7bd9 | ||
|
|
5168a99c3b | ||
|
|
e437917841 | ||
|
|
6543440926 | ||
|
|
30c53c6048 | ||
|
|
87412eb4ff | ||
|
|
f1ad5fc57d | ||
|
|
dd3a457ec0 | ||
|
|
069f2aba8b | ||
|
|
9e19553150 | ||
|
|
2d7b009442 | ||
|
|
68817e3595 | ||
|
|
3e229d6b82 | ||
|
|
5fce471577 | ||
|
|
68b1e7cb16 | ||
|
|
761e02ff6e | ||
|
|
5fe4408631 | ||
|
|
bae0685ac1 | ||
|
|
6322a0c045 | ||
|
|
0b89de905e | ||
|
|
49c4453678 | ||
|
|
c056cafda6 | ||
|
|
b375c24340 | ||
|
|
5a76131f24 | ||
|
|
764769a6ca | ||
|
|
b0226df3ab | ||
|
|
8c86120b33 | ||
|
|
8805d0666b | ||
|
|
ae259a894e | ||
|
|
88d3c94c26 | ||
|
|
93dd0e1c72 | ||
|
|
af1a762048 | ||
|
|
980daa6f64 | ||
|
|
814ba371cb | ||
|
|
027e1c7330 | ||
|
|
585af9656b | ||
|
|
101e84d837 | ||
|
|
c0de6137b0 | ||
|
|
eb93a5ae2d | ||
|
|
77124cd922 | ||
|
|
3b5c844743 | ||
|
|
8e909011a0 | ||
|
|
9607720a5d | ||
|
|
95ac332c85 | ||
|
|
df615f1aba | ||
|
|
1a60e097f2 | ||
|
|
ebcd758e68 | ||
|
|
15201af923 | ||
|
|
e4b43abf11 | ||
|
|
f63cccdb0c | ||
|
|
b5fd465788 | ||
|
|
6c2d05742d | ||
|
|
838422b568 | ||
|
|
4ca327d3d9 | ||
|
|
f5dce3db0a | ||
|
|
aee7de283a | ||
|
|
7d9c65cb41 | ||
|
|
6140b11185 | ||
|
|
b8394d2d9a | ||
|
|
d615f0a5ab | ||
|
|
d2249f6788 | ||
|
|
7a7707f712 | ||
|
|
54af3d4a4a | ||
|
|
e5fd46a3fa | ||
|
|
26e5cb6f14 | ||
|
|
2c01f64a06 | ||
|
|
79654c344c | ||
|
|
ed9a17320c | ||
|
|
093b795407 | ||
|
|
cf0b0ed8d2 | ||
|
|
53503883d1 | ||
|
|
b4eaf9ff1d | ||
|
|
5f286322cb | ||
|
|
a1e938626b | ||
|
|
13c0fb769d | ||
|
|
67f92c9e4c | ||
|
|
f438627063 | ||
|
|
252458b6d5 | ||
|
|
5be109f13c | ||
|
|
0ab6691ea6 | ||
|
|
111b105466 | ||
|
|
1dba805c95 | ||
|
|
a691d69011 | ||
|
|
83e645c1d9 | ||
|
|
1090fbb390 | ||
|
|
7669fec40a | ||
|
|
a8ee2a47cb | ||
|
|
3a159159b0 | ||
|
|
3827fcd306 | ||
|
|
1a925b407d | ||
|
|
568b6bd14d | ||
|
|
94106404ad | ||
|
|
07d5a52004 | ||
|
|
60e59c2c9d | ||
|
|
d9ab1cf2c8 | ||
|
|
3ab85750ed | ||
|
|
13a98f8618 | ||
|
|
4cf64beae0 | ||
|
|
28bba220ad | ||
|
|
c71f7645a5 | ||
|
|
6537c6fcde | ||
|
|
7165c3941a | ||
|
|
31969aa076 | ||
|
|
7e098cd7d9 | ||
|
|
8850812994 | ||
|
|
657d03aaf8 | ||
|
|
cb293f5f9a | ||
|
|
388290a90f | ||
|
|
218e5964c3 | ||
|
|
6a9ec46488 | ||
|
|
4577fa5d40 | ||
|
|
d571f7de9e | ||
|
|
408cf2ff08 | ||
|
|
4be47b175a | ||
|
|
d339e41210 | ||
|
|
dca7d229ba | ||
|
|
edb16c3b46 | ||
|
|
f2a5461e15 | ||
|
|
7d325ade95 | ||
|
|
669924fa2a | ||
|
|
480af187ac |
No files matched your search
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: ['https://paypal.me/neznamy1'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
+4
-5
@@ -1,8 +1,7 @@
|
||||
# Compiled class file
|
||||
*.class
|
||||
/bin/
|
||||
/target/
|
||||
/src/test/
|
||||
bin/
|
||||
target/
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
@@ -31,8 +30,8 @@ hs_err_pid*
|
||||
.project
|
||||
|
||||
# intellij
|
||||
/.idea/
|
||||
.idea/
|
||||
|
||||
# other
|
||||
.gitattributes
|
||||
*.iml
|
||||
*.iml
|
||||
@@ -1,28 +1,26 @@
|
||||
## Introduction
|
||||
Here it is. After countless requests to go opensource, I did it to make those people quiet. It worked.
|
||||
It's already been a year and I still can't see any positives of it. This way I just made it easier for everyone to make a plugin as good, so people can stop using it and switch to something better.
|
||||
It's already been a year and I still can't see any positives of it. This way I just made it easier for everyone to make a plugin as good, so people can stop using it and switch to something better. Or just get premium version for free.
|
||||
|
||||
|
||||
## Compiling
|
||||
Plugin can be compiled using maven.
|
||||
|
||||
|
||||
## Premium code
|
||||
The source contains content of premium version as well. However, this must be activated by setting [this line to return true](https://github.com/NEZNAMY/TAB/blob/master/src/main/java/me/neznamy/tab/shared/TAB.java#L79).
|
||||
Plugin can be compiled using maven. Once you've purchased the premium version, you can enable premium features by setting [this line to return true](https://github.com/NEZNAMY/TAB/blob/master/shared/src/main/java/me/neznamy/tab/shared/TAB.java#L78) and apply your code improvements. Enabling it without purchasing the plugin may result in the features not working correctly.
|
||||
**No support is provided for self-compiled jars, only for official compilations.** If you compiled it yourself, it is highly likely you modified something, which might be the reason why is the plugin not working. If you didn't modify anything, you can just get the plugin from official download places.
|
||||
|
||||
|
||||
## Links
|
||||
Discord: https://discord.gg/EaSvdk6
|
||||
SpigotMC (free): https://www.spigotmc.org/resources/57806/
|
||||
MC-Market (paid): https://www.mc-market.org/resources/14009/
|
||||
Wiki: https://github.com/NEZNAMY/TAB/wiki
|
||||
|
||||
|
||||
## Downloads
|
||||
Official release: https://www.spigotmc.org/resources/57806/
|
||||
Pre-releases: https://github.com/NEZNAMY/TAB/releases
|
||||
|
||||
|
||||
Free version
|
||||
Spigot (releases): https://www.spigotmc.org/resources/57806/
|
||||
Pre-releases https://github.com/NEZNAMY/TAB/releases
|
||||
Premium version
|
||||
Polymart (releases): https://polymart.org/resource/484
|
||||
MC-Market (releases): https://www.mc-market.org/resources/14009/
|
||||
Pre-releases on discord for verified buyers
|
||||
Bukkit bridge https://www.spigotmc.org/resources/83966/
|
||||
Wiki https://github.com/NEZNAMY/TAB/wiki
|
||||
Discord invite link: https://discord.gg/EaSvdk6
|
||||
|
||||
|
||||
## Maven Repository
|
||||
```
|
||||
<repositories>
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>TAB</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</parent>
|
||||
<artifactId>bukkit</artifactId>
|
||||
|
||||
<repositories>
|
||||
|
||||
<!-- Spigot API -->
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
|
||||
<!-- PlaceholderAPI -->
|
||||
<repository>
|
||||
<id>placeholderapi</id>
|
||||
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
||||
</repository>
|
||||
|
||||
<!-- Vault -->
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
|
||||
<!-- ViaVersion -->
|
||||
<repository>
|
||||
<id>viaversion</id>
|
||||
<url>https://repo.viaversion.com/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>aikar</id>
|
||||
<url>https://repo.aikar.co/content/groups/aikar/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- TAB Core -->
|
||||
<dependency>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>shared</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.github.NEZNAMY</groupId>
|
||||
<artifactId>YamlAssist</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Spigot API -->
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.17-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- PlaceholderAPI -->
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.10.9</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Vault -->
|
||||
<dependency>
|
||||
<groupId>com.github.MilkBowl</groupId>
|
||||
<artifactId>VaultAPI</artifactId>
|
||||
<version>1.7</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- ViaVersion -->
|
||||
<dependency>
|
||||
<groupId>com.viaversion</groupId>
|
||||
<artifactId>viaversion-api</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>authlib</artifactId>
|
||||
<version>1.11</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+1
-34
@@ -3,13 +3,10 @@ package me.neznamy.tab.platforms.bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
@@ -45,7 +42,7 @@ public class BukkitEventListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onWorldChange(PlayerChangedWorldEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask("processing PlayerChangedWorldEvent", () -> TAB.getInstance().getFeatureManager().onWorldChange(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getPlayer().getWorld().getName()));
|
||||
TAB.getInstance().getCPUManager().runTask("processing PlayerChangedWorldEvent", () -> TAB.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,34 +54,4 @@ public class BukkitEventListener implements Listener {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
if (TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to AsyncPlayerChatEvent to forward the event to features
|
||||
* @param e chat event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onChat(AsyncPlayerChatEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
if (TAB.getInstance().getFeatureManager().onChat(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage(), e.isCancelled())) e.setCancelled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerRespawnEvent to forward the event to features
|
||||
* @param e respawn event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onRespawn(PlayerRespawnEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask("processing PlayerRespawnEvent", () -> TAB.getInstance().getFeatureManager().onRespawn(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerToggleSneakEvent to forward the event to features
|
||||
* @param e sneak event
|
||||
*/
|
||||
@EventHandler
|
||||
public void onSneak(PlayerToggleSneakEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask("processing PlayerToggleSneakEvent", () -> TAB.getInstance().getFeatureManager().onSneak(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.isSneaking()));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -16,7 +16,7 @@ import org.bukkit.plugin.ServicePriority;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.Metrics;
|
||||
|
||||
@@ -97,7 +97,7 @@ public class BukkitMetrics extends Metrics {
|
||||
}));
|
||||
addCustomChart(new SimplePie("server_version", new Callable<String>() {
|
||||
public String call() {
|
||||
return "1." + ProtocolVersion.SERVER_VERSION.getMinorVersion() + ".x";
|
||||
return "1." + NMSStorage.getInstance().minorVersion + ".x";
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,569 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.libs.gson.JsonParser;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.packets.EnumChatFormat;
|
||||
import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.packets.IChatBaseComponent.ClickAction;
|
||||
import me.neznamy.tab.shared.packets.PacketBuilder;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutBoss;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutBoss.Action;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutChat;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerListHeaderFooter;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardDisplayObjective;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardScore;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardTeam;
|
||||
import me.neznamy.tab.shared.rgb.TextColor;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class BukkitPacketBuilder implements PacketBuilder {
|
||||
|
||||
//nms storage
|
||||
private NMSStorage nms;
|
||||
|
||||
//entity type ids
|
||||
private Map<EntityType, Integer> entityIds = new HashMap<EntityType, Integer>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* @param nms - nms storage
|
||||
*/
|
||||
public BukkitPacketBuilder(NMSStorage nms) {
|
||||
this.nms = nms;
|
||||
if (nms.minorVersion >= 13) {
|
||||
entityIds.put(EntityType.ARMOR_STAND, 1);
|
||||
entityIds.put(EntityType.WITHER, 83);
|
||||
} else {
|
||||
entityIds.put(EntityType.WITHER, 64);
|
||||
if (nms.minorVersion >= 8){
|
||||
entityIds.put(EntityType.ARMOR_STAND, 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws Exception {
|
||||
if (nms.minorVersion >= 9) {
|
||||
//1.9+ server, handled using bukkit api
|
||||
return packet;
|
||||
}
|
||||
if (clientVersion.getMinorVersion() >= 9 && Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
|
||||
//1.9+ client on 1.8 server
|
||||
//technically redundant VV check as there is no other way to get 1.9 client on 1.8 server
|
||||
return buildBossPacketVia(packet, clientVersion);
|
||||
}
|
||||
|
||||
//<1.9 client and server
|
||||
return buildBossPacketEntity(packet, clientVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds 1.9 bossbar packet bytebuf using viaversion
|
||||
* @param packet - packet to build
|
||||
* @param clientVersion - client version
|
||||
* @return bytebuf with 1.9 bossbar packet content
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
private ByteBuf buildBossPacketVia(PacketPlayOutBoss packet, ProtocolVersion clientVersion) {
|
||||
if (clientVersion == ProtocolVersion.UNKNOWN) return null; //preventing disconnect if packet ID changes and users do not update
|
||||
try {
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
Type.VAR_INT.writePrimitive(buf, clientVersion.getMinorVersion() == 15 || clientVersion.getMinorVersion() >= 17 ? 0x0D : 0x0C);
|
||||
Type.UUID.write(buf, packet.id);
|
||||
Type.VAR_INT.writePrimitive(buf, packet.operation.ordinal());
|
||||
switch (packet.operation) {
|
||||
case ADD:
|
||||
Type.COMPONENT.write(buf, JsonParser.parseString(IChatBaseComponent.optimizedComponent(packet.name).toString(clientVersion)));
|
||||
Type.FLOAT.writePrimitive(buf, packet.pct);
|
||||
Type.VAR_INT.writePrimitive(buf, packet.color.ordinal());
|
||||
Type.VAR_INT.writePrimitive(buf, packet.overlay.ordinal());
|
||||
Type.BYTE.write(buf, packet.getFlags());
|
||||
break;
|
||||
case REMOVE:
|
||||
break;
|
||||
case UPDATE_PCT:
|
||||
Type.FLOAT.writePrimitive(buf, packet.pct);
|
||||
break;
|
||||
case UPDATE_NAME:
|
||||
Type.COMPONENT.write(buf, JsonParser.parseString(IChatBaseComponent.optimizedComponent(packet.name).toString(clientVersion)));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
Type.VAR_INT.writePrimitive(buf, packet.color.ordinal());
|
||||
Type.VAR_INT.writePrimitive(buf, packet.overlay.ordinal());
|
||||
break;
|
||||
case UPDATE_PROPERTIES:
|
||||
Type.BYTE.write(buf, packet.getFlags());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return buf;
|
||||
} catch (Throwable t) {
|
||||
return TAB.getInstance().getErrorManager().printError(null, "Failed to create 1.9 bossbar packet using ViaVersion v" + Bukkit.getPluginManager().getPlugin("ViaVersion").getDescription().getVersion() + ". Is it the latest version?", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds entity bossbar packet
|
||||
* @param packet - packet to build
|
||||
* @param clientVersion - client version
|
||||
* @return entity bossbar packet
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
private Object buildBossPacketEntity(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws Exception {
|
||||
if (packet.operation == Action.UPDATE_STYLE) return null; //nothing to do here
|
||||
|
||||
int entityId = Math.abs(packet.id.hashCode());
|
||||
if (packet.operation == Action.REMOVE) {
|
||||
return buildEntityDestroyPacket(entityId);
|
||||
}
|
||||
DataWatcher w = new DataWatcher();
|
||||
if (packet.operation == Action.UPDATE_PCT || packet.operation == Action.ADD) {
|
||||
float health = (float)300*packet.pct;
|
||||
if (health == 0) health = 1;
|
||||
w.helper().setHealth(health);
|
||||
}
|
||||
if (packet.operation == Action.UPDATE_NAME || packet.operation == Action.ADD) {
|
||||
w.helper().setCustomName(packet.name, clientVersion);
|
||||
}
|
||||
if (packet.operation == Action.ADD) {
|
||||
w.helper().setEntityFlags((byte) 32);
|
||||
return buildEntitySpawnPacket(entityId, null, EntityType.WITHER, new Location(null, 0,0,0), w);
|
||||
} else {
|
||||
return buildEntityMetadataPacket(entityId, w);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) throws Exception {
|
||||
Object component = stringToComponent(packet.message.toString(clientVersion));
|
||||
if (nms.minorVersion >= 16) {
|
||||
return nms.newPacketPlayOutChat.newInstance(component, ((Object[])nms.ChatMessageType.getMethod("values").invoke(null))[packet.type.ordinal()], UUID.randomUUID());
|
||||
}
|
||||
if (nms.minorVersion >= 12) {
|
||||
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.type.toString()));
|
||||
}
|
||||
if (nms.minorVersion >= 8) {
|
||||
return nms.newPacketPlayOutChat.newInstance(component, (byte) packet.type.ordinal());
|
||||
}
|
||||
if (nms.minorVersion == 7) {
|
||||
return nms.newPacketPlayOutChat.newInstance(component);
|
||||
}
|
||||
throw new IllegalStateException("Not supported on <1.7");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws Exception {
|
||||
if (nms.minorVersion < 8) return null;
|
||||
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(((Object[])nms.EnumPlayerInfoAction.getMethod("values").invoke(null))[packet.action.ordinal()], Array.newInstance(nms.EntityPlayer, 0));
|
||||
List<Object> items = new ArrayList<Object>();
|
||||
for (PlayerInfoData data : packet.entries) {
|
||||
GameProfile profile = new GameProfile(data.uniqueId, data.name);
|
||||
|
||||
if (data.skin != null) profile.getProperties().putAll((PropertyMap) data.skin);
|
||||
List<Object> parameters = new ArrayList<Object>();
|
||||
if (nms.newPlayerInfoData.getParameterCount() == 5) {
|
||||
parameters.add(nmsPacket);
|
||||
}
|
||||
parameters.add(profile);
|
||||
parameters.add(data.latency);
|
||||
Object[] values = (Object[]) nms.EnumGamemode.getMethod("values").invoke(null);
|
||||
parameters.add(data.gameMode == null ? null : values[values.length-EnumGamemode.values().length+data.gameMode.ordinal()]); //not_set was removed in 1.17
|
||||
parameters.add(data.displayName == null ? null : toNMSComponent(data.displayName, clientVersion));
|
||||
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
|
||||
}
|
||||
nms.PacketPlayOutPlayerInfo_PLAYERS.set(nmsPacket, items);
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutPlayerListHeaderFooter packet, ProtocolVersion clientVersion) throws Exception {
|
||||
if (nms.PacketPlayOutPlayerListHeaderFooter == null) return null;
|
||||
if (nms.minorVersion >= 17) {
|
||||
return nms.newPacketPlayOutPlayerListHeaderFooter.newInstance(toNMSComponent(packet.header, clientVersion), toNMSComponent(packet.footer, clientVersion));
|
||||
}
|
||||
Object nmsPacket = nms.newPacketPlayOutPlayerListHeaderFooter.newInstance();
|
||||
nms.PacketPlayOutPlayerListHeaderFooter_HEADER.set(nmsPacket, toNMSComponent(packet.header, clientVersion));
|
||||
nms.PacketPlayOutPlayerListHeaderFooter_FOOTER.set(nmsPacket, toNMSComponent(packet.footer, clientVersion));
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutScoreboardDisplayObjective packet, ProtocolVersion clientVersion) throws Exception {
|
||||
return nms.newPacketPlayOutScoreboardDisplayObjective.newInstance(packet.slot, newScoreboardObjective(packet.objectiveName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutScoreboardObjective packet, ProtocolVersion clientVersion) throws Exception {
|
||||
String displayName = clientVersion.getMinorVersion() < 13 ? cutTo(packet.displayName, 32) : packet.displayName;
|
||||
if (nms.minorVersion >= 13) {
|
||||
return nms.newPacketPlayOutScoreboardObjective.newInstance(nms.newScoreboardObjective.newInstance(null, packet.objectiveName, null,
|
||||
toNMSComponent(IChatBaseComponent.optimizedComponent(displayName), clientVersion),
|
||||
packet.renderType == null ? null : ((Object[])nms.EnumScoreboardHealthDisplay.getMethod("values").invoke(null))[packet.renderType.ordinal()]),
|
||||
packet.method);
|
||||
}
|
||||
|
||||
Object nmsPacket = nms.newPacketPlayOutScoreboardObjective.newInstance();
|
||||
nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME.set(nmsPacket, packet.objectiveName);
|
||||
nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.set(nmsPacket, displayName);
|
||||
if (nms.PacketPlayOutScoreboardObjective_RENDERTYPE != null && packet.renderType != null) {
|
||||
nms.PacketPlayOutScoreboardObjective_RENDERTYPE.set(nmsPacket, Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.renderType.toString()));
|
||||
}
|
||||
nms.PacketPlayOutScoreboardObjective_METHOD.set(nmsPacket, packet.method);
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws Exception {
|
||||
if (nms.minorVersion >= 13) {
|
||||
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(((Object[])nms.EnumScoreboardAction.getMethod("values").invoke(null))[packet.action.ordinal()], packet.objectiveName, packet.player, packet.score);
|
||||
}
|
||||
if (packet.action == PacketPlayOutScoreboardScore.Action.REMOVE) {
|
||||
return nms.newPacketPlayOutScoreboardScore_String.newInstance(packet.player);
|
||||
}
|
||||
Object score = nms.newScoreboardScore.newInstance(nms.newScoreboard.newInstance(), newScoreboardObjective(packet.objectiveName), packet.player);
|
||||
nms.ScoreboardScore_setScore.invoke(score, packet.score);
|
||||
if (nms.minorVersion >= 8) {
|
||||
return nms.newPacketPlayOutScoreboardScore.newInstance(score);
|
||||
}
|
||||
return nms.newPacketPlayOutScoreboardScore.newInstance(score, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) throws Exception {
|
||||
String prefix = packet.playerPrefix;
|
||||
String suffix = packet.playerSuffix;
|
||||
if (clientVersion.getMinorVersion() < 13) {
|
||||
prefix = cutTo(prefix, 16);
|
||||
suffix = cutTo(suffix, 16);
|
||||
}
|
||||
Object team = nms.newScoreboardTeam.newInstance(nms.newScoreboard.newInstance(), packet.name);
|
||||
((Collection<String>)nms.ScoreboardTeam_getPlayerNameSet.invoke(team)).addAll(packet.players);
|
||||
if (nms.minorVersion >= 13) {
|
||||
if (prefix != null && prefix.length() > 0) nms.ScoreboardTeam_setPrefix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(prefix), clientVersion));
|
||||
if (suffix != null && suffix.length() > 0) nms.ScoreboardTeam_setSuffix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(suffix), clientVersion));
|
||||
EnumChatFormat format = packet.color != null ? packet.color : EnumChatFormat.lastColorsOf(prefix);
|
||||
nms.ScoreboardTeam_setColor.invoke(team, ((Object[])nms.EnumChatFormat.getMethod("values").invoke(null))[format.ordinal()]);
|
||||
} else {
|
||||
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, prefix);
|
||||
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, suffix);
|
||||
}
|
||||
if (nms.EnumNameTagVisibility != null && packet.nametagVisibility != null) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, packet.nametagVisibility.equals("always") ? ((Object[])nms.EnumNameTagVisibility.getMethod("values").invoke(null))[0] : ((Object[])nms.EnumNameTagVisibility.getMethod("values").invoke(null))[1]);
|
||||
if (nms.EnumTeamPush != null && packet.collisionRule != null) nms.ScoreboardTeam_setCollisionRule.invoke(team, packet.collisionRule.equals("always") ? ((Object[])nms.EnumTeamPush.getMethod("values").invoke(null))[0] : ((Object[])nms.EnumTeamPush.getMethod("values").invoke(null))[1]);
|
||||
if (nms.minorVersion >= 17) {
|
||||
switch (packet.method) {
|
||||
case 0:
|
||||
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, true);
|
||||
case 1:
|
||||
return nms.PacketPlayOutScoreboardTeam_of.invoke(null, team);
|
||||
case 2:
|
||||
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, false);
|
||||
case 3:
|
||||
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.players.toArray(new String[0])[0], ((Object[])nms.PacketPlayOutScoreboardTeam_a.getMethod("values").invoke(null))[0]);
|
||||
case 4:
|
||||
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.players.toArray(new String[0])[0], ((Object[])nms.PacketPlayOutScoreboardTeam_a.getMethod("values").invoke(null))[1]);
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid action: " + packet.method);
|
||||
}
|
||||
}
|
||||
|
||||
return nms.newPacketPlayOutScoreboardTeam.newInstance(team, packet.method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds entity destroy packet with given parameter
|
||||
* @param id - entity id to destroy
|
||||
* @return destroy packet
|
||||
* @throws Exception - if reflection fails
|
||||
*/
|
||||
public Object buildEntityDestroyPacket(int id) throws Exception {
|
||||
if (nms.minorVersion >= 17) {
|
||||
return nms.newPacketPlayOutEntityDestroy.newInstance(id);
|
||||
}
|
||||
return nms.newPacketPlayOutEntityDestroy.newInstance(new int[] {id});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds entity metadata packet with given parameters
|
||||
* @param entityId - entity id
|
||||
* @param dataWatcher - datawatcher
|
||||
* @return metadata packet
|
||||
* @throws Exception - if reflection fails
|
||||
*/
|
||||
public Object buildEntityMetadataPacket(int entityId, DataWatcher dataWatcher) throws Exception {
|
||||
return nms.newPacketPlayOutEntityMetadata.newInstance(entityId, dataWatcher.toNMS(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds entity spawn packet with given parameters
|
||||
* @param entityId - entity id
|
||||
* @param uuid - entity uuid
|
||||
* @param entityType - entity type
|
||||
* @param loc - location to spawn at
|
||||
* @param dataWatcher - datawatcher
|
||||
* @return entity spawn packet
|
||||
* @throws Exception - if reflection fails
|
||||
*/
|
||||
public Object buildEntitySpawnPacket(int entityId, UUID uuid, EntityType entityType, Location loc, DataWatcher dataWatcher) throws Exception {
|
||||
Object nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance(nms.getHandle.invoke(Bukkit.getOnlinePlayers().iterator().next()));
|
||||
nms.PacketPlayOutSpawnEntityLiving_ENTITYID.set(nmsPacket, entityId);
|
||||
nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE.set(nmsPacket, entityIds.get(entityType));
|
||||
nms.PacketPlayOutSpawnEntityLiving_YAW.set(nmsPacket, (byte)(loc.getYaw() * 256.0f / 360.0f));
|
||||
nms.PacketPlayOutSpawnEntityLiving_PITCH.set(nmsPacket, (byte)(loc.getPitch() * 256.0f / 360.0f));
|
||||
if (nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER != null) {
|
||||
nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.set(nmsPacket, dataWatcher.toNMS());
|
||||
}
|
||||
if (nms.minorVersion >= 9) {
|
||||
nms.PacketPlayOutSpawnEntityLiving_UUID.set(nmsPacket, uuid);
|
||||
nms.PacketPlayOutSpawnEntityLiving_X.set(nmsPacket, loc.getX());
|
||||
nms.PacketPlayOutSpawnEntityLiving_Y.set(nmsPacket, loc.getY());
|
||||
nms.PacketPlayOutSpawnEntityLiving_Z.set(nmsPacket, loc.getZ());
|
||||
} else {
|
||||
nms.PacketPlayOutSpawnEntityLiving_X.set(nmsPacket, floor((double)loc.getX()*32));
|
||||
nms.PacketPlayOutSpawnEntityLiving_Y.set(nmsPacket, floor((double)loc.getY()*32));
|
||||
nms.PacketPlayOutSpawnEntityLiving_Z.set(nmsPacket, floor((double)loc.getZ()*32));
|
||||
}
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds entity teleport packet with given parameters
|
||||
* @param entityId - entity id
|
||||
* @param location - location to teleport to
|
||||
* @return entity teleport packet
|
||||
* @throws Exception - if reflection fails
|
||||
*/
|
||||
public Object buildEntityTeleportPacket(int entityId, Location location) throws Exception {
|
||||
Object nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance(nms.getHandle.invoke(Bukkit.getOnlinePlayers().iterator().next()));
|
||||
nms.PacketPlayOutEntityTeleport_ENTITYID.set(nmsPacket, entityId);
|
||||
if (nms.minorVersion >= 9) {
|
||||
nms.PacketPlayOutEntityTeleport_X.set(nmsPacket, location.getX());
|
||||
nms.PacketPlayOutEntityTeleport_Y.set(nmsPacket, location.getY());
|
||||
nms.PacketPlayOutEntityTeleport_Z.set(nmsPacket, location.getZ());
|
||||
} else {
|
||||
nms.PacketPlayOutEntityTeleport_X.set(nmsPacket, floor((double)location.getX()*32));
|
||||
nms.PacketPlayOutEntityTeleport_Y.set(nmsPacket, floor((double)location.getY()*32));
|
||||
nms.PacketPlayOutEntityTeleport_Z.set(nmsPacket, floor((double)location.getZ()*32));
|
||||
}
|
||||
nms.PacketPlayOutEntityTeleport_YAW.set(nmsPacket, (byte)((float)location.getYaw()/360*256));
|
||||
nms.PacketPlayOutEntityTeleport_PITCH.set(nmsPacket, (byte)((float)location.getPitch()/360*256));
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutPlayerInfo readPlayerInfo(Object nmsPacket, ProtocolVersion clientVersion) throws Exception {
|
||||
if (nms.minorVersion < 8) return null;
|
||||
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
|
||||
List<PlayerInfoData> listData = new ArrayList<PlayerInfoData>();
|
||||
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
|
||||
Object nmsGamemode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
|
||||
EnumGamemode gamemode = (nmsGamemode == null) ? null : EnumGamemode.valueOf(nmsGamemode.toString());
|
||||
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
|
||||
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
|
||||
IChatBaseComponent listName = nmsComponent == null ? null : fromNMSComponent(nmsComponent);
|
||||
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), profile.getProperties(), (int)nms.PlayerInfoData_getLatency.invoke(nmsData), gamemode, listName));
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(action, listData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutScoreboardObjective readObjective(Object nmsPacket, ProtocolVersion clientVersion) throws Exception {
|
||||
String objective = (String) nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME.get(nmsPacket);
|
||||
String displayName;
|
||||
if (nms.minorVersion >= 13) {
|
||||
Object component = nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.get(nmsPacket);
|
||||
IChatBaseComponent c = component == null ? null : fromNMSComponent(component);
|
||||
displayName = c == null ? null : c.toLegacyText();
|
||||
} else {
|
||||
displayName = (String) nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.get(nmsPacket);
|
||||
}
|
||||
EnumScoreboardHealthDisplay renderType = null;
|
||||
if (nms.PacketPlayOutScoreboardObjective_RENDERTYPE != null) {
|
||||
Object nmsRender = nms.PacketPlayOutScoreboardObjective_RENDERTYPE.get(nmsPacket);
|
||||
if (nmsRender != null) {
|
||||
renderType = EnumScoreboardHealthDisplay.valueOf(nmsRender.toString());
|
||||
}
|
||||
}
|
||||
int method = nms.PacketPlayOutScoreboardObjective_METHOD.getInt(nmsPacket);
|
||||
return new PacketPlayOutScoreboardObjective(method, objective, displayName, renderType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object nmsPacket, ProtocolVersion clientVersion) throws Exception {
|
||||
return new PacketPlayOutScoreboardDisplayObjective(
|
||||
nms.PacketPlayOutScoreboardDisplayObjective_POSITION.getInt(nmsPacket),
|
||||
(String) nms.PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME.get(nmsPacket)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A method yoinked from minecraft code used to convert double to int
|
||||
* @param paramDouble double value
|
||||
* @return int value
|
||||
*/
|
||||
private int floor(double paramDouble){
|
||||
int i = (int)paramDouble;
|
||||
return paramDouble < i ? i - 1 : i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts json string into a component
|
||||
* @param json json as string
|
||||
* @return NMS component
|
||||
* @throws Exception if something fails
|
||||
*/
|
||||
public Object stringToComponent(String json) throws Exception {
|
||||
if (json == null) return null;
|
||||
return nms.ChatSerializer_DESERIALIZE.invoke(null, json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts minecraft IChatBaseComponent into TAB's component class. Currently does not support hover event.
|
||||
* @param component - component to convert
|
||||
* @return converted component
|
||||
* @throws Exception if something fails
|
||||
*/
|
||||
public IChatBaseComponent fromNMSComponent(Object component) throws Exception {
|
||||
long time = System.nanoTime();
|
||||
IChatBaseComponent obj = fromNMSComponent0(component);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("fromNMSComponent", System.nanoTime()-time);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//separate method to prevent extras counting cpu again due to recursion and finally showing higher usage than real
|
||||
@SuppressWarnings("rawtypes")
|
||||
public IChatBaseComponent fromNMSComponent0(Object component) throws Exception {
|
||||
if (!nms.ChatComponentText.isInstance(component)) return null; //paper
|
||||
IChatBaseComponent chat = new IChatBaseComponent((String) nms.ChatComponentText_text.get(component));
|
||||
Object modifier = nms.ChatBaseComponent_modifier.get(component);
|
||||
if (modifier != null) {
|
||||
Object color = nms.ChatModifier_color.get(modifier);
|
||||
if (color != null) {
|
||||
if (nms.minorVersion >= 16) {
|
||||
String name = (String) nms.ChatHexColor_name.get(color);
|
||||
if (name != null) {
|
||||
//legacy code
|
||||
chat.setColor(new TextColor(EnumChatFormat.valueOf(name.toUpperCase())));
|
||||
} else {
|
||||
int rgb = (int) nms.ChatHexColor_rgb.get(color);
|
||||
chat.setColor(new TextColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF));
|
||||
}
|
||||
} else {
|
||||
chat.setColor(new TextColor(EnumChatFormat.valueOf(((Enum)color).name())));
|
||||
}
|
||||
}
|
||||
chat.setBold((Boolean) nms.ChatModifier_bold.get(modifier));
|
||||
chat.setItalic((Boolean) nms.ChatModifier_italic.get(modifier));
|
||||
chat.setObfuscated((Boolean) nms.ChatModifier_obfuscated.get(modifier));
|
||||
chat.setStrikethrough((Boolean) nms.ChatModifier_strikethrough.get(modifier));
|
||||
chat.setUnderlined((Boolean) nms.ChatModifier_underlined.get(modifier));
|
||||
Object clickEvent = nms.ChatModifier_clickEvent.get(modifier);
|
||||
if (clickEvent != null) {
|
||||
chat.onClick(ClickAction.valueOf(nms.ChatClickable_action.get(clickEvent).toString().toUpperCase()), (String) nms.ChatClickable_value.get(clickEvent));
|
||||
}
|
||||
/* Object hoverEvent = nms.ChatModifier_hoverEvent.get(modifier);
|
||||
if (hoverEvent != null) {
|
||||
chat.onHover(HoverAction.fromString(nms.ChatHoverable_action.get(hoverEvent).toString()), fromNMSComponent(nms.ChatHoverable_value.get(hoverEvent)));
|
||||
}*/
|
||||
}
|
||||
for (Object extra : (List<Object>) nms.ChatBaseComponent_extra.get(component)) {
|
||||
chat.addExtra(fromNMSComponent0(extra));
|
||||
}
|
||||
return chat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts TAB's IChatBaseComponent into minecraft's component. Currently does not support hover event.
|
||||
* @param component - component to convert
|
||||
* @param clientVersion - client version used to decide RGB conversion
|
||||
* @return converted component
|
||||
* @throws Exception if something fails
|
||||
*/
|
||||
public Object toNMSComponent(IChatBaseComponent component, ProtocolVersion clientVersion) throws Exception {
|
||||
long time = System.nanoTime();
|
||||
Object obj = toNMSComponent0(component, clientVersion);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("toNMSComponent", System.nanoTime()-time);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//separate method to prevent extras counting cpu again due to recursion and finally showing higher usage than real
|
||||
private Object toNMSComponent0(IChatBaseComponent component, ProtocolVersion clientVersion) throws Exception {
|
||||
if (component == null) return null;
|
||||
Object chat = nms.newChatComponentText.newInstance(component.getText());
|
||||
Object modifier;
|
||||
if (nms.minorVersion >= 16) {
|
||||
Object color = null;
|
||||
if (component.getColor() != null) {
|
||||
if (clientVersion.getMinorVersion() >= 16) {
|
||||
color = nms.ChatHexColor_ofInt.invoke(null, (component.getColor().getRed() << 16) + (component.getColor().getGreen() << 8) + component.getColor().getBlue());
|
||||
} else {
|
||||
color = nms.ChatHexColor_ofString.invoke(null, component.getColor().getLegacyColor().toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
modifier = nms.newChatModifier.newInstance(
|
||||
color,
|
||||
component.getBold(),
|
||||
component.getItalic(),
|
||||
component.getUnderlined(),
|
||||
component.getStrikethrough(),
|
||||
component.getObfuscated(),
|
||||
component.getClickAction() == null ? null : nms.newChatClickable.newInstance(nms.EnumClickAction_a.invoke(null, component.getClickAction().toString().toLowerCase()), component.getClickValue().toString()),
|
||||
null, //component.getHoverAction() == null ? null : nms.newChatHoverable.newInstance(nms.EnumHoverAction_a.invoke(null, component.getHoverAction().toString().toLowerCase()), stringToComponent(component.getHoverValue().toString())),
|
||||
null, null
|
||||
);
|
||||
} else {
|
||||
modifier = nms.newChatModifier.newInstance();
|
||||
if (component.getClickAction() != null){
|
||||
nms.ChatModifier_clickEvent.set(modifier, nms.newChatClickable.newInstance(nms.EnumClickAction_a.invoke(null, component.getClickAction().toString().toLowerCase()), component.getClickValue().toString()));
|
||||
}
|
||||
/* if (component.getHoverAction() != null) {
|
||||
nms.ChatModifier_hoverEvent.set(modifier, nms.newChatHoverable.newInstance(nms.EnumHoverAction_a.invoke(null, (component.getHoverAction().toString().toLowerCase())), stringToComponent(component.getHoverValue().toString())));
|
||||
}*/
|
||||
if (component.getColor() != null) nms.ChatModifier_color.set(modifier, Enum.valueOf(nms.EnumChatFormat, component.getColor().getLegacyColor().toString()));
|
||||
if (component.isBold()) nms.ChatModifier_bold.set(modifier, true);
|
||||
if (component.isItalic()) nms.ChatModifier_italic.set(modifier, true);
|
||||
if (component.isUnderlined()) nms.ChatModifier_underlined.set(modifier, true);
|
||||
if (component.isStrikethrough()) nms.ChatModifier_strikethrough.set(modifier, true);
|
||||
if (component.isObfuscated()) nms.ChatModifier_obfuscated.set(modifier, true);
|
||||
}
|
||||
nms.ChatBaseComponent_modifier.set(chat, modifier);
|
||||
for (IChatBaseComponent extra : component.getExtra()) {
|
||||
nms.ChatComponentText_addSibling.invoke(chat, toNMSComponent0(extra, clientVersion));
|
||||
}
|
||||
return chat;
|
||||
}
|
||||
|
||||
private Object newScoreboardObjective(String objectiveName) throws Exception {
|
||||
if (nms.minorVersion >= 13) {
|
||||
return nms.newScoreboardObjective.newInstance(null, objectiveName, null, stringToComponent("{\"text\":\"\"}"), null);
|
||||
}
|
||||
return nms.newScoreboardObjective.newInstance(null, objectiveName, nms.getFields(nms.IScoreboardCriteria, nms.IScoreboardCriteria).get(0).get(null));
|
||||
}
|
||||
}
|
||||
+41
-23
@@ -16,11 +16,18 @@ import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
|
||||
public class BukkitPipelineInjector extends PipelineInjector {
|
||||
|
||||
//handler to inject before
|
||||
private final String INJECT_POSITION = "packet_handler";
|
||||
|
||||
//nms storage
|
||||
private NMSStorage nms;
|
||||
|
||||
public BukkitPipelineInjector(TAB tab, NMSStorage nms) throws ClassNotFoundException {
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
* @param tab - tab instance
|
||||
* @param nms - nms storage
|
||||
*/
|
||||
public BukkitPipelineInjector(TAB tab, NMSStorage nms){
|
||||
super(tab);
|
||||
this.nms = nms;
|
||||
}
|
||||
@@ -49,34 +56,48 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all real players from team if packet does not come from TAB and reports this to override log
|
||||
* @param packetPlayOutScoreboardTeam - team packet
|
||||
* @throws Exception - if reflection fails
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void modifyPlayers(Object packetPlayOutScoreboardTeam) throws Exception {
|
||||
long time = System.nanoTime();
|
||||
if (nms.PacketPlayOutScoreboardTeam_SIGNATURE.getInt(packetPlayOutScoreboardTeam) != 69) {
|
||||
Collection<String> players = (Collection<String>) nms.PacketPlayOutScoreboardTeam_PLAYERS.get(packetPlayOutScoreboardTeam);
|
||||
//creating a new list to prevent NoSuchFieldException in minecraft packet encoder when a player is removed
|
||||
Collection<String> newList = new ArrayList<String>();
|
||||
for (String entry : players) {
|
||||
TabPlayer p = tab.getPlayer(entry);
|
||||
if (p == null) {
|
||||
newList.add(entry);
|
||||
continue;
|
||||
}
|
||||
if (tab.getFeatureManager().getNameTagFeature().isDisabledWorld(p.getWorldName())) {
|
||||
newList.add(entry);
|
||||
} else {
|
||||
logTeamOverride((String) nms.PacketPlayOutScoreboardTeam_NAME.get(packetPlayOutScoreboardTeam), entry);
|
||||
}
|
||||
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<String>();
|
||||
for (String entry : players) {
|
||||
TabPlayer p = tab.getPlayer(entry);
|
||||
if (p == null) {
|
||||
newList.add(entry);
|
||||
continue;
|
||||
}
|
||||
if (!tab.getFeatureManager().getNameTagFeature().isDisabledWorld(p.getWorldName()) && !p.hasTeamHandlingPaused() &&
|
||||
!p.getTeamName().equals(teamName)) {
|
||||
logTeamOverride(teamName, entry);
|
||||
} else {
|
||||
newList.add(entry);
|
||||
}
|
||||
nms.PacketPlayOutScoreboardTeam_PLAYERS.set(packetPlayOutScoreboardTeam, newList);
|
||||
}
|
||||
nms.PacketPlayOutScoreboardTeam_PLAYERS.set(packetPlayOutScoreboardTeam, newList);
|
||||
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
public class BukkitChannelDuplexHandler extends ChannelDuplexHandler {
|
||||
|
||||
//injected player
|
||||
private TabPlayer player;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player
|
||||
* @param player - player to inject
|
||||
*/
|
||||
public BukkitChannelDuplexHandler(TabPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
@@ -98,20 +119,17 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
super.write(context, tab.getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
|
||||
return;
|
||||
}
|
||||
if (tab.getFeatureManager().getNameTagFeature() != null && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
|
||||
if (tab.getFeatureManager().getNameTagFeature() != null && antiOverrideTeams && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
|
||||
modifyPlayers(packet);
|
||||
super.write(context, packet, channelPromise);
|
||||
return;
|
||||
}
|
||||
if (nms.PacketPlayOutScoreboardDisplayObjective.isInstance(packet) && tab.getFeatureManager().onDisplayObjective(player, packet)) {
|
||||
if (nms.PacketPlayOutScoreboardDisplayObjective.isInstance(packet) && antiOverrideObjectives && tab.getFeatureManager().onDisplayObjective(player, packet)) {
|
||||
return;
|
||||
}
|
||||
if (nms.PacketPlayOutScoreboardObjective.isInstance(packet)) {
|
||||
if (antiOverrideObjectives && nms.PacketPlayOutScoreboardObjective.isInstance(packet)) {
|
||||
tab.getFeatureManager().onObjective(player, packet);
|
||||
}
|
||||
if (nms.PacketPlayOutPlayerListHeaderFooter.isInstance(packet) && tab.getFeatureManager().onHeaderFooter(player, packet)) {
|
||||
return;
|
||||
}
|
||||
tab.getFeatureManager().onPacketSend(player, packet);
|
||||
} catch (Throwable e){
|
||||
tab.getErrorManager().printError("An error occurred when reading packets", e);
|
||||
+82
-38
@@ -1,7 +1,7 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
@@ -17,6 +17,7 @@ import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManager;
|
||||
import me.neznamy.tab.shared.placeholders.Placeholder;
|
||||
@@ -31,13 +32,29 @@ import net.milkbowl.vault.economy.Economy;
|
||||
*/
|
||||
public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
|
||||
//formatter for 2 decimal places
|
||||
public final DecimalFormat decimal2 = ((DecimalFormat)NumberFormat.getNumberInstance(Locale.US));
|
||||
|
||||
|
||||
//plugin instance
|
||||
private JavaPlugin plugin;
|
||||
|
||||
//vault economy
|
||||
private Economy economy;
|
||||
|
||||
//vault chat
|
||||
private Chat chat;
|
||||
|
||||
//placeholder registry buffer
|
||||
private List<Placeholder> placeholders;
|
||||
|
||||
//essentials methods
|
||||
private Method getUser;
|
||||
private Method getNickname;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* @param plugin - plugin instance
|
||||
*/
|
||||
public BukkitPlaceholderRegistry(JavaPlugin plugin) {
|
||||
decimal2.applyPattern("#.##");
|
||||
this.plugin = plugin;
|
||||
@@ -47,6 +64,14 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
RegisteredServiceProvider<Chat> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
|
||||
if (rspChat != null) chat = rspChat.getProvider();
|
||||
}
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
|
||||
try {
|
||||
getUser = Class.forName("com.earth2me.essentials.Essentials").getMethod("getUser", Player.class);
|
||||
getNickname = Class.forName("com.earth2me.essentials.User").getMethod("getNickname");
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to load essentials methods", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,17 +79,6 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
placeholders = new ArrayList<Placeholder>();
|
||||
placeholders.add(new PlayerPlaceholder("%money%", 1000) {
|
||||
public String get(TabPlayer p) {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
|
||||
try {
|
||||
Object essentials = Bukkit.getPluginManager().getPlugin("Essentials");
|
||||
Object user = essentials.getClass().getMethod("getUser", Player.class).invoke(essentials, p.getPlayer());
|
||||
BigDecimal money = (BigDecimal) user.getClass().getMethod("getMoney").invoke(user);
|
||||
return decimal2.format(money.doubleValue());
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get player balance of " + p.getName() + " using Essentials", e);
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
if (economy != null) return decimal2.format(economy.getBalance((Player) p.getPlayer()));
|
||||
return "-";
|
||||
}
|
||||
@@ -77,14 +91,16 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
});
|
||||
placeholders.add(new PlayerPlaceholder("%deaths%", 1000) {
|
||||
public String get(TabPlayer p) {
|
||||
return ((Player) p.getPlayer()).getStatistic(Statistic.DEATHS)+"";
|
||||
}
|
||||
});
|
||||
placeholders.add(new PlayerPlaceholder("%health%", 100) {
|
||||
public String get(TabPlayer p) {
|
||||
return (int) Math.ceil(((Player) p.getPlayer()).getHealth())+"";
|
||||
return String.valueOf(((Player) p.getPlayer()).getStatistic(Statistic.DEATHS));
|
||||
}
|
||||
});
|
||||
if (NMSStorage.getInstance().minorVersion >= 6) {
|
||||
placeholders.add(new PlayerPlaceholder("%health%", 100) {
|
||||
public String get(TabPlayer p) {
|
||||
return String.valueOf((int) Math.ceil(((Player) p.getPlayer()).getHealth()));
|
||||
}
|
||||
});
|
||||
}
|
||||
placeholders.add(new ServerPlaceholder("%tps%", 1000) {
|
||||
public String get() {
|
||||
try {
|
||||
@@ -102,7 +118,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
for (TabPlayer all : TAB.getInstance().getPlayers()){
|
||||
if (((Player) p.getPlayer()).canSee((Player) all.getPlayer())) var++;
|
||||
}
|
||||
return var+"";
|
||||
return String.valueOf(var);
|
||||
}
|
||||
});
|
||||
placeholders.add(new PlayerPlaceholder("%canseestaffonline%", 2000) {
|
||||
@@ -111,7 +127,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
for (TabPlayer all : TAB.getInstance().getPlayers()){
|
||||
if (all.isStaff() && ((Player) p.getPlayer()).canSee((Player) all.getPlayer())) var++;
|
||||
}
|
||||
return var+"";
|
||||
return String.valueOf(var);
|
||||
}
|
||||
});
|
||||
registerAFKPlaceholder();
|
||||
@@ -122,6 +138,9 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers AFK placeholder
|
||||
*/
|
||||
private void registerAFKPlaceholder() {
|
||||
String noAfk = TAB.getInstance().getConfiguration().config.getString("placeholders.afk-no", "");
|
||||
String yesAfk = TAB.getInstance().getConfiguration().config.getString("placeholders.afk-yes", " &4*&4&lAFK&4*&r");
|
||||
@@ -155,6 +174,14 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
} catch (Throwable t) {
|
||||
return TAB.getInstance().getErrorManager().printError("", "Failed to check AFK status of " + p.getName(), t);
|
||||
}
|
||||
if (!afk) {
|
||||
try {
|
||||
//purpur AFK API
|
||||
afk = (boolean) p.getPlayer().getClass().getMethod("isAfk").invoke(p.getPlayer());
|
||||
} catch (Throwable t) {
|
||||
//not purpur
|
||||
}
|
||||
}
|
||||
return afk ? yesAfk : noAfk;
|
||||
}
|
||||
@Override
|
||||
@@ -164,20 +191,31 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers vault placeholders
|
||||
*/
|
||||
private void registerVaultPlaceholders() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Vault") && chat != null) {
|
||||
placeholders.add(new PlayerPlaceholder("%vault-prefix%", 500) {
|
||||
|
||||
public String get(TabPlayer p) {
|
||||
String prefix = chat.getPlayerPrefix((Player) p.getPlayer());
|
||||
return prefix != null ? prefix : "";
|
||||
try {
|
||||
String prefix = chat.getPlayerPrefix((Player) p.getPlayer());
|
||||
return prefix != null ? prefix : "";
|
||||
} catch (Exception e) {
|
||||
return TAB.getInstance().getErrorManager().printError("", "Placeholder %vault-prefix% threw an exception", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
placeholders.add(new PlayerPlaceholder("%vault-suffix%", 500) {
|
||||
|
||||
public String get(TabPlayer p) {
|
||||
String suffix = chat.getPlayerSuffix((Player) p.getPlayer());
|
||||
return suffix != null ? suffix : "";
|
||||
try {
|
||||
String suffix = chat.getPlayerSuffix((Player) p.getPlayer());
|
||||
return suffix != null ? suffix : "";
|
||||
} catch (Exception e) {
|
||||
return TAB.getInstance().getErrorManager().printError("", "Placeholder %vault-suffix% threw an exception", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -194,40 +232,43 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers position placeholders
|
||||
*/
|
||||
private void registerPositionPlaceholders() {
|
||||
placeholders.add(new PlayerPlaceholder("%xPos%", 100) {
|
||||
public String get(TabPlayer p) {
|
||||
return ((Player) p.getPlayer()).getLocation().getBlockX()+"";
|
||||
return String.valueOf(((Player) p.getPlayer()).getLocation().getBlockX());
|
||||
}
|
||||
});
|
||||
placeholders.add(new PlayerPlaceholder("%yPos%", 100) {
|
||||
public String get(TabPlayer p) {
|
||||
return ((Player) p.getPlayer()).getLocation().getBlockY()+"";
|
||||
return String.valueOf(((Player) p.getPlayer()).getLocation().getBlockY());
|
||||
}
|
||||
});
|
||||
placeholders.add(new PlayerPlaceholder("%zPos%", 100) {
|
||||
public String get(TabPlayer p) {
|
||||
return ((Player) p.getPlayer()).getLocation().getBlockZ()+"";
|
||||
return String.valueOf(((Player) p.getPlayer()).getLocation().getBlockZ());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers essentials placeholders
|
||||
*/
|
||||
private void registerEssentialsPlaceholders() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
|
||||
placeholders.add(new PlayerPlaceholder("%essentialsnick%", 1000) {
|
||||
public String get(TabPlayer p) {
|
||||
if (getNickname == null) return "<Internal plugin error>";
|
||||
String name = null;
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
|
||||
try {
|
||||
Object essentials = Bukkit.getPluginManager().getPlugin("Essentials");
|
||||
Object user = essentials.getClass().getMethod("getUser", Player.class).invoke(essentials, p.getPlayer());
|
||||
name = (String) user.getClass().getMethod("getNickname").invoke(user);
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get Essentials nickname of " + p.getName(), e);
|
||||
}
|
||||
try {
|
||||
name = (String) getNickname.invoke(getUser.invoke(Bukkit.getPluginManager().getPlugin("Essentials"), p.getPlayer()));
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get Essentials nickname of " + p.getName(), e);
|
||||
}
|
||||
if (name == null || name.length() == 0) return p.getName();
|
||||
return TAB.getInstance().getConfiguration().getSecretOption("essentials-nickname-prefix", "") + name;
|
||||
return TAB.getInstance().getConfiguration().essentialsNickPrefix + name;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -239,6 +280,9 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers synchronous placeholders
|
||||
*/
|
||||
private void registerSyncPlaceholders() {
|
||||
PlaceholderManager pl = TAB.getInstance().getPlaceholderManager();
|
||||
for (String identifier : pl.allUsedPlaceholderIdentifiers) {
|
||||
@@ -267,7 +311,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
|
||||
TAB.getInstance().getCPUManager().addPlaceholderTime(getIdentifier(), System.nanoTime()-time);
|
||||
}
|
||||
});
|
||||
return getLastValue(p);
|
||||
return lastValue.get(p.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
+76
-67
@@ -1,12 +1,8 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -25,11 +21,14 @@ import me.neznamy.tab.platforms.bukkit.features.unlimitedtags.NameTagX;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.permission.Vault;
|
||||
import me.neznamy.tab.shared.Platform;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.config.ConfigurationFile;
|
||||
import me.neznamy.tab.shared.features.NameTag16;
|
||||
import me.neznamy.tab.shared.features.BelowName;
|
||||
import me.neznamy.tab.shared.features.NameTag;
|
||||
import me.neznamy.tab.shared.features.PingSpoof;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManager;
|
||||
import me.neznamy.tab.shared.features.SpectatorFix;
|
||||
import me.neznamy.tab.shared.features.TabObjective;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
|
||||
import me.neznamy.tab.shared.permission.LuckPerms;
|
||||
import me.neznamy.tab.shared.permission.None;
|
||||
import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
@@ -46,10 +45,27 @@ import net.milkbowl.vault.permission.Permission;
|
||||
*/
|
||||
public class BukkitPlatform implements Platform {
|
||||
|
||||
//list of used expansions
|
||||
private Set<String> usedExpansions;
|
||||
|
||||
//plugin instance
|
||||
private JavaPlugin plugin;
|
||||
|
||||
//nms storage
|
||||
private NMSStorage nms;
|
||||
|
||||
//booleans to check plugin presence
|
||||
public boolean placeholderAPI;
|
||||
public boolean viaversion;
|
||||
public boolean idisguise;
|
||||
public boolean libsdisguises;
|
||||
public boolean essentials;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
* @param plugin - plugin instance
|
||||
* @param nms - nms storage
|
||||
*/
|
||||
public BukkitPlatform(JavaPlugin plugin, NMSStorage nms) {
|
||||
this.plugin = plugin;
|
||||
this.nms = nms;
|
||||
@@ -70,21 +86,30 @@ public class BukkitPlatform implements Platform {
|
||||
|
||||
@Override
|
||||
public void loadFeatures() throws Exception {
|
||||
int version = ProtocolVersion.SERVER_VERSION.getMinorVersion();
|
||||
placeholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
|
||||
viaversion = Bukkit.getPluginManager().isPluginEnabled("ViaVersion");
|
||||
idisguise = Bukkit.getPluginManager().isPluginEnabled("iDisguise");
|
||||
libsdisguises = Bukkit.getPluginManager().isPluginEnabled("LibsDisguises");
|
||||
essentials = Bukkit.getPluginManager().isPluginEnabled("Essentials");
|
||||
TAB tab = TAB.getInstance();
|
||||
usedExpansions = new HashSet<String>();
|
||||
tab.getPlaceholderManager().addRegistry(new BukkitPlaceholderRegistry(plugin));
|
||||
tab.getPlaceholderManager().addRegistry(new UniversalPlaceholderRegistry());
|
||||
tab.getPlaceholderManager().registerPlaceholders();
|
||||
if (version >= 8) {
|
||||
if (nms.minorVersion >= 8 && tab.getConfiguration().pipelineInjection) {
|
||||
tab.getFeatureManager().registerFeature("injection", new BukkitPipelineInjector(tab, nms));
|
||||
}
|
||||
loadNametagFeature(tab);
|
||||
loadUniversalFeatures();
|
||||
if (tab.getConfiguration().BossBarEnabled && version < 9) tab.getFeatureManager().registerFeature("bossbar1.8", new BossBar_legacy(tab));
|
||||
if (version >= 9 && tab.getConfiguration().config.getBoolean("fix-pet-names", false)) tab.getFeatureManager().registerFeature("petfix", new PetFix(nms));
|
||||
if (tab.getConfiguration().config.getBoolean("ping-spoof.enabled", false)) tab.getFeatureManager().registerFeature("pingspoof", new PingSpoof());
|
||||
if (tab.getConfiguration().config.getString("yellow-number-in-tablist", "%ping%").length() > 0) tab.getFeatureManager().registerFeature("tabobjective", new TabObjective(tab));
|
||||
if (tab.getConfiguration().config.getBoolean("do-not-move-spectators", false)) tab.getFeatureManager().registerFeature("spectatorfix", new SpectatorFix());
|
||||
if (tab.getConfiguration().config.getBoolean("classic-vanilla-belowname.enabled", true)) tab.getFeatureManager().registerFeature("belowname", new BelowName(tab));
|
||||
if (tab.getConfiguration().premiumconfig != null && tab.getConfiguration().premiumconfig.getBoolean("scoreboard.enabled", false)) tab.getFeatureManager().registerFeature("scoreboard", new ScoreboardManager(tab));
|
||||
if (tab.getConfiguration().bossbar.getBoolean("bossbar-enabled", false) && nms.minorVersion < 9) tab.getFeatureManager().registerFeature("bossbar1.8", new BossBar_legacy(tab, plugin));
|
||||
if (nms.minorVersion >= 9 && tab.getConfiguration().config.getBoolean("fix-pet-names", false)) tab.getFeatureManager().registerFeature("petfix", new PetFix(nms));
|
||||
if (tab.getConfiguration().config.getBoolean("per-world-playerlist.enabled", false)) tab.getFeatureManager().registerFeature("pwp", new PerWorldPlayerlist(plugin, tab));
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
||||
if (placeholderAPI) {
|
||||
new TabExpansion(plugin);
|
||||
new ExpansionDownloader(plugin).download(usedExpansions);
|
||||
}
|
||||
@@ -94,16 +119,25 @@ public class BukkitPlatform implements Platform {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads nametag feature from config
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
private void loadNametagFeature(TAB tab) {
|
||||
if (tab.getConfiguration().config.getBoolean("change-nametag-prefix-suffix", true)) {
|
||||
if (tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.enabled", false) && ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 8) {
|
||||
if (tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.enabled", false) && nms.minorVersion >= 8) {
|
||||
tab.getFeatureManager().registerFeature("nametagx", new NameTagX(plugin, nms, tab));
|
||||
} else {
|
||||
tab.getFeatureManager().registerFeature("nametag16", new NameTag16(tab));
|
||||
tab.getFeatureManager().registerFeature("nametag16", new NameTag(tab));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of online players from Bukkit API
|
||||
* @return list of online players from Bukkit API
|
||||
* @throws Exception - if reflection fails
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Player[] getOnlinePlayers() throws Exception {
|
||||
Object players = Bukkit.class.getMethod("getOnlinePlayers").invoke(null);
|
||||
@@ -146,6 +180,11 @@ public class BukkitPlatform implements Platform {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers server placeholder
|
||||
* @param identifier - placeholder identifier
|
||||
* @param refresh - refresh interval in milliseconds
|
||||
*/
|
||||
private void registerServerPlaceholder(String identifier, int refresh) {
|
||||
TAB.getInstance().getPlaceholderManager().registerPlaceholder(new ServerPlaceholder(identifier, TAB.getInstance().getErrorManager().fixPlaceholderInterval(identifier, refresh)){
|
||||
|
||||
@@ -156,6 +195,11 @@ public class BukkitPlatform implements Platform {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers player placeholder
|
||||
* @param identifier - placeholder identifier
|
||||
* @param refresh - refresh interval in milliseconds
|
||||
*/
|
||||
private void registerPlayerPlaceholder(String identifier, int refresh) {
|
||||
TAB.getInstance().getPlaceholderManager().registerPlaceholder(new PlayerPlaceholder(identifier, TAB.getInstance().getErrorManager().fixPlaceholderInterval(identifier, refresh)) {
|
||||
|
||||
@@ -166,12 +210,17 @@ public class BukkitPlatform implements Platform {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers relational placeholder
|
||||
* @param identifier - placeholder identifier
|
||||
* @param refresh - refresh interval in milliseconds
|
||||
*/
|
||||
private void registerRelationalPlaceholder(String identifier, int refresh) {
|
||||
TAB.getInstance().getPlaceholderManager().registerPlaceholder(new RelationalPlaceholder(identifier, TAB.getInstance().getErrorManager().fixPlaceholderInterval(identifier, refresh)) {
|
||||
|
||||
@Override
|
||||
public String get(TabPlayer viewer, TabPlayer target) {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) return identifier;
|
||||
if (!placeholderAPI) return identifier;
|
||||
try {
|
||||
return PlaceholderAPI.setRelationalPlaceholders((Player) viewer.getPlayer(), (Player) target.getPlayer(), identifier);
|
||||
} catch (Throwable t) {
|
||||
@@ -182,8 +231,14 @@ public class BukkitPlatform implements Platform {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs PlaceholderAPI call and returns the output. If the task fails, error is logged and "ERROR" returned instead
|
||||
* @param player - player to set placeholder for
|
||||
* @param placeholder - placeholder
|
||||
* @return result from PlaceholderAPI
|
||||
*/
|
||||
public String setPlaceholders(Player player, String placeholder) {
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) return placeholder;
|
||||
if (!placeholderAPI) return placeholder;
|
||||
try {
|
||||
return PlaceholderAPI.setPlaceholders(player, placeholder);
|
||||
} catch (Throwable t) {
|
||||
@@ -193,57 +248,6 @@ public class BukkitPlatform implements Platform {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertConfig(ConfigurationFile config) {
|
||||
convertUniversalOptions(config);
|
||||
if (config.getName().equals("config.yml")) {
|
||||
removeOld(config, "nametag-refresh-interval-ticks");
|
||||
removeOld(config, "tablist-refresh-interval-ticks");
|
||||
removeOld(config, "header-footer-refresh-interval-ticks");
|
||||
removeOld(config, "belowname.refresh-interval-ticks");
|
||||
removeOld(config, "placeholders.deluxetag-yes");
|
||||
removeOld(config, "placeholders.deluxetag-no");
|
||||
removeOld(config, "placeholders.faction-yes");
|
||||
removeOld(config, "placeholders.faction-no");
|
||||
removeOld(config, "staff-groups");
|
||||
removeOld(config, "use-essentials-nickname");
|
||||
removeOld(config, "deluxetag-empty-value");
|
||||
removeOld(config, "factions-faction");
|
||||
removeOld(config, "factions-nofaction");
|
||||
removeOld(config, "date-format");
|
||||
removeOld(config, "time-format");
|
||||
removeOld(config, "relational-placeholders-refresh");
|
||||
removeOld(config, "bukkit-bridge-mode");
|
||||
if (config.hasConfigOption("tablist-objective")) {
|
||||
String type = config.getString("tablist-objective");
|
||||
String value;
|
||||
if (type.equals("NONE")) {
|
||||
value = "";
|
||||
} else if (type.equals("PING")){
|
||||
value = "%ping%";
|
||||
} else if (type.equals("HEARTS")) {
|
||||
value = "%health%";
|
||||
} else {
|
||||
value = config.getString("tablist-objective-custom-value");
|
||||
}
|
||||
config.set("tablist-objective", null);
|
||||
config.set("tablist-objective-custom-value", null);
|
||||
config.set("yellow-number-in-tablist", value);
|
||||
TAB.getInstance().print('2', "Converted old tablist-objective config option to new yellow-number-in-tablist");
|
||||
}
|
||||
if (config.getObject("per-world-playerlist") instanceof Boolean) {
|
||||
rename(config, "per-world-playerlist", "per-world-playerlist.enabled");
|
||||
rename(config, "allow-pwp-bypass-permission", "per-world-playerlist.allow-bypass-permission");
|
||||
rename(config, "ignore-pwp-in-worlds", "per-world-playerlist.ignore-effect-in-worlds");
|
||||
Map<String, List<String>> sharedWorlds = new HashMap<String, List<String>>();
|
||||
sharedWorlds.put("lobby", Arrays.asList("lobby1", "lobby2"));
|
||||
sharedWorlds.put("minigames", Arrays.asList("paintball", "bedwars"));
|
||||
config.set("per-world-playerlist.shared-playerlist-world-groups", sharedWorlds);
|
||||
TAB.getInstance().print('2', "Converted old per-world-playerlist section to new one in advancedconfig.yml.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerVersion() {
|
||||
return Bukkit.getBukkitVersion().split("-")[0] + " (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")";
|
||||
@@ -287,4 +291,9 @@ public class BukkitPlatform implements Platform {
|
||||
public int getMaxPlayers() {
|
||||
return Bukkit.getMaxPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigName() {
|
||||
return "bukkitconfig.yml";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.boss.BarFlag;
|
||||
import org.bukkit.boss.BarStyle;
|
||||
import org.bukkit.boss.BossBar;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutBoss;
|
||||
import me.neznamy.tab.shared.rgb.RGBUtils;
|
||||
|
||||
/**
|
||||
* TabPlayer for Bukkit
|
||||
*/
|
||||
public class BukkitTabPlayer extends ITabPlayer {
|
||||
|
||||
//bukkit player
|
||||
private Player player;
|
||||
|
||||
//nms handle
|
||||
private Object handle;
|
||||
|
||||
//nms player connection
|
||||
private Object playerConnection;
|
||||
|
||||
//player's visible boss bars
|
||||
private Map<UUID, BossBar> bossbars = new HashMap<UUID, BossBar>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* @param p - bukkit player
|
||||
*/
|
||||
public BukkitTabPlayer(Player p){
|
||||
player = p;
|
||||
world = p.getWorld().getName();
|
||||
try {
|
||||
handle = NMSStorage.getInstance().getHandle.invoke(player);
|
||||
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get playerConnection of " + p.getName(), e);
|
||||
}
|
||||
try {
|
||||
if (NMSStorage.getInstance().CHANNEL != null)
|
||||
channel = (Channel) NMSStorage.getInstance().CHANNEL.get(NMSStorage.getInstance().NETWORK_MANAGER.get(playerConnection));
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get channel of " + p.getName(), e);
|
||||
}
|
||||
uniqueId = p.getUniqueId();
|
||||
name = p.getName();
|
||||
version = ProtocolVersion.fromNetworkId(getProtocolVersion());
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets protocol version and returns it
|
||||
* @return protocol version of this player
|
||||
*/
|
||||
private int getProtocolVersion() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport")){
|
||||
int version = getProtocolVersionPS();
|
||||
//some PS versions return -1 on unsupported server versions instead of throwing exception
|
||||
if (version != -1 && version < ProtocolVersion.SERVER_VERSION.getNetworkId()) return version;
|
||||
}
|
||||
if (((BukkitPlatform)TAB.getInstance().getPlatform()).viaversion) {
|
||||
return getProtocolVersionVia();
|
||||
}
|
||||
return ProtocolVersion.SERVER_VERSION.getNetworkId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns protocol version of this player using ProtocolSupport
|
||||
* @return protocol version of this player using ProtocolSupport
|
||||
*/
|
||||
private int getProtocolVersionPS(){
|
||||
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 " + getName());
|
||||
return version;
|
||||
} catch (Throwable e) {
|
||||
return TAB.getInstance().getErrorManager().printError(ProtocolVersion.SERVER_VERSION.getNetworkId(), "Failed to get protocol version of " + getName() + " using ProtocolSupport", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns protocol version of this player using ViaVersion
|
||||
* @return protocol version of this player using ViaVersion
|
||||
*/
|
||||
private int getProtocolVersionVia(){
|
||||
try {
|
||||
int version = Via.getAPI().getPlayerVersion(uniqueId);
|
||||
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + getName());
|
||||
return version;
|
||||
} catch (Throwable e) {
|
||||
return TAB.getInstance().getErrorManager().printError(ProtocolVersion.SERVER_VERSION.getNetworkId(), "Failed to get protocol version of " + getName() + " using ViaVersion v" + Bukkit.getPluginManager().getPlugin("ViaVersion").getDescription().getVersion() + ". Is it the latest version?", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPing() {
|
||||
try {
|
||||
int ping = NMSStorage.getInstance().PING.getInt(handle);
|
||||
if (ping > 10000 || ping < 0) ping = -1;
|
||||
return ping;
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Object nmsPacket) {
|
||||
if (nmsPacket == null || !player.isOnline()) return;
|
||||
try {
|
||||
if (((BukkitPlatform)TAB.getInstance().getPlatform()).viaversion && nmsPacket instanceof ByteBuf) {
|
||||
try {
|
||||
Via.getAPI().sendRawPacket(uniqueId, (ByteBuf) nmsPacket);
|
||||
} catch (IllegalArgumentException e) {
|
||||
//java.lang.IllegalArgumentException: This player is not controlled by ViaVersion!
|
||||
//this is only used to send 1.9 bossbar packets on 1.8 servers, no idea why it does this sometimes
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (nmsPacket instanceof PacketPlayOutBoss) {
|
||||
handle((PacketPlayOutBoss) nmsPacket);
|
||||
return;
|
||||
}
|
||||
NMSStorage.getInstance().sendPacket.invoke(playerConnection, nmsPacket);
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when sending " + nmsPacket.getClass().getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(PacketPlayOutBoss packet) {
|
||||
Set<BarFlag> flags = new HashSet<BarFlag>();
|
||||
BossBar bar;
|
||||
switch (packet.operation) {
|
||||
case ADD:
|
||||
if (packet.createWorldFog) flags.add(BarFlag.CREATE_FOG);
|
||||
if (packet.darkenScreen) flags.add(BarFlag.DARKEN_SKY);
|
||||
if (packet.playMusic) flags.add(BarFlag.PLAY_BOSS_MUSIC);
|
||||
bar = Bukkit.createBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.name, getVersion().getMinorVersion() >= 16 && NMSStorage.getInstance().minorVersion >= 16),
|
||||
BarColor.valueOf(packet.color.name()),
|
||||
BarStyle.valueOf(packet.overlay.name().replace("PROGRESS", "SOLID").replace("NOTCHED", "SEGMENTED")),
|
||||
flags.toArray(new BarFlag[0]));
|
||||
bossbars.put(packet.id, bar);
|
||||
bar.addPlayer(player);
|
||||
break;
|
||||
case REMOVE:
|
||||
bossbars.get(packet.id).removePlayer(player);
|
||||
bossbars.remove(packet.id);
|
||||
break;
|
||||
case UPDATE_PCT:
|
||||
bossbars.get(packet.id).setProgress(packet.pct);
|
||||
break;
|
||||
case UPDATE_NAME:
|
||||
bossbars.get(packet.id).setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.name, getVersion().getMinorVersion() >= 16 && NMSStorage.getInstance().minorVersion >= 16));
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
bar = bossbars.get(packet.id);
|
||||
//yet another platform with yet another buggy bossbar API, how lovely it is regressing back to APIs instead of internals
|
||||
bar.removePlayer(player);
|
||||
bar.setColor(BarColor.valueOf(packet.color.name()));
|
||||
bar.setStyle(BarStyle.valueOf(packet.overlay.name().replace("PROGRESS", "SOLID").replace("NOTCHED", "SEGMENTED")));
|
||||
bar.addPlayer(player);
|
||||
break;
|
||||
case UPDATE_PROPERTIES:
|
||||
bar = bossbars.get(packet.id);
|
||||
processFlag(bar, packet.createWorldFog, BarFlag.CREATE_FOG);
|
||||
processFlag(bar, packet.darkenScreen, BarFlag.DARKEN_SKY);
|
||||
processFlag(bar, packet.playMusic, BarFlag.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInvisibilityPotion() {
|
||||
return player.hasPotionEffect(PotionEffectType.INVISIBILITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisguised() {
|
||||
return isDisguisedLD() || isDisguisediDis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns disguise status of player using LibsDisguises
|
||||
* @return disguise status of player using LibsDisguises
|
||||
*/
|
||||
private boolean isDisguisedLD() {
|
||||
try {
|
||||
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).libsdisguises) return false;
|
||||
return (boolean) Class.forName("me.libraryaddict.disguise.DisguiseAPI").getMethod("isDisguised", Entity.class).invoke(null, player);
|
||||
} catch (Throwable e) {
|
||||
return TAB.getInstance().getErrorManager().printError(false, "Failed to check disguise status using LibsDisguises", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns disguise status of player using iDisguise
|
||||
* @return disguise status of player using iDisguise
|
||||
*/
|
||||
private boolean isDisguisediDis() {
|
||||
try {
|
||||
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).idisguise) return false;
|
||||
RegisteredServiceProvider<?> provider = Bukkit.getServicesManager().getRegistration(Class.forName("de.robingrether.idisguise.api.DisguiseAPI"));
|
||||
Object iDisguise = provider.getProvider();
|
||||
Method m = iDisguise.getClass().getMethod("isDisguised", Player.class);
|
||||
m.setAccessible(true);
|
||||
return (boolean) m.invoke(iDisguise, player);
|
||||
} catch (Throwable e) {
|
||||
return TAB.getInstance().getErrorManager().printError(false, "Failed to check disguise status using iDisguise", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSkin() {
|
||||
try {
|
||||
return Class.forName("com.mojang.authlib.GameProfile").getMethod("getProperties").invoke(NMSStorage.getInstance().getProfile.invoke(handle));
|
||||
} catch (Throwable e) {
|
||||
return TAB.getInstance().getErrorManager().printError(null, "Failed to get skin of " + getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return player.isOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanished() {
|
||||
try {
|
||||
if (((BukkitPlatform)TAB.getInstance().getPlatform()).essentials) {
|
||||
Object essentials = Bukkit.getPluginManager().getPlugin("Essentials");
|
||||
Object user = essentials.getClass().getMethod("getUser", Player.class).invoke(essentials, player);
|
||||
boolean vanished = (boolean) user.getClass().getMethod("isVanished").invoke(user);
|
||||
if (vanished) return true;
|
||||
}
|
||||
if (player.hasMetadata("vanished") && !player.getMetadata("vanished").isEmpty()) {
|
||||
return player.getMetadata("vanished").get(0).asBoolean();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to check vanish status of " + player.getName(), e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public int getGamemode() {
|
||||
return player.getGameMode().getValue();
|
||||
}
|
||||
}
|
||||
+19
-4
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -7,6 +8,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
@@ -35,18 +37,30 @@ public class Main extends JavaPlugin {
|
||||
sender.sendMessage(message.replace('&', '\u00a7'));
|
||||
}
|
||||
} else {
|
||||
TAB.getInstance().command.execute(sender instanceof Player ? TAB.getInstance().getPlayer(((Player)sender).getUniqueId()) : null, args);
|
||||
TabPlayer p = null;
|
||||
if (sender instanceof Player) {
|
||||
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
|
||||
if (p == null) return false; //player not loaded correctly
|
||||
}
|
||||
TAB.getInstance().command.execute(p, args);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
Bukkit.getPluginCommand("tab").setTabCompleter((sender, c, cmd, args) -> TAB.getInstance().command.complete(sender instanceof Player ? TAB.getInstance().getPlayer(((Player)sender).getUniqueId()) : null, args));
|
||||
Bukkit.getPluginCommand("tab").setTabCompleter((sender, c, cmd, args) -> {
|
||||
TabPlayer p = null;
|
||||
if (sender instanceof Player) {
|
||||
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
|
||||
if (p == null) return new ArrayList<String>(); //player not loaded correctly
|
||||
}
|
||||
return TAB.getInstance().command.complete(p, args);
|
||||
});
|
||||
TAB.getInstance().load();
|
||||
new BukkitMetrics(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
//null check due to L22 return making L27 not run
|
||||
//null check due to L23 return making L30 not run
|
||||
if (TAB.getInstance() != null) TAB.getInstance().unload();
|
||||
}
|
||||
|
||||
@@ -56,9 +70,10 @@ public class Main extends JavaPlugin {
|
||||
*/
|
||||
private boolean isVersionSupported(){
|
||||
List<String> SUPPORTED_VERSIONS = 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_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1");
|
||||
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
try {
|
||||
NMSStorage.setInstance(new NMSStorage());
|
||||
@@ -0,0 +1,87 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitPacketBuilder;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBar;
|
||||
import me.neznamy.tab.shared.features.types.Loadable;
|
||||
|
||||
/**
|
||||
* An additional class with additional code for <1.9 servers due to an entity being required
|
||||
*/
|
||||
public class BossBar_legacy implements Listener, Loadable {
|
||||
|
||||
//distance of wither in blocks
|
||||
private final int WITHER_DISTANCE = 75;
|
||||
|
||||
//main bossbar feature
|
||||
private BossBar mainFeature;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the class
|
||||
* @param mainFeature - main bossbar feature
|
||||
*/
|
||||
public BossBar_legacy(TAB tab, JavaPlugin plugin) {
|
||||
this.mainFeature = (BossBar) tab.getFeatureManager().getFeature("bossbar");
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
//bar disappears in client after ~1 second of not seeing boss entity
|
||||
tab.getCPUManager().startRepeatingMeasuredTask(900, "refreshing bossbar", TabFeature.BOSSBAR, UsageType.TELEPORTING_ENTITY, () -> teleport());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
teleport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates wither location for all players
|
||||
*/
|
||||
private void teleport() {
|
||||
for (TabPlayer all : TAB.getInstance().getPlayers()) {
|
||||
if (all.getVersion().getMinorVersion() > 8) continue; //sending VV packets to those
|
||||
for (me.neznamy.tab.api.bossbar.BossBar l : all.getActiveBossBars()) {
|
||||
try {
|
||||
all.sendPacket(((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).buildEntityTeleportPacket(Math.abs(l.getUniqueId().hashCode()), getWitherLocation(all)), TabFeature.BOSSBAR);
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to create PacketPlayOutEntityTeleport", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void 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", TabFeature.BOSSBAR, UsageType.PLAYER_RESPAWN_EVENT, () -> mainFeature.detectBossBarsAndSend(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns location where wither should be located based on where player is looking
|
||||
* @param p - player to get wither location for
|
||||
* @return location of wither
|
||||
*/
|
||||
public Location getWitherLocation(TabPlayer p) {
|
||||
Player pl = (Player) p.getPlayer();
|
||||
Location loc = pl.getEyeLocation().add(pl.getEyeLocation().getDirection().normalize().multiply(WITHER_DISTANCE));
|
||||
if (loc.getY() < 1) loc.setY(1);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -46,8 +46,8 @@ public class ExpansionDownloader {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
//initial delay to give PlaceholderAPI time to load
|
||||
Thread.sleep(5000);
|
||||
//initial delay to give PlaceholderAPI & plugins with expansions time to load
|
||||
Thread.sleep(10000);
|
||||
TAB.getInstance().debug("Used expansions: " + expansions.toString());
|
||||
TAB.getInstance().debug("Registered expansions in PlaceholderAPI: " + PlaceholderAPI.getRegisteredIdentifiers());
|
||||
expansions.removeAll(PlaceholderAPI.getRegisteredIdentifiers());
|
||||
@@ -66,6 +66,9 @@ public class ExpansionDownloader {
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException | ConcurrentModificationException e) {
|
||||
//fast shutdown probably
|
||||
} catch (NoClassDefFoundError e){
|
||||
//PAPI class not found, fast shutdown probably
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to download PlaceholderAPI expansions. PlaceholderAPI version: " + Bukkit.getPluginManager().getPlugin("PlaceholderAPI").getDescription().getVersion(), e);
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
import me.neznamy.tab.shared.features.types.Loadable;
|
||||
|
||||
/**
|
||||
* Per-world-playerlist feature handler
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PerWorldPlayerlist implements Loadable, Listener {
|
||||
|
||||
//plugin instance
|
||||
private JavaPlugin plugin;
|
||||
|
||||
//config options
|
||||
private boolean allowBypass;
|
||||
private List<String> ignoredWorlds;
|
||||
private Map<String, List<String>> sharedWorlds;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
* @param plugin - plugin instance
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
public PerWorldPlayerlist(JavaPlugin plugin, TAB tab) {
|
||||
this.plugin = plugin;
|
||||
allowBypass = tab.getConfiguration().config.getBoolean("per-world-playerlist.allow-bypass-permission", false);
|
||||
ignoredWorlds = tab.getConfiguration().config.getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
|
||||
sharedWorlds = tab.getConfiguration().config.getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
|
||||
for (String group : sharedWorlds.keySet()) {
|
||||
if (sharedWorlds.get(group).size() == 1) {
|
||||
tab.getErrorManager().startupWarn("World group \"" + group + "\" in per-world-playerlist only contain a single world (\"" + sharedWorlds.get(group).get(0) +
|
||||
"\"), which has no effect and only makes config less readable. Delete the group entirely for a cleaner config.");
|
||||
}
|
||||
}
|
||||
tab.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(p -> checkPlayer(p)));
|
||||
}
|
||||
|
||||
@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(TabFeature.PER_WORLD_PLAYERLIST, UsageType.PLAYER_JOIN_EVENT, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldChange(PlayerChangedWorldEvent e) {
|
||||
long time = System.nanoTime();
|
||||
checkPlayer(e.getPlayer());
|
||||
TAB.getInstance().getCPUManager().addTime(TabFeature.PER_WORLD_PLAYERLIST, UsageType.WORLD_SWITCH_EVENT, 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("tab.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 (String group : sharedWorlds.keySet()) {
|
||||
if (sharedWorlds.get(group) != null) {
|
||||
if (sharedWorlds.get(group).contains(viewer.getWorld().getName())) viewerWorldGroup = group;
|
||||
if (sharedWorlds.get(group).contains(target.getWorld().getName())) targetWorldGroup = group;
|
||||
}
|
||||
}
|
||||
return viewerWorldGroup.equals(targetWorldGroup);
|
||||
}
|
||||
}
|
||||
+24
-10
@@ -9,7 +9,7 @@ import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.RawPacketListener;
|
||||
@@ -17,19 +17,27 @@ import me.neznamy.tab.shared.features.types.packet.RawPacketListener;
|
||||
/**
|
||||
* 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 implements RawPacketListener, QuitEventListener {
|
||||
|
||||
//datawatcher position of pet owner field
|
||||
private int PET_OWNER_POSITION = getPetOwnerPosition();
|
||||
private int PET_OWNER_POSITION;
|
||||
|
||||
//logger of last interacts to prevent feature not working on 1.16
|
||||
private Map<String, Long> lastInteractFix = new HashMap<String, Long>();
|
||||
|
||||
//nms storage
|
||||
private NMSStorage nms;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* @param nms
|
||||
*/
|
||||
public PetFix(NMSStorage nms) {
|
||||
this.nms = nms;
|
||||
PET_OWNER_POSITION = getPetOwnerPosition();
|
||||
TAB.getInstance().debug("Loaded PetFix feature");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,13 +45,16 @@ public class PetFix implements RawPacketListener, QuitEventListener {
|
||||
* @return position of pet owner field based on server version
|
||||
*/
|
||||
private int getPetOwnerPosition() {
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 15) {
|
||||
if (nms.minorVersion >= 17) {
|
||||
//1.17.x
|
||||
return 18;
|
||||
} else if (nms.minorVersion >= 15) {
|
||||
//1.15.x, 1.16.x
|
||||
return 17;
|
||||
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 14) {
|
||||
} else if (nms.minorVersion >= 14) {
|
||||
//1.14.x
|
||||
return 16;
|
||||
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 10) {
|
||||
} else if (nms.minorVersion >= 10) {
|
||||
//1.10.x - 1.13.x
|
||||
return 14;
|
||||
} else {
|
||||
@@ -81,19 +92,22 @@ public class PetFix implements RawPacketListener, QuitEventListener {
|
||||
if (items == null) return;
|
||||
List<Object> newList = new ArrayList<Object>();
|
||||
for (Object item : items) {
|
||||
DataWatcherItem i = DataWatcherItem.fromNMS(item);
|
||||
if (i.type.position == PET_OWNER_POSITION && i.value.toString().startsWith("Optional")) continue;
|
||||
newList.add(nms.newDataWatcherItem.newInstance(nms.newDataWatcherObject.newInstance(i.type.position, i.type.classType), i.value));
|
||||
if (nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item)) == PET_OWNER_POSITION) {
|
||||
Object value = nms.DataWatcherItem_VALUE.get(item);
|
||||
if (value.getClass().getSimpleName().equals("Optional")) continue;
|
||||
}
|
||||
newList.add(item);
|
||||
}
|
||||
nms.PacketPlayOutEntityMetadata_LIST.set(packet, newList);
|
||||
}
|
||||
//<1.15
|
||||
if (nms.PacketPlayOutSpawnEntityLiving.isInstance(packet) && nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER != null) {
|
||||
DataWatcher watcher = DataWatcher.fromNMS(nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.get(packet));
|
||||
DataWatcherItem petOwner = watcher.getItem(PET_OWNER_POSITION);
|
||||
if (petOwner != null && petOwner.value.toString().startsWith("Optional")) {
|
||||
if (petOwner != null && petOwner.value.getClass().getSimpleName().equals("Optional")) {
|
||||
watcher.removeValue(PET_OWNER_POSITION);
|
||||
nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.set(packet, watcher.toNMS());
|
||||
}
|
||||
nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.set(packet, watcher.toNMS());
|
||||
}
|
||||
}
|
||||
|
||||
+19
-3
@@ -60,13 +60,13 @@ public class TabExpansion extends PlaceholderExpansion {
|
||||
if (player == null) return "";
|
||||
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
|
||||
if (identifier.equals("scoreboard_visible")) {
|
||||
return p.isScoreboardVisible() ? "Enabled" : "Disabled";
|
||||
return translate(p.isScoreboardVisible());
|
||||
}
|
||||
if (identifier.equals("bossbar_visible")) {
|
||||
return p.hasBossbarVisible() ? "Enabled" : "Disabled";
|
||||
return translate(p.hasBossbarVisible());
|
||||
}
|
||||
if (identifier.equals("ntpreview")) {
|
||||
return p.isPreviewingNametag() ? "Enabled" : "Disabled";
|
||||
return translate(p.isPreviewingNametag());
|
||||
}
|
||||
if (identifier.startsWith("replace_")) {
|
||||
return findReplacement("%" + identifier.substring(8) + "%", player);
|
||||
@@ -78,6 +78,16 @@ public class TabExpansion extends PlaceholderExpansion {
|
||||
return getProperty(identifier, p);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!TAB.getInstance().isPremium()) return "Placeholder output replacements require premium version to work";
|
||||
String output = ((BukkitPlatform) TAB.getInstance().getPlatform()).setPlaceholders(player, placeholder);
|
||||
@@ -85,6 +95,12 @@ public class TabExpansion extends PlaceholderExpansion {
|
||||
return Placeholder.findReplacement(replacements, 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);
|
||||
+33
-12
@@ -10,6 +10,7 @@ import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Pose;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
@@ -171,7 +172,7 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
if (this.sneaking == sneaking) return; //idk
|
||||
this.sneaking = sneaking;
|
||||
for (TabPlayer viewer : getNearbyPlayers()) {
|
||||
if (viewer.getVersion().getMinorVersion() == 14 && !(boolean) TAB.getInstance().getConfiguration().getSecretOption("unlimited-nametag-prefix-suffix-mode.always-visible", false)) {
|
||||
if (viewer.getVersion().getMinorVersion() == 14 && !TAB.getInstance().getConfiguration().armorStandsAlwaysVisible) {
|
||||
//1.14.x client sided bug, despawning completely
|
||||
if (sneaking) {
|
||||
viewer.sendPacket(getDestroyPacket(), TabFeature.NAMETAGX);
|
||||
@@ -221,6 +222,10 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns destroy packet
|
||||
* @return destroy packet
|
||||
*/
|
||||
private Object getDestroyPacket() {
|
||||
try {
|
||||
return ((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).buildEntityDestroyPacket(entityId);
|
||||
@@ -229,6 +234,11 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns metadata packet with specified datawatcher
|
||||
* @param dataWatcher - datawatcher
|
||||
* @return metadata packet
|
||||
*/
|
||||
private Object getMetadataPacket(DataWatcher dataWatcher) {
|
||||
try {
|
||||
return ((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).buildEntityMetadataPacket(entityId, dataWatcher);
|
||||
@@ -237,6 +247,12 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns spawn packet with specified parameters
|
||||
* @param loc - location to spawn at
|
||||
* @param dataWatcher - datawatcher
|
||||
* @return spawn packet
|
||||
*/
|
||||
private Object getSpawnPacket(Location loc, DataWatcher dataWatcher) {
|
||||
try {
|
||||
return ((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).buildEntitySpawnPacket(entityId, uuid, EntityType.ARMOR_STAND, loc, dataWatcher);
|
||||
@@ -259,9 +275,9 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
* @return true if should be visible, false if not
|
||||
*/
|
||||
private boolean getVisibility() {
|
||||
if (((BukkitTabPlayer)owner).isDisguised() || (((NameTagX)TAB.getInstance().getFeatureManager().getFeature("nametagx")).disableOnBoats && owner.isOnBoat())) return false;
|
||||
if ((boolean) TAB.getInstance().getConfiguration().getSecretOption("unlimited-nametag-prefix-suffix-mode.always-visible", false)) return true;
|
||||
return !player.hasPotionEffect(PotionEffectType.INVISIBILITY) && player.getGameMode() != GameMode.SPECTATOR && !owner.hasHiddenNametag() && property.get().length() > 0 && !owner.isOnBoat();
|
||||
if (((BukkitTabPlayer)owner).isDisguised() || (((NameTagX)TAB.getInstance().getFeatureManager().getFeature("nametagx")).playersOnBoats.contains(owner))) return false;
|
||||
if (TAB.getInstance().getConfiguration().armorStandsAlwaysVisible) return true;
|
||||
return !player.hasPotionEffect(PotionEffectType.INVISIBILITY) && player.getGameMode() != GameMode.SPECTATOR && !owner.hasHiddenNametag() && property.get().length() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,28 +305,33 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
* @return correct player's Y
|
||||
*/
|
||||
private double getY() {
|
||||
//1.14+ bukkit api bug
|
||||
//1.14+ server sided bug
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (vehicle != null) {
|
||||
if (vehicle.getType() == EntityType.HORSE) {
|
||||
if (vehicle.getType().toString().contains("HORSE")) { //covering all 3 horse types
|
||||
return vehicle.getLocation().getY() + 0.85;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("DONKEY")) {
|
||||
if (vehicle.getType() == EntityType.DONKEY) {
|
||||
return vehicle.getLocation().getY() + 0.525;
|
||||
}
|
||||
if (vehicle.getType() == EntityType.PIG) {
|
||||
return vehicle.getLocation().getY() + 0.325;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("STRIDER")) { //preventing errors on <1.16
|
||||
if (vehicle.getType().toString().equals("STRIDER")) { //added in 1.16
|
||||
return vehicle.getLocation().getY() + 1.15;
|
||||
}
|
||||
}
|
||||
//1.13+ swimming
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 13 && player.isSwimming()) {
|
||||
return player.getLocation().getY()-1.22;
|
||||
}
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9 && player.isGliding())) {
|
||||
return player.getLocation().getY()-1.22;
|
||||
}
|
||||
return player.getLocation().getY();
|
||||
}
|
||||
|
||||
private boolean isSwimming() {
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
|
||||
return ProtocolVersion.SERVER_VERSION.getMinorVersion() == 13 && player.isSwimming();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
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.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
|
||||
/**
|
||||
* The event listener part for securing proper functionality of armor stands
|
||||
*/
|
||||
public class EventListener implements Listener {
|
||||
|
||||
//the nametag feature handler
|
||||
private NameTagX feature;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
* @param feature - nametagx feature handler
|
||||
*/
|
||||
public EventListener(NameTagX feature) {
|
||||
this.feature = feature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sneak event listener to despawn & 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.isDisabledWorld(p.getWorldName()) || feature.isDisabledWorld(feature.disabledUnlimitedWorlds, p.getWorldName())) return;
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerToggleSneakEvent", TabFeature.NAMETAGX, UsageType.PLAYER_TOGGLE_SNEAK_EVENT, () -> {
|
||||
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", TabFeature.NAMETAGX, UsageType.PLAYER_RESPAWN_EVENT, () -> {
|
||||
TabPlayer respawned = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (feature.isDisabledWorld(respawned.getWorldName()) || feature.isDisabledWorld(feature.disabledUnlimitedWorlds, respawned.getWorldName())) return;
|
||||
respawned.getArmorStandManager().teleport();
|
||||
});
|
||||
}
|
||||
}
|
||||
+178
-91
@@ -3,9 +3,11 @@ package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -16,8 +18,6 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.ArmorStandManager;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -28,25 +28,24 @@ import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
import me.neznamy.tab.shared.features.NameTag;
|
||||
import me.neznamy.tab.shared.features.types.Loadable;
|
||||
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.RespawnEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.SneakEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
|
||||
|
||||
/**
|
||||
* The core class for unlimited nametag mode
|
||||
*/
|
||||
public class NameTagX extends NameTag implements Loadable, JoinEventListener, QuitEventListener, WorldChangeListener, RespawnEventListener, SneakEventListener {
|
||||
public class NameTagX extends NameTag {
|
||||
|
||||
private final int ENTITY_TRACKING_RANGE = 48;
|
||||
|
||||
private JavaPlugin plugin;
|
||||
//config options
|
||||
public boolean markerFor18x;
|
||||
public boolean disableOnBoats;
|
||||
private double spaceBetweenLines;
|
||||
|
||||
//list of worlds with unlimited nametag mode disabled
|
||||
protected List<String> disabledUnlimitedWorlds;
|
||||
|
||||
//list of defined dynamic lines
|
||||
public List<String> dynamicLines = Arrays.asList("belowname", "nametag", "abovename");
|
||||
|
||||
//map of defined static lines
|
||||
public Map<String, Object> staticLines = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
//entity id counter to pick unique entity IDs
|
||||
@@ -54,15 +53,33 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
|
||||
//player data by entityId, used for better performance
|
||||
public Map<Integer, TabPlayer> entityIdMap = new ConcurrentHashMap<Integer, TabPlayer>();
|
||||
public Map<Integer, List<Entity>> vehicles = new ConcurrentHashMap<>();
|
||||
|
||||
//map of vehicles carrying players
|
||||
public Map<Integer, List<Entity>> vehicles = new ConcurrentHashMap<Integer, List<Entity>>();
|
||||
|
||||
//bukkit event listener
|
||||
private EventListener eventListener;
|
||||
|
||||
//list of players currently on boats
|
||||
public List<TabPlayer> playersOnBoats = new ArrayList<TabPlayer>();
|
||||
|
||||
//list of players currently in a vehicle
|
||||
private Map<TabPlayer, Entity> playersInVehicle = new ConcurrentHashMap<TabPlayer, Entity>();
|
||||
|
||||
private Map<TabPlayer, Location> playerLocations = new ConcurrentHashMap<TabPlayer, Location>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
* @param plugin - plugin instance
|
||||
* @param nms - nms storage
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
public NameTagX(JavaPlugin plugin, NMSStorage nms, TAB tab) {
|
||||
super(tab);
|
||||
this.plugin = plugin;
|
||||
markerFor18x = tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.use-marker-tag-for-1-8-x-clients", false);
|
||||
disableOnBoats = tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.disable-on-boats", true);
|
||||
spaceBetweenLines = tab.getConfiguration().config.getDouble("unlimited-nametag-prefix-suffix-mode.space-between-lines", 0.22);
|
||||
disabledUnlimitedWorlds = tab.getConfiguration().config.getStringList("disable-features-in-worlds.unlimited-nametags", Arrays.asList("disabledworld"));
|
||||
if (tab.getConfiguration().premiumconfig != null) {
|
||||
List<String> realList = tab.getConfiguration().premiumconfig.getStringList("unlimited-nametag-mode-dynamic-lines", Arrays.asList("abovename", "nametag", "belowname", "another"));
|
||||
dynamicLines = new ArrayList<String>();
|
||||
@@ -72,76 +89,148 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
}
|
||||
refreshUsedPlaceholders();
|
||||
eventListener = new EventListener(this);
|
||||
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
|
||||
tab.getFeatureManager().registerFeature("nametagx-packet", new PacketListener(this, nms, tab));
|
||||
tab.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() {
|
||||
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
|
||||
for (TabPlayer all : tab.getPlayers()){
|
||||
super.load();
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
entityIdMap.put(((Player) all.getPlayer()).getEntityId(), all);
|
||||
all.setTeamName(sorting.getTeamName(all));
|
||||
updateProperties(all);
|
||||
loadArmorStands(all);
|
||||
if (isDisabledWorld(all.getWorldName())) continue;
|
||||
all.registerTeam();
|
||||
if (isDisabledWorld(all.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, all.getWorldName())) continue;
|
||||
loadPassengers(all);
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
spawnArmorStands(all, viewer, false);
|
||||
}
|
||||
}
|
||||
startRefreshingTasks();
|
||||
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing nametag visibility", getFeatureType(), UsageType.REFRESHING_NAMETAG_VISIBILITY, new Runnable() {
|
||||
startVisibilityRefreshTask();
|
||||
startVehicleTickingTask();
|
||||
}
|
||||
|
||||
private void startVisibilityRefreshTask() {
|
||||
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing nametag visibility", getFeatureType(), UsageType.REFRESHING_NAMETAG_VISIBILITY_AND_COLLISION, new Runnable() {
|
||||
public void run() {
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
if (!p.isLoaded() || isDisabledWorld(p.getWorldName())) continue;
|
||||
if (!p.isLoaded() || isDisabledWorld(p.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, p.getWorldName())) continue;
|
||||
p.getArmorStandManager().updateVisibility(false);
|
||||
if (!disableOnBoats) continue;
|
||||
boolean onBoat = ((Player)p.getPlayer()).getVehicle() != null && ((Player)p.getPlayer()).getVehicle().getType() == EntityType.BOAT;
|
||||
if (p.isOnBoat() != onBoat) {
|
||||
p.setOnBoat(onBoat);
|
||||
p.updateTeamData();
|
||||
if (onBoat) {
|
||||
if (!playersOnBoats.contains(p)) {
|
||||
playersOnBoats.add(p);
|
||||
updateTeamData(p);
|
||||
}
|
||||
} else {
|
||||
if (playersOnBoats.contains(p)) {
|
||||
playersOnBoats.remove(p);
|
||||
updateTeamData(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void startVehicleTickingTask() {
|
||||
tab.getCPUManager().startRepeatingMeasuredTask(100, "ticking vehicles", TabFeature.NAMETAGX, UsageType.TICKING_VEHICLES, () -> {
|
||||
|
||||
for (TabPlayer p : TAB.getInstance().getPlayers()) {
|
||||
if (!p.isLoaded()) continue;
|
||||
if (isDisabledWorld(p.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, p.getWorldName())) {
|
||||
playersInVehicle.remove(p);
|
||||
playerLocations.remove(p);
|
||||
continue;
|
||||
}
|
||||
processVehicles(p);
|
||||
if (!playerLocations.containsKey(p) || !playerLocations.get(p).equals(((Player)p.getPlayer()).getLocation())) {
|
||||
playerLocations.put(p, ((Player)p.getPlayer()).getLocation());
|
||||
processPassengers((Entity) p.getPlayer());
|
||||
//also updating position if player is previewing since we're here as the code would be same if we want to avoid listening to move event
|
||||
if (p.isPreviewingNametag() && p.getArmorStandManager() != null) {
|
||||
p.getArmorStandManager().teleport(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for vehicle changes of player and sends packets if needed
|
||||
* @param p - player to check
|
||||
*/
|
||||
private void processVehicles(TabPlayer p) {
|
||||
Entity vehicle = ((Player)p.getPlayer()).getVehicle();
|
||||
if (playersInVehicle.containsKey(p) && vehicle == null) {
|
||||
//vehicle exit
|
||||
vehicles.remove(playersInVehicle.get(p).getEntityId());
|
||||
p.getArmorStandManager().teleport();
|
||||
playersInVehicle.remove(p);
|
||||
}
|
||||
if (!playersInVehicle.containsKey(p) && vehicle != null) {
|
||||
//vehicle enter
|
||||
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
|
||||
p.getArmorStandManager().teleport();
|
||||
playersInVehicle.put(p, vehicle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports armor stands of all passengers on specified vehicle
|
||||
* @param vehicle - entity to check passengers of
|
||||
*/
|
||||
private void processPassengers(Entity vehicle) {
|
||||
for (Entity passenger : getPassengers(vehicle)) {
|
||||
if (passenger instanceof Player) {
|
||||
TabPlayer pl = TAB.getInstance().getPlayer(passenger.getUniqueId());
|
||||
pl.getArmorStandManager().teleport();
|
||||
} else {
|
||||
processPassengers(passenger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
HandlerList.unregisterAll(eventListener);
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
if (!isDisabledWorld(p.getWorldName())) p.unregisterTeam();
|
||||
p.getArmorStandManager().destroy();
|
||||
if (p.getArmorStandManager() != null) p.getArmorStandManager().destroy();
|
||||
}
|
||||
entityIdMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
super.onJoin(connectedPlayer);
|
||||
entityIdMap.put(((Player) connectedPlayer.getPlayer()).getEntityId(), connectedPlayer);
|
||||
connectedPlayer.setTeamName(sorting.getTeamName(connectedPlayer));
|
||||
updateProperties(connectedPlayer);
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (!all.isLoaded()) continue; //avoiding NPE when 2 players join at once
|
||||
if (all == connectedPlayer) continue;
|
||||
if (!isDisabledWorld(all.getWorldName())) all.registerTeam(connectedPlayer);
|
||||
}
|
||||
loadArmorStands(connectedPlayer);
|
||||
if (isDisabledWorld(connectedPlayer.getWorldName())) return;
|
||||
connectedPlayer.registerTeam();
|
||||
if (isDisabledWorld(connectedPlayer.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, connectedPlayer.getWorldName())) return;
|
||||
loadPassengers(connectedPlayer);
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all passengers riding this player and adds them to vehicle list
|
||||
* @param p - player to load passengers of
|
||||
*/
|
||||
private void loadPassengers(TabPlayer p) {
|
||||
if (((Entity) p.getPlayer()).getVehicle() == null) return;
|
||||
Entity vehicle = ((Entity) p.getPlayer()).getVehicle();
|
||||
@@ -150,51 +239,50 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
if (!isDisabledWorld(disconnectedPlayer.getWorldName())) disconnectedPlayer.unregisterTeam();
|
||||
invisiblePlayers.remove(disconnectedPlayer.getName());
|
||||
super.onQuit(disconnectedPlayer);
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (all.getArmorStandManager() != null) all.getArmorStandManager().unregisterPlayer(disconnectedPlayer);
|
||||
}
|
||||
//entity destroy packet is sent too late, need to send it manually
|
||||
if (disconnectedPlayer.getArmorStandManager() != null) disconnectedPlayer.getArmorStandManager().destroy();
|
||||
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (all == disconnectedPlayer) continue;
|
||||
all.showNametag(disconnectedPlayer.getUniqueId()); //clearing memory from API method
|
||||
}
|
||||
entityIdMap.remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
|
||||
eventListener.onQuit(disconnectedPlayer);
|
||||
playersInVehicle.remove(disconnectedPlayer);
|
||||
playerLocations.remove(disconnectedPlayer);
|
||||
tab.getCPUManager().runTaskLater(100, "processing onQuit", getFeatureType(), UsageType.PLAYER_QUIT_EVENT, () -> disconnectedPlayer.getArmorStandManager().destroy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorldChange(TabPlayer p, String from, String to) {
|
||||
if (!p.isLoaded()) return;
|
||||
updateProperties(p);
|
||||
super.onWorldChange(p, from, to);
|
||||
Set<TabPlayer> nearby = p.getArmorStandManager().getNearbyPlayers();
|
||||
p.getArmorStandManager().destroy();
|
||||
loadArmorStands(p);
|
||||
loadPassengers(p);
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
spawnArmorStands(p, viewer, true);
|
||||
}
|
||||
if (isDisabledWorld(p.getWorldName()) && !isDisabledWorld(disabledWorlds, from)) {
|
||||
p.unregisterTeam();
|
||||
} else if (!isDisabledWorld(p.getWorldName()) && isDisabledWorld(disabledWorlds, from)) {
|
||||
p.registerTeam();
|
||||
} else {
|
||||
p.updateTeam();
|
||||
p.getArmorStandManager().refresh();
|
||||
viewer.getArmorStandManager().destroy(p);
|
||||
if (nearby.contains(viewer) && to.equals(viewer.getWorldName())) spawnArmorStands(p, viewer, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private void spawnArmorStands(TabPlayer owner, TabPlayer viewer, boolean sendMutually) {
|
||||
if (owner == viewer) return; //not displaying own armorstands
|
||||
if (((Player) viewer.getPlayer()).getWorld() != ((Player) owner.getPlayer()).getWorld()) return; //different world
|
||||
if (getDistance(viewer, owner) <= ENTITY_TRACKING_RANGE) {
|
||||
owner.getArmorStandManager().spawn(viewer);
|
||||
if (sendMutually && viewer.getArmorStandManager() != null) viewer.getArmorStandManager().spawn(owner);
|
||||
if (isDisabledWorld(owner.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, owner.getWorldName())) 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("nametag", pl.getProperty("tagprefix").getCurrentRawValue() + pl.getProperty("customtagname").getCurrentRawValue() + pl.getProperty("tagsuffix").getCurrentRawValue());
|
||||
@@ -207,11 +295,15 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
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(idCounter++, pl, p, Double.parseDouble(line.getValue()+""), true));
|
||||
pl.getArmorStandManager().addArmorStand(line.getKey(), new BukkitArmorStand(idCounter++, 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) {
|
||||
p.getArmorStandManager().refresh();
|
||||
double currentY = -spaceBetweenLines;
|
||||
@@ -226,24 +318,12 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
if (isDisabledWorld(refreshed.getWorldName())) return;
|
||||
boolean refresh;
|
||||
if (force) {
|
||||
updateProperties(refreshed);
|
||||
refresh = true;
|
||||
} else {
|
||||
boolean prefix = refreshed.getProperty("tagprefix").update();
|
||||
boolean suffix = refreshed.getProperty("tagsuffix").update();
|
||||
refresh = prefix || suffix;
|
||||
}
|
||||
if (refresh) refreshed.updateTeam();
|
||||
super.refresh(refreshed, force);
|
||||
if (isDisabledWorld(refreshed.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, refreshed.getWorldName())) return;
|
||||
if (force) {
|
||||
refreshed.getArmorStandManager().destroy();
|
||||
loadArmorStands(refreshed);
|
||||
if (((Entity) refreshed.getPlayer()).getVehicle() != null) {
|
||||
Entity vehicle = ((Entity) refreshed.getPlayer()).getVehicle();
|
||||
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
|
||||
}
|
||||
loadPassengers(refreshed);
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
if (viewer == refreshed) continue;
|
||||
if (viewer.getWorldName().equals(refreshed.getWorldName())) {
|
||||
@@ -262,10 +342,13 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
}
|
||||
}
|
||||
|
||||
private void updateProperties(TabPlayer p) {
|
||||
p.loadPropertyFromConfig("tagprefix");
|
||||
/**
|
||||
* Updates raw values of properties for specified player
|
||||
* @param p - player to update
|
||||
*/
|
||||
public void updateProperties(TabPlayer p) {
|
||||
super.updateProperties(p);
|
||||
p.loadPropertyFromConfig("customtagname", p.getName());
|
||||
p.loadPropertyFromConfig("tagsuffix");
|
||||
p.setProperty("nametag", p.getProperty("tagprefix").getCurrentRawValue() + p.getProperty("customtagname").getCurrentRawValue() + p.getProperty("tagsuffix").getCurrentRawValue());
|
||||
for (String property : dynamicLines) {
|
||||
if (!property.equals("nametag")) p.loadPropertyFromConfig(property);
|
||||
@@ -275,13 +358,18 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of all passengers on specified vehicle
|
||||
* @param vehicle - vehicle to check passengers of
|
||||
* @return list of passengers
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public List<Entity> getPassengers(Entity vehicle){
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 11) {
|
||||
return vehicle.getPassengers();
|
||||
} else {
|
||||
if (vehicle.getPassenger() != null) {
|
||||
return Lists.newArrayList(vehicle.getPassenger());
|
||||
return Arrays.asList(vehicle.getPassenger());
|
||||
} else {
|
||||
return new ArrayList<Entity>();
|
||||
}
|
||||
@@ -290,13 +378,18 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
|
||||
@Override
|
||||
public void refreshUsedPlaceholders() {
|
||||
usedPlaceholders = tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive("tagprefix", "customtagname", "tagsuffix");
|
||||
usedPlaceholders = new HashSet<>(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive("tagprefix", "customtagname", "tagsuffix"));
|
||||
for (String line : dynamicLines) {
|
||||
usedPlaceholders.addAll(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive(line));
|
||||
}
|
||||
for (String line : staticLines.keySet()) {
|
||||
usedPlaceholders.addAll(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive(line));
|
||||
}
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
usedPlaceholders.addAll(tab.getPlaceholderManager().getUsedPlaceholderIdentifiersRecursive(p.getProperty("tagprefix").getCurrentRawValue(),
|
||||
p.getProperty("customtagname").getCurrentRawValue(), p.getProperty("tagsuffix").getCurrentRawValue(),
|
||||
p.getProperty("abovename").getCurrentRawValue(), p.getProperty("belowname").getCurrentRawValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -305,14 +398,8 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRespawn(TabPlayer respawned) {
|
||||
if (isDisabledWorld(respawned.getWorldName())) return;
|
||||
respawned.getArmorStandManager().teleport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSneak(TabPlayer player, boolean isSneaking) {
|
||||
if (isDisabledWorld(player.getWorldName())) return;
|
||||
player.getArmorStandManager().sneak(isSneaking);
|
||||
public boolean getTeamVisibility(TabPlayer p, TabPlayer viewer) {
|
||||
//only visible if player is on boat & config option is enabled and player is not invisible (1.8 bug) or feature is disabled
|
||||
return (playersOnBoats.contains(p) && !invisiblePlayers.contains(p.getName())) || isDisabledWorld(disabledUnlimitedWorlds, p.getWorldName());
|
||||
}
|
||||
}
|
||||
+49
-22
@@ -10,28 +10,33 @@ import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
import me.neznamy.tab.shared.features.types.packet.PlayerInfoPacketListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.RawPacketListener;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
|
||||
/**
|
||||
* The packet listening part for securing proper functionality of armor stands
|
||||
* Bukkit events are too unreliable and delayed/ahead which causes desync
|
||||
*/
|
||||
public class PacketListener implements RawPacketListener, PlayerInfoPacketListener {
|
||||
public class PacketListener implements RawPacketListener {
|
||||
|
||||
//main feature
|
||||
private NameTagX nameTagX;
|
||||
|
||||
//nms storage
|
||||
private NMSStorage nms;
|
||||
|
||||
//tab instance
|
||||
private TAB tab;
|
||||
private boolean modifyNPCnames;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
* @param nameTagX - main feature
|
||||
* @param nms - nms storage
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
public PacketListener(NameTagX nameTagX, NMSStorage nms, TAB tab) {
|
||||
this.nameTagX = nameTagX;
|
||||
this.nms = nms;
|
||||
this.tab = tab;
|
||||
modifyNPCnames = tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.modify-npc-names", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,43 +60,65 @@ public class PacketListener implements RawPacketListener, PlayerInfoPacketListen
|
||||
|
||||
@Override
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws Throwable {
|
||||
if (nms.PacketPlayOutEntity.isInstance(packet) && !packet.getClass().getSimpleName().equals("PacketPlayOutEntityLook")) {
|
||||
if (receiver.getVersion().getMinorVersion() < 8) return;
|
||||
//using bukkit player to check world due to old data on world change due to asynchronous processing & world name changing
|
||||
String world = ((Player)receiver.getPlayer()).getWorld().getName();
|
||||
if (!receiver.isLoaded() || nameTagX.isDisabledWorld(world) || nameTagX.isDisabledWorld(nameTagX.disabledUnlimitedWorlds, world)) return;
|
||||
if (nms.PacketPlayOutEntity.isInstance(packet) && !nms.PacketPlayOutEntityLook.isInstance(packet)) { //ignoring head rotation only packets
|
||||
onEntityMove(receiver, nms.PacketPlayOutEntity_ENTITYID.getInt(packet));
|
||||
}
|
||||
if (nms.PacketPlayOutEntityTeleport.isInstance(packet)) {
|
||||
onEntityMove(receiver, nms.PacketPlayOutEntityTeleport_ENTITYID.getInt(packet));
|
||||
}
|
||||
if (nms.PacketPlayOutNamedEntitySpawn.isInstance(packet)) {
|
||||
onEntitySpawn(receiver, nms.PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(packet));
|
||||
}
|
||||
if (nms.PacketPlayOutEntityDestroy.isInstance(packet)) {
|
||||
if (nms.minorVersion >= 17) {
|
||||
onEntityDestroy(receiver, nms.PacketPlayOutEntityDestroy_ENTITIES.getInt(packet));
|
||||
} else {
|
||||
for (int entity : (int[]) nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet)) {
|
||||
onEntityDestroy(receiver, entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes entity move packet
|
||||
* @param receiver - packet receiver
|
||||
* @param entityId - entity that moved
|
||||
*/
|
||||
public void onEntityMove(TabPlayer receiver, int entityId) {
|
||||
TabPlayer pl = nameTagX.entityIdMap.get(entityId);
|
||||
List<Entity> vehicleList;
|
||||
if (pl != null) {
|
||||
//player moved
|
||||
if (!pl.isLoaded() || nameTagX.isDisabledWorld(pl.getWorldName())) return;
|
||||
tab.getCPUManager().runMeasuredTask("processing EntityMove", getFeatureType(), UsageType.PACKET_ENTITY_MOVE, () -> pl.getArmorStandManager().teleport(receiver));
|
||||
} else if ((vehicleList = nameTagX.vehicles.get(entityId)) != null){
|
||||
//a vehicle carrying something moved
|
||||
for (Entity entity : vehicleList) {
|
||||
TabPlayer passenger = nameTagX.entityIdMap.get(entity.getEntityId());
|
||||
if (passenger != null && passenger.getArmorStandManager() != null && !nameTagX.isDisabledWorld(passenger.getWorldName())) {
|
||||
if (passenger != null && passenger.getArmorStandManager() != null) {
|
||||
tab.getCPUManager().runMeasuredTask("processing EntityMove", getFeatureType(), UsageType.PACKET_ENTITY_MOVE, () -> passenger.getArmorStandManager().teleport(receiver));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
|
||||
if (!modifyNPCnames || info.action != EnumPlayerInfoAction.ADD_PLAYER) return;
|
||||
for (PlayerInfoData playerInfoData : info.entries) {
|
||||
if (tab.getPlayerByTablistUUID(playerInfoData.uniqueId) == null && playerInfoData.name.length() <= 15) {
|
||||
if (playerInfoData.name.length() <= 14) {
|
||||
playerInfoData.name += "\u00a7r";
|
||||
} else {
|
||||
playerInfoData.name += " ";
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntitySpawn(TabPlayer receiver, int entityId) {
|
||||
TabPlayer spawnedPlayer = nameTagX.entityIdMap.get(entityId);
|
||||
if (spawnedPlayer != null && spawnedPlayer.isLoaded()) {
|
||||
tab.getCPUManager().runMeasuredTask("processing NamedEntitySpawn", getFeatureType(), UsageType.PACKET_NAMED_ENTITY_SPAWN, () -> spawnedPlayer.getArmorStandManager().spawn(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntityDestroy(TabPlayer receiver, int entity) {
|
||||
TabPlayer despawnedPlayer = nameTagX.entityIdMap.get(entity);
|
||||
if (despawnedPlayer != null && despawnedPlayer.isLoaded())
|
||||
tab.getCPUManager().runMeasuredTask("processing EntityDestroy", getFeatureType(), UsageType.PACKET_ENTITY_DESTROY, () -> despawnedPlayer.getArmorStandManager().destroy(receiver));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabFeature getFeatureType() {
|
||||
return TabFeature.NAMETAGX;
|
||||
@@ -0,0 +1,664 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.platforms.bukkit.Main;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherRegistry;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class NMSStorage {
|
||||
|
||||
//instance of this class
|
||||
private static NMSStorage instance;
|
||||
|
||||
//data watcher registry
|
||||
public DataWatcherRegistry dataWatcherRegistry;
|
||||
|
||||
//server package, such as "v1_16_R3"
|
||||
private String serverPackage;
|
||||
|
||||
//server minor version such as "16"
|
||||
public int minorVersion;
|
||||
|
||||
public Class<?> Entity;
|
||||
public Class<?> EntityLiving;
|
||||
private Class<?> Packet;
|
||||
private Class<?> PlayerConnection;
|
||||
private Class<?> NetworkManager;
|
||||
public Class<?> EntityPlayer;
|
||||
public Field PING;
|
||||
public Field PLAYER_CONNECTION;
|
||||
public Field NETWORK_MANAGER;
|
||||
public Field CHANNEL;
|
||||
public Method getHandle;
|
||||
public Method getProfile;
|
||||
public Method sendPacket;
|
||||
|
||||
//chat
|
||||
public Class<Enum> EnumChatFormat;
|
||||
private Class<?> IChatBaseComponent;
|
||||
private Class<?> ChatBaseComponent;
|
||||
private Class<?> ChatSerializer;
|
||||
private Class<?> EnumClickAction;
|
||||
private Class<?> EnumHoverAction;
|
||||
private Class<?> ChatModifier;
|
||||
public Class<?> ChatComponentText;
|
||||
private Class<?> ChatClickable;
|
||||
private Class<?> ChatHoverable;
|
||||
private Class<?> ChatHexColor;
|
||||
public Constructor<?> newChatComponentText;
|
||||
public Constructor<?> newChatClickable;
|
||||
public Constructor<?> newChatHoverable;
|
||||
public Constructor<?> newChatModifier;
|
||||
public Field ChatBaseComponent_extra;
|
||||
public Field ChatBaseComponent_modifier;
|
||||
public Field ChatClickable_action;
|
||||
public Field ChatClickable_value;
|
||||
public Field ChatHexColor_name;
|
||||
public Field ChatHexColor_rgb;
|
||||
public Field ChatHoverable_action;
|
||||
public Field ChatHoverable_value;
|
||||
public Field ChatModifier_color;
|
||||
public Field ChatModifier_bold;
|
||||
public Field ChatModifier_italic;
|
||||
public Field ChatModifier_underlined;
|
||||
public Field ChatModifier_strikethrough;
|
||||
public Field ChatModifier_obfuscated;
|
||||
public Field ChatModifier_clickEvent;
|
||||
public Field ChatModifier_hoverEvent;
|
||||
public Field ChatComponentText_text;
|
||||
public Method ChatSerializer_DESERIALIZE;
|
||||
public Method EnumClickAction_a;
|
||||
public Method EnumHoverAction_a;
|
||||
public Method ChatHexColor_ofInt;
|
||||
public Method ChatHexColor_ofString;
|
||||
public Method ChatComponentText_addSibling;
|
||||
|
||||
//PacketPlayOutChat
|
||||
private Class<?> PacketPlayOutChat;
|
||||
public Class<Enum> ChatMessageType;
|
||||
public Constructor<?> newPacketPlayOutChat;
|
||||
|
||||
//PacketPlayOutPlayerListHeaderFooter
|
||||
public Class<?> PacketPlayOutPlayerListHeaderFooter;
|
||||
public Constructor<?> newPacketPlayOutPlayerListHeaderFooter;
|
||||
public Field PacketPlayOutPlayerListHeaderFooter_HEADER;
|
||||
public Field PacketPlayOutPlayerListHeaderFooter_FOOTER;
|
||||
|
||||
//PacketPlayOutScoreboardDisplayObjective
|
||||
public Class<?> PacketPlayOutScoreboardDisplayObjective;
|
||||
public Constructor<?> newPacketPlayOutScoreboardDisplayObjective;
|
||||
public Field PacketPlayOutScoreboardDisplayObjective_POSITION;
|
||||
public Field PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME;
|
||||
|
||||
//PacketPlayOutScoreboardObjective
|
||||
public Class<?> PacketPlayOutScoreboardObjective;
|
||||
public Class<?> IScoreboardCriteria;
|
||||
private Class<?> ScoreboardObjective;
|
||||
public Class<Enum> EnumScoreboardHealthDisplay;
|
||||
public Constructor<?> newPacketPlayOutScoreboardObjective;
|
||||
public Constructor<?> newScoreboardObjective;
|
||||
public Field PacketPlayOutScoreboardObjective_OBJECTIVENAME;
|
||||
public Field PacketPlayOutScoreboardObjective_DISPLAYNAME;
|
||||
public Field PacketPlayOutScoreboardObjective_RENDERTYPE;
|
||||
public Field PacketPlayOutScoreboardObjective_METHOD;
|
||||
|
||||
//PacketPlayOutScoreboardScore
|
||||
private Class<?> PacketPlayOutScoreboardScore;
|
||||
private Class<?> ScoreboardScore;
|
||||
public Class<Enum> EnumScoreboardAction;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore_String;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore_1_13;
|
||||
public Constructor<?> newScoreboardScore;
|
||||
public Method ScoreboardScore_setScore;
|
||||
|
||||
//PacketPlayOutScoreboardTeam
|
||||
public Class<?> PacketPlayOutScoreboardTeam;
|
||||
public Class<?> EnumNameTagVisibility;
|
||||
public Class<?> EnumTeamPush;
|
||||
public Constructor<?> newPacketPlayOutScoreboardTeam;
|
||||
public Field PacketPlayOutScoreboardTeam_NAME;
|
||||
public Field PacketPlayOutScoreboardTeam_PLAYERS;
|
||||
private Class<?> Scoreboard;
|
||||
private Class<?> ScoreboardTeam;
|
||||
public Constructor<?> newScoreboard;
|
||||
public Constructor<?> newScoreboardTeam;
|
||||
public Method ScoreboardTeam_setPrefix;
|
||||
public Method ScoreboardTeam_setSuffix;
|
||||
public Method ScoreboardTeam_setNameTagVisibility;
|
||||
public Method ScoreboardTeam_setCollisionRule;
|
||||
public Method ScoreboardTeam_setColor;
|
||||
public Method ScoreboardTeam_getPlayerNameSet;
|
||||
//1.17
|
||||
public Class<?> PacketPlayOutScoreboardTeam_a;
|
||||
public Method PacketPlayOutScoreboardTeam_of;
|
||||
public Method PacketPlayOutScoreboardTeam_ofBoolean;
|
||||
public Method PacketPlayOutScoreboardTeam_ofString;
|
||||
|
||||
|
||||
//PacketPlayOutPlayerInfo
|
||||
public Class<?> PacketPlayOutPlayerInfo;
|
||||
public Class<Enum> EnumGamemode;
|
||||
public Class<Enum> EnumPlayerInfoAction;
|
||||
private Class<?> PlayerInfoData;
|
||||
public Constructor<?> newPacketPlayOutPlayerInfo;
|
||||
public Constructor<?> newPlayerInfoData;
|
||||
public Field PacketPlayOutPlayerInfo_ACTION;
|
||||
public Field PacketPlayOutPlayerInfo_PLAYERS;
|
||||
public Method PlayerInfoData_getProfile;
|
||||
public Method PlayerInfoData_getLatency;
|
||||
public Method PlayerInfoData_getGamemode;
|
||||
public Method PlayerInfoData_getDisplayName;
|
||||
|
||||
public Class<?> PacketPlayOutEntityDestroy;
|
||||
public Constructor<?> newPacketPlayOutEntityDestroy;
|
||||
public Field PacketPlayOutEntityDestroy_ENTITIES;
|
||||
|
||||
public Class<?> PacketPlayInUseEntity;
|
||||
private Class<?> EnumEntityUseAction;
|
||||
public Field PacketPlayInUseEntity_ENTITY;
|
||||
public Field PacketPlayInUseEntity_ACTION;
|
||||
|
||||
public Class<?> PacketPlayOutNamedEntitySpawn;
|
||||
public Field PacketPlayOutNamedEntitySpawn_ENTITYID;
|
||||
|
||||
public Class<?> PacketPlayOutEntity;
|
||||
public Class<?> PacketPlayOutEntityLook;
|
||||
public Field PacketPlayOutEntity_ENTITYID;
|
||||
|
||||
public Class<?> PacketPlayOutEntityTeleport;
|
||||
public Constructor<?> newPacketPlayOutEntityTeleport;
|
||||
public Field PacketPlayOutEntityTeleport_ENTITYID;
|
||||
public Field PacketPlayOutEntityTeleport_X;
|
||||
public Field PacketPlayOutEntityTeleport_Y;
|
||||
public Field PacketPlayOutEntityTeleport_Z;
|
||||
public Field PacketPlayOutEntityTeleport_YAW;
|
||||
public Field PacketPlayOutEntityTeleport_PITCH;
|
||||
|
||||
public Class<?> PacketPlayOutEntityMetadata;
|
||||
public Constructor<?> newPacketPlayOutEntityMetadata;
|
||||
public Field PacketPlayOutEntityMetadata_LIST;
|
||||
|
||||
public Class<?> PacketPlayOutSpawnEntityLiving;
|
||||
public Constructor<?> newPacketPlayOutSpawnEntityLiving;
|
||||
public Field PacketPlayOutSpawnEntityLiving_ENTITYID;
|
||||
public Field PacketPlayOutSpawnEntityLiving_UUID;
|
||||
public Field PacketPlayOutSpawnEntityLiving_ENTITYTYPE;
|
||||
public Field PacketPlayOutSpawnEntityLiving_X;
|
||||
public Field PacketPlayOutSpawnEntityLiving_Y;
|
||||
public Field PacketPlayOutSpawnEntityLiving_Z;
|
||||
public Field PacketPlayOutSpawnEntityLiving_YAW;
|
||||
public Field PacketPlayOutSpawnEntityLiving_PITCH;
|
||||
public Field PacketPlayOutSpawnEntityLiving_DATAWATCHER;
|
||||
|
||||
//DataWatcher
|
||||
private Class<?> DataWatcher;
|
||||
public Constructor<?> newDataWatcher;
|
||||
public Method DataWatcher_REGISTER;
|
||||
|
||||
private Class<?> DataWatcherItem;
|
||||
public Field DataWatcherItem_TYPE;
|
||||
public Field DataWatcherItem_VALUE;
|
||||
|
||||
private Class<?> DataWatcherObject;
|
||||
public Constructor<?> newDataWatcherObject;
|
||||
public Field DataWatcherObject_SLOT;
|
||||
public Field DataWatcherObject_SERIALIZER;
|
||||
|
||||
private Class<?> DataWatcherRegistry;
|
||||
private Class<?> DataWatcherSerializer;
|
||||
|
||||
/**
|
||||
* Creates new instance, initializes required NMS classes and fields
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
public NMSStorage() throws Exception {
|
||||
serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
|
||||
initializeClasses();
|
||||
initializeConstructors();
|
||||
initializeFields();
|
||||
initializeMethods();
|
||||
dataWatcherRegistry = new DataWatcherRegistry(this, DataWatcherRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new instance
|
||||
* @param instance - new instance
|
||||
*/
|
||||
public static void setInstance(NMSStorage instance) {
|
||||
NMSStorage.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance
|
||||
* @return instance
|
||||
*/
|
||||
public static NMSStorage getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes required NMS classes
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
private void initializeClasses() throws Exception {
|
||||
DataWatcher = getNMSClass("net.minecraft.network.syncher.DataWatcher", "DataWatcher");
|
||||
DataWatcherItem = getNMSClass("net.minecraft.network.syncher.DataWatcher$Item", "DataWatcher$Item", "DataWatcher$WatchableObject", "WatchableObject");
|
||||
EntityPlayer = getNMSClass("net.minecraft.server.level.EntityPlayer", "EntityPlayer");
|
||||
EnumChatFormat = (Class<Enum>) getNMSClass("net.minecraft.EnumChatFormat", "EnumChatFormat");
|
||||
Entity = getNMSClass("net.minecraft.world.entity.Entity", "Entity");
|
||||
EntityLiving = getNMSClass("net.minecraft.world.entity.EntityLiving", "EntityLiving");
|
||||
Packet = getNMSClass("net.minecraft.network.protocol.Packet", "Packet");
|
||||
PacketPlayInUseEntity = getNMSClass("net.minecraft.network.protocol.game.PacketPlayInUseEntity", "PacketPlayInUseEntity", "Packet7UseEntity");
|
||||
PacketPlayOutChat = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutChat", "PacketPlayOutChat", "Packet3Chat");
|
||||
PacketPlayOutEntity = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntity", "PacketPlayOutEntity", "Packet30Entity");
|
||||
PacketPlayOutEntityDestroy = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy", "PacketPlayOutEntityDestroy", "Packet29DestroyEntity");
|
||||
PacketPlayOutEntityLook = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntityLook", "Packet32EntityLook");
|
||||
PacketPlayOutEntityMetadata = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata", "PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
|
||||
PacketPlayOutEntityTeleport = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport", "PacketPlayOutEntityTeleport", "Packet34EntityTeleport");
|
||||
PacketPlayOutNamedEntitySpawn = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn", "PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
|
||||
PacketPlayOutScoreboardDisplayObjective = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective", "PacketPlayOutScoreboardDisplayObjective", "Packet208SetScoreboardDisplayObjective");
|
||||
PacketPlayOutScoreboardObjective = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective", "PacketPlayOutScoreboardObjective", "Packet206SetScoreboardObjective");
|
||||
PacketPlayOutScoreboardScore = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore", "PacketPlayOutScoreboardScore", "Packet207SetScoreboardScore");
|
||||
PacketPlayOutScoreboardTeam = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam", "PacketPlayOutScoreboardTeam", "Packet209SetScoreboardTeam");
|
||||
PacketPlayOutSpawnEntityLiving = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving", "PacketPlayOutSpawnEntityLiving", "Packet24MobSpawn");
|
||||
PlayerConnection = getNMSClass("net.minecraft.server.network.PlayerConnection", "PlayerConnection");
|
||||
NetworkManager = getNMSClass("net.minecraft.network.NetworkManager", "NetworkManager");
|
||||
Scoreboard = getNMSClass("net.minecraft.world.scores.Scoreboard", "Scoreboard");
|
||||
ScoreboardObjective = getNMSClass("net.minecraft.world.scores.ScoreboardObjective", "ScoreboardObjective");
|
||||
ScoreboardTeam = getNMSClass("net.minecraft.world.scores.ScoreboardTeam", "ScoreboardTeam");
|
||||
ScoreboardScore = getNMSClass("net.minecraft.world.scores.ScoreboardScore", "ScoreboardScore");
|
||||
IScoreboardCriteria = getNMSClass("net.minecraft.world.scores.criteria.IScoreboardCriteria", "IScoreboardCriteria");
|
||||
|
||||
if (minorVersion >= 7) {
|
||||
ChatBaseComponent = getNMSClass("net.minecraft.network.chat.ChatBaseComponent", "ChatBaseComponent");
|
||||
ChatClickable = getNMSClass("net.minecraft.network.chat.ChatClickable", "ChatClickable");
|
||||
ChatComponentText = getNMSClass("net.minecraft.network.chat.ChatComponentText", "ChatComponentText");
|
||||
ChatHoverable = getNMSClass("net.minecraft.network.chat.ChatHoverable", "ChatHoverable");
|
||||
ChatModifier = getNMSClass("net.minecraft.network.chat.ChatModifier", "ChatModifier");
|
||||
ChatSerializer = getNMSClass("net.minecraft.network.chat.IChatBaseComponent$ChatSerializer", "IChatBaseComponent$ChatSerializer", "ChatSerializer");
|
||||
EnumClickAction = getNMSClass("net.minecraft.network.chat.ChatClickable$EnumClickAction", "ChatClickable$EnumClickAction", "EnumClickAction");
|
||||
EnumHoverAction = getNMSClass("net.minecraft.network.chat.ChatHoverable$EnumHoverAction", "ChatHoverable$EnumHoverAction", "EnumHoverAction");
|
||||
IChatBaseComponent = getNMSClass("net.minecraft.network.chat.IChatBaseComponent", "IChatBaseComponent");
|
||||
EnumEntityUseAction = getNMSClass("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction", "PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction");
|
||||
}
|
||||
|
||||
if (minorVersion >= 8) {
|
||||
PacketPlayOutPlayerInfo = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo", "PacketPlayOutPlayerInfo");
|
||||
PacketPlayOutPlayerListHeaderFooter = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter", "PacketPlayOutPlayerListHeaderFooter");
|
||||
EnumPlayerInfoAction = (Class<Enum>) getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
|
||||
PlayerInfoData = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData", "PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
|
||||
EnumScoreboardHealthDisplay = (Class<Enum>) getNMSClass("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay", "IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
|
||||
EnumGamemode = (Class<Enum>) getNMSClass("net.minecraft.world.level.EnumGamemode", "EnumGamemode", "WorldSettings$EnumGamemode");
|
||||
EnumScoreboardAction = (Class<Enum>) getNMSClass("net.minecraft.server.ScoreboardServer$Action", "ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
|
||||
EnumNameTagVisibility = getNMSClass("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility", "ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
DataWatcherObject = getNMSClass("net.minecraft.network.syncher.DataWatcherObject", "DataWatcherObject");
|
||||
DataWatcherRegistry = getNMSClass("net.minecraft.network.syncher.DataWatcherRegistry", "DataWatcherRegistry");
|
||||
DataWatcherSerializer = getNMSClass("net.minecraft.network.syncher.DataWatcherSerializer", "DataWatcherSerializer");
|
||||
EnumTeamPush = getNMSClass("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush", "ScoreboardTeamBase$EnumTeamPush");
|
||||
|
||||
}
|
||||
if (minorVersion >= 12) {
|
||||
ChatMessageType = (Class<Enum>) getNMSClass("net.minecraft.network.chat.ChatMessageType", "ChatMessageType");
|
||||
}
|
||||
if (minorVersion >= 16) {
|
||||
ChatHexColor = getNMSClass("net.minecraft.network.chat.ChatHexColor", "ChatHexColor");
|
||||
}
|
||||
if (minorVersion >= 17) {
|
||||
PacketPlayOutScoreboardTeam_a = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$a");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes required NMS constructors
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
private void initializeConstructors() throws Exception {
|
||||
newDataWatcher = DataWatcher.getConstructors()[0];
|
||||
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class);
|
||||
newScoreboardObjective = ScoreboardObjective.getConstructors()[0];
|
||||
newScoreboard = Scoreboard.getConstructor();
|
||||
newScoreboardTeam = ScoreboardTeam.getConstructor(Scoreboard, String.class);
|
||||
newScoreboardScore = ScoreboardScore.getConstructor(Scoreboard, ScoreboardObjective, String.class);
|
||||
newPacketPlayOutEntityDestroy = getConstructor(PacketPlayOutEntityDestroy, 1);
|
||||
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor(Entity);
|
||||
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
|
||||
newPacketPlayOutScoreboardDisplayObjective = PacketPlayOutScoreboardDisplayObjective.getConstructor(int.class, ScoreboardObjective);
|
||||
if (minorVersion >= 7) {
|
||||
newChatComponentText = ChatComponentText.getConstructor(String.class);
|
||||
newChatClickable = ChatClickable.getConstructor(EnumClickAction, String.class);
|
||||
newChatHoverable = ChatHoverable.getConstructors()[0];
|
||||
}
|
||||
if (minorVersion >= 8) {
|
||||
newPacketPlayOutPlayerListHeaderFooter = PacketPlayOutPlayerListHeaderFooter.getConstructors()[0];
|
||||
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
|
||||
newPlayerInfoData = PlayerInfoData.getConstructors()[0];
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
newDataWatcherObject = DataWatcherObject.getConstructors()[0];
|
||||
}
|
||||
if (minorVersion >= 13) {
|
||||
newPacketPlayOutScoreboardObjective = PacketPlayOutScoreboardObjective.getConstructor(ScoreboardObjective, int.class);
|
||||
newPacketPlayOutScoreboardScore_1_13 = PacketPlayOutScoreboardScore.getConstructor(EnumScoreboardAction, String.class, String.class, int.class);
|
||||
} else {
|
||||
newPacketPlayOutScoreboardObjective = PacketPlayOutScoreboardObjective.getConstructor();
|
||||
newPacketPlayOutScoreboardScore_String = PacketPlayOutScoreboardScore.getConstructor(String.class);
|
||||
if (minorVersion >= 8) {
|
||||
newPacketPlayOutScoreboardScore = PacketPlayOutScoreboardScore.getConstructor(ScoreboardScore);
|
||||
} else {
|
||||
newPacketPlayOutScoreboardScore = PacketPlayOutScoreboardScore.getConstructor(ScoreboardScore, int.class);
|
||||
}
|
||||
}
|
||||
if (minorVersion >= 16) {
|
||||
newChatModifier = getConstructor(ChatModifier, 10);
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType, UUID.class);
|
||||
} else if (minorVersion >= 7) {
|
||||
newChatModifier = ChatModifier.getConstructor();
|
||||
if (minorVersion >= 12) {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType);
|
||||
} else if (minorVersion >= 8) {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, byte.class);
|
||||
} else {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent);
|
||||
}
|
||||
}
|
||||
if (minorVersion < 17) {
|
||||
newPacketPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.getConstructor(ScoreboardTeam, int.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes required NMS fields
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
private void initializeFields() throws Exception {
|
||||
PLAYER_CONNECTION = getFields(EntityPlayer, PlayerConnection).get(0);
|
||||
|
||||
PacketPlayOutScoreboardDisplayObjective_POSITION = getFields(PacketPlayOutScoreboardDisplayObjective, int.class).get(0);
|
||||
PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME = getFields(PacketPlayOutScoreboardDisplayObjective, String.class).get(0);
|
||||
|
||||
PacketPlayOutScoreboardObjective_OBJECTIVENAME = getFields(PacketPlayOutScoreboardObjective, String.class).get(0);
|
||||
List<Field> fields = getFields(PacketPlayOutScoreboardObjective, int.class);
|
||||
PacketPlayOutScoreboardObjective_METHOD = fields.get(fields.size()-1);
|
||||
|
||||
PacketPlayOutScoreboardTeam_NAME = getFields(PacketPlayOutScoreboardTeam, String.class).get(0);
|
||||
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
|
||||
|
||||
PacketPlayOutEntityTeleport_ENTITYID = getFields(PacketPlayOutEntityTeleport, int.class).get(0);
|
||||
PacketPlayOutEntityTeleport_YAW = getFields(PacketPlayOutEntityTeleport, byte.class).get(0);
|
||||
PacketPlayOutEntityTeleport_PITCH = getFields(PacketPlayOutEntityTeleport, byte.class).get(1);
|
||||
|
||||
PacketPlayOutEntity_ENTITYID = getFields(PacketPlayOutEntity, int.class).get(0);
|
||||
PacketPlayOutEntityMetadata_LIST = getFields(PacketPlayOutEntityMetadata, List.class).get(0);
|
||||
|
||||
PacketPlayOutSpawnEntityLiving_ENTITYID = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(0);
|
||||
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(1);
|
||||
PacketPlayOutSpawnEntityLiving_YAW = getFields(PacketPlayOutSpawnEntityLiving, byte.class).get(0);
|
||||
PacketPlayOutSpawnEntityLiving_PITCH = getFields(PacketPlayOutSpawnEntityLiving, byte.class).get(0);
|
||||
|
||||
(PacketPlayOutEntityDestroy_ENTITIES = PacketPlayOutEntityDestroy.getDeclaredFields()[0]).setAccessible(true);
|
||||
PacketPlayOutNamedEntitySpawn_ENTITYID = getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
|
||||
|
||||
if (minorVersion >= 7) {
|
||||
NETWORK_MANAGER = getFields(PlayerConnection, NetworkManager).get(0);
|
||||
ChatBaseComponent_extra = getFields(ChatBaseComponent, List.class).get(0);
|
||||
ChatBaseComponent_modifier = getFields(ChatBaseComponent, ChatModifier).get(0);
|
||||
ChatComponentText_text = getFields(ChatComponentText, String.class).get(0);
|
||||
ChatClickable_action = getFields(ChatClickable, EnumClickAction).get(0);
|
||||
ChatClickable_value = getFields(ChatClickable, String.class).get(0);
|
||||
ChatHoverable_action = getFields(ChatHoverable, EnumHoverAction).get(0);
|
||||
List<Field> booleans = getFields(ChatModifier, Boolean.class);
|
||||
ChatModifier_bold = booleans.get(0);
|
||||
ChatModifier_italic = booleans.get(1);
|
||||
ChatModifier_underlined = booleans.get(2);
|
||||
ChatModifier_strikethrough = booleans.get(3);
|
||||
ChatModifier_obfuscated = booleans.get(4);
|
||||
ChatModifier_clickEvent = getFields(ChatModifier, ChatClickable).get(0);
|
||||
ChatModifier_hoverEvent = getFields(ChatModifier, ChatHoverable).get(0);
|
||||
PacketPlayInUseEntity_ENTITY = getFields(PacketPlayInUseEntity, int.class).get(0);
|
||||
PacketPlayInUseEntity_ACTION = getFields(PacketPlayInUseEntity, EnumEntityUseAction).get(0);
|
||||
}
|
||||
|
||||
if (minorVersion >= 8) {
|
||||
CHANNEL = getFields(NetworkManager, Channel.class).get(0);
|
||||
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
|
||||
PacketPlayOutPlayerInfo_PLAYERS = getFields(PacketPlayOutPlayerInfo, List.class).get(0);
|
||||
PacketPlayOutPlayerListHeaderFooter_HEADER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(0);
|
||||
PacketPlayOutPlayerListHeaderFooter_FOOTER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(1);
|
||||
PacketPlayOutScoreboardObjective_RENDERTYPE = getFields(PacketPlayOutScoreboardObjective, EnumScoreboardHealthDisplay).get(0);
|
||||
}
|
||||
|
||||
if (minorVersion >= 9) {
|
||||
DataWatcherItem_TYPE = getFields(DataWatcherItem, DataWatcherObject).get(0);
|
||||
DataWatcherItem_VALUE = getFields(DataWatcherItem, Object.class).get(0);
|
||||
DataWatcherObject_SLOT = getFields(DataWatcherObject, int.class).get(0);
|
||||
DataWatcherObject_SERIALIZER = getFields(DataWatcherObject, DataWatcherSerializer).get(0);
|
||||
PacketPlayOutEntityTeleport_X = getFields(PacketPlayOutEntityTeleport, double.class).get(0);
|
||||
PacketPlayOutEntityTeleport_Y = getFields(PacketPlayOutEntityTeleport, double.class).get(1);
|
||||
PacketPlayOutEntityTeleport_Z = getFields(PacketPlayOutEntityTeleport, double.class).get(2);
|
||||
PacketPlayOutSpawnEntityLiving_UUID = getFields(PacketPlayOutSpawnEntityLiving, UUID.class).get(0);
|
||||
PacketPlayOutSpawnEntityLiving_X = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(0);
|
||||
PacketPlayOutSpawnEntityLiving_Y = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(1);
|
||||
PacketPlayOutSpawnEntityLiving_Z = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(2);
|
||||
} else {
|
||||
//1.8-
|
||||
DataWatcherItem_TYPE = getFields(DataWatcherItem, int.class).get(1);
|
||||
DataWatcherItem_VALUE = getFields(DataWatcherItem, Object.class).get(0);
|
||||
PacketPlayOutEntityTeleport_X = getFields(PacketPlayOutEntityTeleport, int.class).get(1);
|
||||
PacketPlayOutEntityTeleport_Y = getFields(PacketPlayOutEntityTeleport, int.class).get(2);
|
||||
PacketPlayOutEntityTeleport_Z = getFields(PacketPlayOutEntityTeleport, int.class).get(3);
|
||||
PacketPlayOutSpawnEntityLiving_X = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(2);
|
||||
PacketPlayOutSpawnEntityLiving_Y = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(3);
|
||||
PacketPlayOutSpawnEntityLiving_Z = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(4);
|
||||
}
|
||||
|
||||
if (minorVersion >= 13) {
|
||||
PacketPlayOutScoreboardObjective_DISPLAYNAME = getFields(PacketPlayOutScoreboardObjective, IChatBaseComponent).get(0);
|
||||
} else {
|
||||
PacketPlayOutScoreboardObjective_DISPLAYNAME = getFields(PacketPlayOutScoreboardObjective, String.class).get(1);
|
||||
}
|
||||
|
||||
if (minorVersion <= 14) {
|
||||
PacketPlayOutSpawnEntityLiving_DATAWATCHER = getFields(PacketPlayOutSpawnEntityLiving, DataWatcher).get(0);
|
||||
}
|
||||
|
||||
if (minorVersion >= 16) {
|
||||
//1.16+
|
||||
fields = getFields(ChatHexColor, String.class);
|
||||
ChatHexColor_name = fields.get(fields.size()-1);
|
||||
ChatHexColor_rgb = getFields(ChatHexColor, int.class).get(0);
|
||||
ChatHoverable_value = getFields(ChatHoverable, Object.class).get(0);
|
||||
ChatModifier_color = getFields(ChatModifier, ChatHexColor).get(0);
|
||||
} else if (minorVersion >= 7) {
|
||||
ChatHoverable_value = getFields(ChatHoverable, IChatBaseComponent).get(0);
|
||||
ChatModifier_color = getFields(ChatModifier, EnumChatFormat).get(0);
|
||||
}
|
||||
if (minorVersion >= 17) {
|
||||
try {
|
||||
PING = getField(EntityPlayer, "e");
|
||||
} catch (NoSuchFieldException e) {
|
||||
//deobfuscated spigot jat
|
||||
PING = getField(EntityPlayer, "latency");
|
||||
}
|
||||
} else {
|
||||
PING = getField(EntityPlayer, "ping");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes required NMS methods
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
private void initializeMethods() throws Exception {
|
||||
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
|
||||
sendPacket = getMethod(PlayerConnection, new String[]{"sendPacket", "func_147359_a"}, Packet);
|
||||
ScoreboardTeam_getPlayerNameSet = getMethod(ScoreboardTeam, new String[]{"getPlayerNameSet", "func_96670_d"});
|
||||
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[]{"setScore", "func_96647_c"}, int.class);
|
||||
if (minorVersion >= 7) {
|
||||
ChatComponentText_addSibling = getMethod(ChatComponentText, new String[]{"addSibling", "a", "func_150257_a"}, IChatBaseComponent); //v1.7.R4+, v1.7.R3-
|
||||
ChatSerializer_DESERIALIZE = getMethod(ChatSerializer, new String[]{"a", "func_150699_a"}, String.class);
|
||||
EnumClickAction_a = getMethod(EnumClickAction, new String[]{"a", "func_150672_a"}, String.class);
|
||||
EnumHoverAction_a = getMethod(EnumHoverAction, new String[]{"a", "func_150684_a"}, String.class);
|
||||
}
|
||||
if (minorVersion >= 8) {
|
||||
PlayerInfoData_getProfile = PlayerInfoData.getMethod("a");
|
||||
PlayerInfoData_getLatency = PlayerInfoData.getMethod("b");
|
||||
PlayerInfoData_getGamemode = PlayerInfoData.getMethod("c");
|
||||
PlayerInfoData_getDisplayName = PlayerInfoData.getMethod("d");
|
||||
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[]{"setNameTagVisibility", "a"}, EnumNameTagVisibility);
|
||||
getProfile = EntityPlayer.getMethod("getProfile");
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
ScoreboardTeam_setCollisionRule = ScoreboardTeam.getMethod("setCollisionRule", EnumTeamPush);
|
||||
DataWatcher_REGISTER = DataWatcher.getMethod("register", DataWatcherObject, Object.class);
|
||||
} else {
|
||||
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"a", "func_75682_a"}, int.class, Object.class);
|
||||
}
|
||||
if (minorVersion >= 13) {
|
||||
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("setPrefix", IChatBaseComponent);
|
||||
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("setSuffix", IChatBaseComponent);
|
||||
ScoreboardTeam_setColor = ScoreboardTeam.getMethod("setColor", EnumChatFormat);
|
||||
} else {
|
||||
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[]{"setPrefix", "func_96666_b"}, String.class);
|
||||
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[]{"setSuffix", "func_96662_c"}, String.class);
|
||||
}
|
||||
if (minorVersion >= 16) {
|
||||
ChatHexColor_ofInt = ChatHexColor.getMethod("a", int.class);
|
||||
ChatHexColor_ofString = ChatHexColor.getMethod("a", String.class);
|
||||
}
|
||||
if (minorVersion >= 17) {
|
||||
PacketPlayOutScoreboardTeam_of = PacketPlayOutScoreboardTeam.getMethod("a", ScoreboardTeam);
|
||||
PacketPlayOutScoreboardTeam_ofBoolean = PacketPlayOutScoreboardTeam.getMethod("a", ScoreboardTeam, boolean.class);
|
||||
PacketPlayOutScoreboardTeam_ofString = PacketPlayOutScoreboardTeam.getMethod("a", ScoreboardTeam, String.class, PacketPlayOutScoreboardTeam_a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method that prints all methods of class into console, including their return type, name and parameters
|
||||
* Useful for modded servers which code I can not access
|
||||
* @param clazz - class to show methods of
|
||||
*/
|
||||
public void showMethods(Class<?> clazz) {
|
||||
System.out.println("--- " + clazz.getSimpleName() + " ---");
|
||||
for (Method m : clazz.getMethods()) {
|
||||
System.out.println(m.getReturnType().getName() + " " + m.getName() + "(" + Arrays.toString(m.getParameterTypes()) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class with given potential names in same order
|
||||
* @param names - possible class names
|
||||
* @return class for specified name(s)
|
||||
* @throws ClassNotFoundException if class does not exist
|
||||
*/
|
||||
public Class<?> getNMSClass(String... names) throws ClassNotFoundException {
|
||||
for (String name : names) {
|
||||
try {
|
||||
return getNMSClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
}
|
||||
throw new ClassNotFoundException("No class found with possible names " + Arrays.toString(names));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class from given name
|
||||
* @param name - class name
|
||||
* @return class from given name
|
||||
* @throws ClassNotFoundException if class was not found
|
||||
*/
|
||||
private Class<?> getNMSClass(String name) throws ClassNotFoundException {
|
||||
if (minorVersion >= 17) {
|
||||
return Class.forName(name);
|
||||
} else {
|
||||
try {
|
||||
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
//modded server?
|
||||
return Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
|
||||
} catch (NullPointerException e) {
|
||||
//nested class in modded server
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns method with specified possible names and parameters. Throws exception if no such method was found
|
||||
* @param clazz - class to get method from
|
||||
* @param names - possible method names
|
||||
* @param parameterTypes - parameter types of the method
|
||||
* @return method with specified name and parameters
|
||||
* @throws NoSuchMethodException if no such method exists
|
||||
*/
|
||||
private Method getMethod(Class<?> clazz, String[] names, Class<?>... parameterTypes) throws NoSuchMethodException {
|
||||
for (String name : names) {
|
||||
try {
|
||||
return clazz.getMethod(name, parameterTypes);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(names) + " in class " + clazz.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all fields of class with defined class type
|
||||
* @param clazz - class to check fields of
|
||||
* @param type - field type to check for
|
||||
* @return list of all fields with specified class type
|
||||
*/
|
||||
public List<Field> getFields(Class<?> clazz, Class<?> type){
|
||||
if (clazz == null) throw new IllegalArgumentException("Source class cannot be null");
|
||||
List<Field> list = new ArrayList<Field>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
if (field.getType() == type) list.add(field);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns field with specified name and makes it accessible
|
||||
* @param clazz - class to get field from
|
||||
* @param name - field name
|
||||
* @return accessible field with defined name
|
||||
* @throws NoSuchFieldException if field was not found
|
||||
*/
|
||||
private Field getField(Class<?> clazz, String name) throws NoSuchFieldException {
|
||||
for (Field f : clazz.getDeclaredFields()) {
|
||||
if (f.getName().equals(name) || (f.getName().split("_").length == 3 && f.getName().split("_")[2].equals(name))) {
|
||||
f.setAccessible(true);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
if (name.equals("ping")){
|
||||
return getField(clazz, "field_71138_i"); //thermos
|
||||
}
|
||||
throw new NoSuchFieldException("Field \"" + name + "\" was not found in class " + clazz.getName());
|
||||
}
|
||||
|
||||
private Constructor<?> getConstructor(Class<?> clazz, int parameterCount) throws NoSuchMethodException {
|
||||
for (Constructor<?> c : clazz.getDeclaredConstructors()) {
|
||||
if (c.getParameterCount() == parameterCount) {
|
||||
c.setAccessible(true);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
throw new NoSuchMethodException("No constructor found in class " + clazz.getName() + " with " + parameterCount + " parameters");
|
||||
}
|
||||
}
|
||||
+11
-5
@@ -5,7 +5,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.DataWatcher class to make work with it much easier
|
||||
@@ -59,13 +58,20 @@ public class DataWatcher {
|
||||
*/
|
||||
public Object toNMS() throws Exception {
|
||||
NMSStorage nms = NMSStorage.getInstance();
|
||||
Object nmsWatcher = nms.newDataWatcher.newInstance(new Object[] {null});
|
||||
Object nmsWatcher;
|
||||
if (nms.newDataWatcher.getParameterCount() == 1) {
|
||||
nmsWatcher = nms.newDataWatcher.newInstance(new Object[] {null});
|
||||
} else {
|
||||
nmsWatcher = nms.newDataWatcher.newInstance();
|
||||
}
|
||||
for (DataWatcherItem item : dataValues.values()) {
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
|
||||
nms.DataWatcher_REGISTER.invoke(nmsWatcher, nms.newDataWatcherObject.newInstance(item.type.position, item.type.classType), item.value);
|
||||
Object position;
|
||||
if (nms.minorVersion >= 9) {
|
||||
position = nms.newDataWatcherObject.newInstance(item.type.position, item.type.classType);
|
||||
} else {
|
||||
nms.DataWatcher_REGISTER.invoke(nmsWatcher, item.type.position, item.value);
|
||||
position = item.type.position;
|
||||
}
|
||||
nms.DataWatcher_REGISTER.invoke(nmsWatcher, position, item.value);
|
||||
}
|
||||
return nmsWatcher;
|
||||
}
|
||||
+21
-8
@@ -19,6 +19,7 @@ public class DataWatcherHelper {
|
||||
//original datawatcher to write to
|
||||
private DataWatcher data;
|
||||
|
||||
//data watcher registry
|
||||
private DataWatcherRegistry registry;
|
||||
|
||||
/**
|
||||
@@ -35,13 +36,16 @@ public class DataWatcherHelper {
|
||||
* @return armor stand flags position based on server version
|
||||
*/
|
||||
private int getArmorStandFlagsPosition() {
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 15) {
|
||||
if (NMSStorage.getInstance().minorVersion >= 17) {
|
||||
//1.17.x
|
||||
return 15;
|
||||
} else if (NMSStorage.getInstance().minorVersion >= 15) {
|
||||
//1.15.x, 1.16.x
|
||||
return 14;
|
||||
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 14) {
|
||||
} else if (NMSStorage.getInstance().minorVersion >= 14) {
|
||||
//1.14.x
|
||||
return 13;
|
||||
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 10) {
|
||||
} else if (NMSStorage.getInstance().minorVersion >= 10) {
|
||||
//1.10.x - 1.13.x
|
||||
return 11;
|
||||
} else {
|
||||
@@ -64,17 +68,22 @@ public class DataWatcherHelper {
|
||||
* @param clientVersion - client version
|
||||
*/
|
||||
public void setCustomName(String customName, ProtocolVersion clientVersion) {
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 13) {
|
||||
if (NMSStorage.getInstance().minorVersion >= 13) {
|
||||
try {
|
||||
data.setValue(new DataWatcherObject(2, registry.Optional_IChatBaseComponent), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).stringToComponent(IChatBaseComponent.optimizedComponent(customName).toString(clientVersion))));
|
||||
data.setValue(new DataWatcherObject(2, registry.Optional_IChatBaseComponent), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to create component", e);
|
||||
}
|
||||
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 8){
|
||||
} else if (NMSStorage.getInstance().minorVersion >= 8){
|
||||
data.setValue(new DataWatcherObject(2, registry.String), customName);
|
||||
} else {
|
||||
String cutName = (customName.length() > 64 ? customName.substring(0, 64) : customName);
|
||||
data.setValue(new DataWatcherObject(10, registry.String), cutName);
|
||||
if (NMSStorage.getInstance().minorVersion >= 6){
|
||||
data.setValue(new DataWatcherObject(10, registry.String), cutName);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(5, registry.String), cutName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -84,7 +93,7 @@ public class DataWatcherHelper {
|
||||
* @param visible - if visible or not
|
||||
*/
|
||||
public void setCustomNameVisible(boolean visible) {
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
|
||||
if (NMSStorage.getInstance().minorVersion >= 9) {
|
||||
data.setValue(new DataWatcherObject(3, registry.Boolean), visible);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(3, registry.Byte), (byte)(visible?1:0));
|
||||
@@ -96,7 +105,11 @@ public class DataWatcherHelper {
|
||||
* @param health - health of entity
|
||||
*/
|
||||
public void setHealth(float health) {
|
||||
data.setValue(new DataWatcherObject(6, registry.Float), health);
|
||||
if (NMSStorage.getInstance().minorVersion >= 6) {
|
||||
data.setValue(new DataWatcherObject(6, registry.Float), health);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(16, registry.Integer), (int)health);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
|
||||
public class DataWatcherItem {
|
||||
|
||||
//type of value (position + data type (1.9+))
|
||||
public DataWatcherObject type;
|
||||
|
||||
//actual data value
|
||||
public Object value;
|
||||
|
||||
/**
|
||||
* Constructs new instance of the object with given parameters
|
||||
* @param type - value type
|
||||
* @param value - value
|
||||
*/
|
||||
public DataWatcherItem(DataWatcherObject type, Object value){
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns and instance of this class from given NMS item
|
||||
* @param nmsItem - NMS item
|
||||
* @return instance of this class with same data
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
public static DataWatcherItem fromNMS(Object nmsItem) throws Exception {
|
||||
NMSStorage nms = NMSStorage.getInstance();
|
||||
if (NMSStorage.getInstance().minorVersion >= 9) {
|
||||
Object nmsObject = nms.DataWatcherItem_TYPE.get(nmsItem);
|
||||
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherObject_SLOT.getInt(nmsObject), nms.DataWatcherObject_SERIALIZER.get(nmsObject)), nms.DataWatcherItem_VALUE.get(nmsItem));
|
||||
} else {
|
||||
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherItem_TYPE.getInt(nmsItem), null), nms.DataWatcherItem_VALUE.get(nmsItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
+4
-10
@@ -5,7 +5,7 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.DataWatcherRegistry class to make work with it much easier
|
||||
@@ -22,26 +22,20 @@ public class DataWatcherRegistry {
|
||||
|
||||
/**
|
||||
* Initializes required NMS classes and fields
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
public DataWatcherRegistry(Class<?> registry) throws Exception {
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
|
||||
public DataWatcherRegistry(NMSStorage nms, Class<?> registry) {
|
||||
if (nms.minorVersion >= 9) {
|
||||
Map<String, Object> fields = getStaticFields(registry);
|
||||
Byte = fields.get("a");
|
||||
Integer = fields.get("b");
|
||||
Float = fields.get("c");
|
||||
String = fields.get("d");
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 13) {
|
||||
if (nms.minorVersion >= 13) {
|
||||
Optional_IChatBaseComponent = fields.get("f");
|
||||
Boolean = fields.get("i");
|
||||
} else {
|
||||
Boolean = fields.get("h");
|
||||
}
|
||||
} else {
|
||||
Byte = 0;
|
||||
Integer = 2;
|
||||
Float = 3;
|
||||
String = 4;
|
||||
}
|
||||
}
|
||||
|
||||
File renamed without changes.
+247
-236
@@ -1,236 +1,247 @@
|
||||
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
|
||||
|
||||
#changing nametags, affecting sorting, collision rule
|
||||
change-nametag-prefix-suffix: true
|
||||
|
||||
#prefix/suffix in tablist
|
||||
change-tablist-prefix-suffix: true
|
||||
|
||||
#header/footer
|
||||
enable-header-footer: true
|
||||
|
||||
header:
|
||||
- "&3&lServer name"
|
||||
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
|
||||
- "&7Online players: &f%online%"
|
||||
- "&6Online staff: &e%staffonline%"
|
||||
- "%animation:ping-tps%"
|
||||
- ""
|
||||
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
|
||||
- "%animation:MyAnimation1%"
|
||||
footer:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "%animation:time%"
|
||||
- ""
|
||||
- "&7Visit our webpage %animation:web%"
|
||||
|
||||
#supports any numeric placeholder, set to "" to disable the feature
|
||||
yellow-number-in-tablist: "%ping%"
|
||||
|
||||
#literally what it says
|
||||
group-sorting-priority-list:
|
||||
- owner
|
||||
- admin
|
||||
- mod
|
||||
- helper
|
||||
- builder
|
||||
- premium
|
||||
- player
|
||||
- default
|
||||
|
||||
Groups:
|
||||
Owner:
|
||||
tabprefix: "&0&l[&a&lOwner&0&l] &a"
|
||||
tagprefix: "&2&lOwner &a"
|
||||
Admin:
|
||||
tabprefix: "&0&l[&4&lAdmin&0&l] &1"
|
||||
tagprefix: "%animation:admin_prefix%"
|
||||
Player:
|
||||
tabprefix: "&0&l[&7&lPlayer&0&l] &3"
|
||||
tagprefix: "&2&lPlayer &3"
|
||||
example_group:
|
||||
header:
|
||||
- "This is an example of per-group header/footer"
|
||||
footer:
|
||||
- "applied to a group"
|
||||
|
||||
#default settings for all groups, all groups will take properties from this section unless player's primary group overrides a specific setting
|
||||
_OTHER_:
|
||||
tabprefix: "%vault-prefix%"
|
||||
tagprefix: "%vault-prefix%"
|
||||
tabsuffix: "%afk%"
|
||||
tagsuffix: "%afk%"
|
||||
customtabname: "%essentialsnick%"
|
||||
customtagname: "%essentialsnick%"
|
||||
|
||||
Users:
|
||||
_NEZNAMY_:
|
||||
tabprefix: "&6&lTAB Author &a"
|
||||
tagprefix: "&6&lTAB &r&a"
|
||||
abovename: "&bOh look, 3 lines of text !"
|
||||
belowname: "&eThere's only one plugin out there supporting it"
|
||||
|
||||
#additional very limited line of text below nametag
|
||||
#don't forget this enables on all player entities, which includes NPCs
|
||||
#tags so you can find this section using the mighty ctrl + f feature: [NPC, Citizens, below_name, health]
|
||||
classic-vanilla-belowname:
|
||||
|
||||
#enabling the feature
|
||||
enabled: true
|
||||
|
||||
#number or numeric placeholder, supports per-player placeholders
|
||||
number: "%health%"
|
||||
|
||||
#text after number, must be same on all players
|
||||
text: "Health"
|
||||
|
||||
#controlling the collision rule
|
||||
enable-collision: true
|
||||
|
||||
#using the opposite of the setting above for worlds
|
||||
revert-collision-rule-in-worlds:
|
||||
- revertedworld
|
||||
|
||||
#preventing players with spectator gamemode from being moved to the bottom of tablist with transparent name
|
||||
do-not-move-spectators: false
|
||||
|
||||
#allows players with tab.spectatorbypass permission to see spectators moved despite the option above enabled
|
||||
allow-spectator-bypass-permission: false
|
||||
|
||||
#using armor stands for nametags, bypassing all limits
|
||||
#THEY ARE CLIENT SIDED AND DO NOT EXIST ON SERVER SO STOP SAYING YOUR SERVER IS LAGGING FROM THIS
|
||||
unlimited-nametag-prefix-suffix-mode:
|
||||
|
||||
#enabling the feature
|
||||
enabled: false
|
||||
|
||||
#enabling will result in NPCs ignoring nametag prefix/suffix/visibility to prevent NPCs with names of online players from being invisible
|
||||
modify-npc-names: false
|
||||
|
||||
#using a feature introduced in 1.8.1 to make hitbox zero so you can't cry it affects pvp despite hit redirection
|
||||
#1.8.0 clients will see wild things
|
||||
#automatically enabled for 1.9+
|
||||
use-marker-tag-for-1-8-x-clients: false
|
||||
|
||||
#disabling the feature for players riding boats to prevent massive desync
|
||||
disable-on-boats: true
|
||||
|
||||
#in blocks
|
||||
space-between-lines: 0.22
|
||||
|
||||
per-world-settings:
|
||||
world1:
|
||||
header:
|
||||
- "an example of world with custom"
|
||||
footer:
|
||||
- "header/footer and prefix/suffix"
|
||||
Groups:
|
||||
Owner:
|
||||
tabprefix: "&0&l[&a&lOwner&0&l] &a"
|
||||
tagprefix: "&2&lOwner &a"
|
||||
|
||||
#separating multiple worlds with "-"
|
||||
world2-world3:
|
||||
header:
|
||||
- "This is a shared header for"
|
||||
- "world2 and world3"
|
||||
|
||||
placeholders:
|
||||
#output of %afk%
|
||||
afk-yes: " &4*&4&lAFK&4*&r"
|
||||
afk-no: ""
|
||||
|
||||
#%date%
|
||||
date-format: "dd.MM.yyyy"
|
||||
|
||||
#%time%
|
||||
time-format: "[HH:mm:ss / h:mm a]"
|
||||
|
||||
#in hours, in case of wrong timezone
|
||||
time-offset: 0
|
||||
|
||||
#don't forget color codes
|
||||
remove-strings:
|
||||
- "[]"
|
||||
- "< >"
|
||||
|
||||
#aliases for %rank%, this is not the place where group prefix is defined!
|
||||
rank-aliases:
|
||||
Admin: "&4&lADMIN"
|
||||
Mod: "&b&lMOD"
|
||||
Premium: "&6&lPREMIUM"
|
||||
Ultra: "&b&lULTRA"
|
||||
Legend: "&a&lLEGEND"
|
||||
Titan: "&c&lTITAN"
|
||||
Youtuber: "&c&lYOUTUBE"
|
||||
_OTHER_: "&7No Rank"
|
||||
|
||||
disable-features-in-worlds:
|
||||
header-footer:
|
||||
- disabledworld
|
||||
tablist-names:
|
||||
- disabledworld
|
||||
nametag:
|
||||
- disabledworld
|
||||
yellow-number:
|
||||
- disabledworld
|
||||
bossbar:
|
||||
- disabledworld
|
||||
belowname:
|
||||
- disabledworld
|
||||
|
||||
placeholderapi-refresh-intervals:
|
||||
default-refresh-interval: 100
|
||||
server:
|
||||
"%server_uptime%": 1000
|
||||
"%server_tps_1_colored%": 1000
|
||||
player:
|
||||
"%player_health%": 200
|
||||
"%player_ping%": 1000
|
||||
"%vault_prefix%": 1000
|
||||
relational:
|
||||
"%rel_factionsuuid_relation_color%": 500
|
||||
|
||||
#different playerlist in different worlds
|
||||
per-world-playerlist:
|
||||
|
||||
#to enable the feature
|
||||
enabled: false
|
||||
|
||||
#allows players with tab.bypass permission to see everyone in tablist despite this option enabled
|
||||
allow-bypass-permission: false
|
||||
|
||||
#players in these worlds will see everyone despite the feature enabled
|
||||
ignore-effect-in-worlds:
|
||||
- ignoredworld
|
||||
- build
|
||||
|
||||
#groups of worlds to share playerlist
|
||||
shared-playerlist-world-groups:
|
||||
#group name, can be anything
|
||||
lobby:
|
||||
- lobby1
|
||||
- lobby2
|
||||
minigames:
|
||||
- paintball
|
||||
- bedwars
|
||||
|
||||
#permission node is tab.sort.<permission group from sorting list>
|
||||
sort-players-by-permissions: false
|
||||
|
||||
#making tamed animals no longer copy nametag properties from their owner (prefix/suffix/visibility)
|
||||
#do not enable this if you're on paper, you'll feed the trolls even more
|
||||
fix-pet-names: false
|
||||
|
||||
#taking primary group from permission plugin
|
||||
use-primary-group: true
|
||||
|
||||
#if the option above is false, all groups are taken from permission plugin and the one higher in this list is used as primary
|
||||
primary-group-finding-list:
|
||||
- Owner
|
||||
- Admin
|
||||
- Helper
|
||||
- default
|
||||
|
||||
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list above> permission
|
||||
assign-groups-by-permissions: false
|
||||
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
|
||||
|
||||
#changing nametags, affecting sorting, collision rule
|
||||
change-nametag-prefix-suffix: true
|
||||
|
||||
#prefix/suffix in tablist
|
||||
change-tablist-prefix-suffix: true
|
||||
|
||||
#header/footer
|
||||
enable-header-footer: true
|
||||
|
||||
header:
|
||||
- "&3&lServer name"
|
||||
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
|
||||
- "&7Online players: &f%online%"
|
||||
- "&6Online staff: &e%staffonline%"
|
||||
- "%animation:ping-tps%"
|
||||
- ""
|
||||
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
|
||||
- "%animation:MyAnimation1%"
|
||||
footer:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "%animation:time%"
|
||||
- ""
|
||||
- "&7Visit our webpage %animation:web%"
|
||||
|
||||
#supports any numeric placeholder, set to "" to disable the feature
|
||||
yellow-number-in-tablist: "%ping%"
|
||||
|
||||
group-sorting-priority-list:
|
||||
- owner
|
||||
- admin
|
||||
- mod
|
||||
- helper
|
||||
- builder
|
||||
- premium
|
||||
- player
|
||||
- default
|
||||
|
||||
Groups:
|
||||
Owner:
|
||||
tabprefix: "&0&l[&a&lOwner&0&l] &a"
|
||||
tagprefix: "&2&lOwner &a"
|
||||
Admin:
|
||||
tabprefix: "&0&l[&4&lAdmin&0&l] &1"
|
||||
tagprefix: "%animation:admin_prefix%"
|
||||
Player:
|
||||
tabprefix: "&0&l[&7&lPlayer&0&l] &3"
|
||||
tagprefix: "&2&lPlayer &3"
|
||||
example_group:
|
||||
header:
|
||||
- "This is an example of per-group header/footer"
|
||||
footer:
|
||||
- "applied to a group"
|
||||
|
||||
#default settings for all groups, all groups will take properties from this section unless player's primary group overrides a specific setting
|
||||
_OTHER_:
|
||||
tabprefix: "%vault-prefix%"
|
||||
tagprefix: "%vault-prefix%"
|
||||
tabsuffix: "%afk%"
|
||||
tagsuffix: "%afk%"
|
||||
customtabname: "%essentialsnick%"
|
||||
customtagname: "%essentialsnick%"
|
||||
|
||||
Users:
|
||||
_NEZNAMY_:
|
||||
tabprefix: "&6&lTAB Author &a"
|
||||
tagprefix: "&6&lTAB &r&a"
|
||||
abovename: "&bOh look, 3 lines of text !"
|
||||
belowname: "&eThere's only one plugin out there supporting it"
|
||||
|
||||
#additional very limited line of text below nametag
|
||||
#don't forget this enables on all player entities, which includes NPCs
|
||||
#tags so you can find this section using the mighty ctrl + f feature: [NPC, Citizens, below_name, health]
|
||||
classic-vanilla-belowname:
|
||||
|
||||
#enabling the feature
|
||||
enabled: true
|
||||
|
||||
#number or numeric placeholder, supports per-player placeholders
|
||||
number: "%health%"
|
||||
|
||||
#text after number, must be same on all players
|
||||
text: "Health"
|
||||
|
||||
#controlling the collision rule
|
||||
enable-collision: true
|
||||
|
||||
#using the opposite of the setting above for worlds
|
||||
revert-collision-rule-in-worlds:
|
||||
- revertedworld
|
||||
|
||||
#preventing players with spectator gamemode from being moved to the bottom of tablist with transparent name
|
||||
do-not-move-spectators: false
|
||||
|
||||
#allows players with tab.spectatorbypass permission to see spectators moved despite the option above enabled
|
||||
allow-spectator-bypass-permission: false
|
||||
|
||||
#using armor stands for nametags, bypassing all limits (they are not on server sided & asynchronous - no TPS loss)
|
||||
unlimited-nametag-prefix-suffix-mode:
|
||||
|
||||
#enabling the feature
|
||||
enabled: false
|
||||
|
||||
#using a feature introduced in 1.8.1 to make hitbox zero so you can't cry it affects pvp despite hit redirection
|
||||
#1.8.0 clients will see wild things
|
||||
#automatically enabled for 1.9+
|
||||
use-marker-tag-for-1-8-x-clients: false
|
||||
|
||||
#disabling the feature for players riding boats to prevent massive desync
|
||||
disable-on-boats: true
|
||||
|
||||
#in blocks
|
||||
space-between-lines: 0.22
|
||||
|
||||
per-world-settings:
|
||||
world1:
|
||||
header:
|
||||
- "an example of world with custom"
|
||||
footer:
|
||||
- "header/footer and prefix/suffix"
|
||||
Groups:
|
||||
Owner:
|
||||
tabprefix: "&0&l[&a&lOwner&0&l] &a"
|
||||
tagprefix: "&2&lOwner &a"
|
||||
|
||||
#separating multiple worlds with "-"
|
||||
world2-world3:
|
||||
header:
|
||||
- "This is a shared header for"
|
||||
- "world2 and world3"
|
||||
|
||||
placeholders:
|
||||
#output of %afk%
|
||||
afk-yes: " &4*&4&lAFK&4*&r"
|
||||
afk-no: ""
|
||||
|
||||
#%date%
|
||||
date-format: "dd.MM.yyyy"
|
||||
|
||||
#%time%
|
||||
time-format: "[HH:mm:ss / h:mm a]"
|
||||
|
||||
#in hours, in case of wrong timezone
|
||||
time-offset: 0
|
||||
|
||||
#don't forget color codes
|
||||
remove-strings:
|
||||
- "[]"
|
||||
- "< >"
|
||||
|
||||
#aliases for %rank%, this is not the place where group prefix is defined!
|
||||
rank-aliases:
|
||||
Admin: "&4&lADMIN"
|
||||
Mod: "&b&lMOD"
|
||||
Premium: "&6&lPREMIUM"
|
||||
Ultra: "&b&lULTRA"
|
||||
Legend: "&a&lLEGEND"
|
||||
Titan: "&c&lTITAN"
|
||||
Youtuber: "&c&lYOUTUBE"
|
||||
_OTHER_: "&7No Rank"
|
||||
|
||||
disable-features-in-worlds:
|
||||
header-footer:
|
||||
- disabledworld
|
||||
tablist-names:
|
||||
- disabledworld
|
||||
nametag:
|
||||
- disabledworld
|
||||
yellow-number:
|
||||
- disabledworld
|
||||
bossbar:
|
||||
- disabledworld
|
||||
belowname:
|
||||
- disabledworld
|
||||
unlimited-nametags:
|
||||
- disabledworld
|
||||
|
||||
placeholderapi-refresh-intervals:
|
||||
default-refresh-interval: 100
|
||||
server:
|
||||
"%server_uptime%": 1000
|
||||
"%server_tps_1_colored%": 1000
|
||||
player:
|
||||
"%player_health%": 200
|
||||
"%player_ping%": 1000
|
||||
"%vault_prefix%": 1000
|
||||
relational:
|
||||
"%rel_factionsuuid_relation_color%": 500
|
||||
|
||||
#different playerlist in different worlds
|
||||
per-world-playerlist:
|
||||
|
||||
#to enable the feature
|
||||
enabled: false
|
||||
|
||||
#allows players with tab.bypass permission to see everyone in tablist despite this option enabled
|
||||
allow-bypass-permission: false
|
||||
|
||||
#players in these worlds will see everyone despite the feature enabled
|
||||
ignore-effect-in-worlds:
|
||||
- ignoredworld
|
||||
- build
|
||||
|
||||
#groups of worlds to share playerlist
|
||||
shared-playerlist-world-groups:
|
||||
#group name, can be anything
|
||||
lobby:
|
||||
- lobby1
|
||||
- lobby2
|
||||
minigames:
|
||||
- paintball
|
||||
- bedwars
|
||||
|
||||
#permission node is tab.sort.<permission group from sorting list>
|
||||
sort-players-by-permissions: false
|
||||
|
||||
#making tamed animals no longer copy nametag properties from their owner (prefix/suffix/visibility)
|
||||
fix-pet-names: false
|
||||
|
||||
#taking primary group from permission plugin
|
||||
use-primary-group: true
|
||||
|
||||
#if the option above is false, all groups are taken from permission plugin and the one higher in this list is used as primary
|
||||
primary-group-finding-list:
|
||||
- Owner
|
||||
- Admin
|
||||
- Helper
|
||||
- default
|
||||
|
||||
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list above> permission
|
||||
assign-groups-by-permissions: false
|
||||
|
||||
#preventing other plugins from overriding TAB
|
||||
anti-override:
|
||||
scoreboard-teams: true
|
||||
scoreboard-objectives: true
|
||||
tablist-names: true
|
||||
usernames: true
|
||||
|
||||
#permanently invisible nametags
|
||||
invisible-nametags: false
|
||||
|
||||
#faking ping of players
|
||||
ping-spoof:
|
||||
enabled: false
|
||||
value: 0
|
||||
@@ -1,10 +1,10 @@
|
||||
name: ${project.name}
|
||||
version: ${project.version}
|
||||
author: NEZNAMY
|
||||
main: me.neznamy.tab.platforms.bukkit.Main
|
||||
api-version: 1.13
|
||||
softdepend: [PlaceholderAPI, Vault, UltraPermissions, LuckPerms, Essentials, LibsDisguises]
|
||||
website: "https://www.spigotmc.org/resources/57806/"
|
||||
description: ${project.description}
|
||||
commands:
|
||||
name: ${project.parent.name}
|
||||
version: ${project.parent.version}
|
||||
author: NEZNAMY
|
||||
main: me.neznamy.tab.platforms.bukkit.Main
|
||||
api-version: 1.13
|
||||
softdepend: [PlaceholderAPI, Vault, UltraPermissions, LuckPerms, Essentials, LibsDisguises, ProtocolSupport]
|
||||
website: "https://www.spigotmc.org/resources/57806/"
|
||||
description: ${project.parent.description}
|
||||
commands:
|
||||
tab:
|
||||
@@ -0,0 +1,164 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>TAB</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</parent>
|
||||
<artifactId>bungeecord</artifactId>
|
||||
|
||||
<repositories>
|
||||
|
||||
<!-- PremiumVanishAPI -->
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
|
||||
<!-- BungeeCord -->
|
||||
<repository>
|
||||
<id>luck-repo</id>
|
||||
<url>https://ci.lucko.me/plugin/repository/everything/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- TAB Core -->
|
||||
<dependency>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>shared</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.github.NEZNAMY</groupId>
|
||||
<artifactId>YamlAssist</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- BungeeCord -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-proxy</artifactId>
|
||||
<version>1.17-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-haproxy</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>brigadier</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>jline</groupId>
|
||||
<artifactId>jline</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-query</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>se.llbit</groupId>
|
||||
<artifactId>jo-nbt</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
<artifactId>jopt-simple</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-resolver-provider</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>
|
||||
maven-resolver-connector-basic
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>
|
||||
maven-resolver-transport-http
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- PremiumVanishAPI -->
|
||||
<dependency>
|
||||
<groupId>com.github.MyzelYam</groupId>
|
||||
<artifactId>PremiumVanishAPI</artifactId>
|
||||
<version>2.0.3</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Bungee Metrics -->
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+2
-6
@@ -35,7 +35,7 @@ public class BungeeEventListener implements Listener {
|
||||
if (!TAB.getInstance().data.containsKey(e.getPlayer().getUniqueId())) {
|
||||
TAB.getInstance().getFeatureManager().onJoin(new BungeeTabPlayer(e.getPlayer()));
|
||||
} else {
|
||||
TAB.getInstance().getFeatureManager().onWorldChange(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getPlayer().getServer().getInfo().getName());
|
||||
TAB.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
|
||||
}
|
||||
} catch (Throwable ex){
|
||||
TAB.getInstance().getErrorManager().criticalError("An error occurred when player joined/changed server", ex);
|
||||
@@ -49,10 +49,6 @@ public class BungeeEventListener implements Listener {
|
||||
@EventHandler
|
||||
public void onChat(ChatEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
if (e.getMessage().startsWith("/")) {
|
||||
if (TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
} else {
|
||||
if (TAB.getInstance().getFeatureManager().onChat(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage(), e.isCancelled())) e.setCancelled(true);
|
||||
}
|
||||
if (e.getMessage().startsWith("/") && TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
+4
-25
@@ -19,18 +19,16 @@ import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardScore;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardTeam;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutTitle;
|
||||
import net.md_5.bungee.protocol.packet.BossBar;
|
||||
import net.md_5.bungee.protocol.packet.Chat;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardObjective;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardObjective.HealthDisplay;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardScore;
|
||||
import net.md_5.bungee.protocol.packet.Team;
|
||||
import net.md_5.bungee.protocol.packet.Title;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
|
||||
|
||||
/**
|
||||
* Packet builder for BungeeCord platform
|
||||
@@ -116,18 +114,7 @@ public class BungeePacketBuilder implements PacketBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutTitle packet, ProtocolVersion clientVersion) throws Exception {
|
||||
Title bungeePacket = new Title();
|
||||
bungeePacket.setAction(Title.Action.valueOf(packet.action.toString()));
|
||||
if (packet.text != null) bungeePacket.setText(IChatBaseComponent.optimizedComponent(packet.text).toString(clientVersion));
|
||||
bungeePacket.setFadeIn(packet.fadeIn);
|
||||
bungeePacket.setStay(packet.stay);
|
||||
bungeePacket.setFadeOut(packet.fadeOut);
|
||||
return bungeePacket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutPlayerInfo readPlayerInfo(Object bungeePacket, ProtocolVersion clientVersion){
|
||||
public PacketPlayOutPlayerInfo readPlayerInfo(Object bungeePacket, ProtocolVersion clientVersion) {
|
||||
PlayerListItem item = (PlayerListItem) bungeePacket;
|
||||
List<PlayerInfoData> listData = new ArrayList<PlayerInfoData>();
|
||||
for (Item i : item.getItems()) {
|
||||
@@ -137,7 +124,7 @@ public class BungeePacketBuilder implements PacketBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutScoreboardObjective readObjective(Object bungeePacket, ProtocolVersion clientVersion) throws Exception {
|
||||
public PacketPlayOutScoreboardObjective readObjective(Object bungeePacket, ProtocolVersion clientVersion) {
|
||||
ScoreboardObjective packet = (ScoreboardObjective) bungeePacket;
|
||||
String title;
|
||||
if (clientVersion.getMinorVersion() >= 13) {
|
||||
@@ -150,15 +137,7 @@ public class BungeePacketBuilder implements PacketBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object bungeePacket, ProtocolVersion clientVersion) throws Exception {
|
||||
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object bungeePacket, ProtocolVersion clientVersion){
|
||||
return new PacketPlayOutScoreboardDisplayObjective(((ScoreboardDisplay) bungeePacket).getPosition(), ((ScoreboardDisplay) bungeePacket).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutPlayerListHeaderFooter readHeaderFooter(Object packet, ProtocolVersion clientVersion) throws Exception {
|
||||
return new PacketPlayOutPlayerListHeaderFooter(
|
||||
IChatBaseComponent.fromString(((PlayerListHeaderFooter)packet).getHeader()),
|
||||
IChatBaseComponent.fromString(((PlayerListHeaderFooter)packet).getFooter())
|
||||
);
|
||||
}
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.protocol.packet.Login;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardObjective;
|
||||
import net.md_5.bungee.protocol.packet.Team;
|
||||
|
||||
public class BungeePipelineInjector extends PipelineInjector {
|
||||
|
||||
//handler to inject before
|
||||
private final String INJECT_POSITION = "inbound-boss";
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
public BungeePipelineInjector(TAB tab) {
|
||||
super(tab);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(TabPlayer player) {
|
||||
if (player.getVersion().getMinorVersion() < 8) return;
|
||||
uninject(player);
|
||||
try {
|
||||
player.getChannel().pipeline().addBefore(INJECT_POSITION, DECODER_NAME, new BungeeChannelDuplexHandler(player));
|
||||
} catch (NoSuchElementException | IllegalArgumentException e) {
|
||||
//idk how does this keep happening but whatever
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninject(TabPlayer player) {
|
||||
if (player.getVersion().getMinorVersion() < 8) return;
|
||||
if (player.getChannel().pipeline().names().contains(DECODER_NAME)) player.getChannel().pipeline().remove(DECODER_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all real players from packet if the packet doesn't come from TAB
|
||||
* @param packet - packet to modify
|
||||
*/
|
||||
private void modifyPlayers(Team packet){
|
||||
long time = System.nanoTime();
|
||||
if (packet.getPlayers() == null) return;
|
||||
Collection<String> col = Lists.newArrayList(packet.getPlayers());
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
if (col.contains(p.getName()) && !tab.getFeatureManager().getNameTagFeature().isDisabledWorld(p.getWorldName()) &&
|
||||
!p.hasTeamHandlingPaused() && !p.getTeamName().equals(packet.getName())) {
|
||||
logTeamOverride(packet.getName(), p.getName());
|
||||
col.remove(p.getName());
|
||||
}
|
||||
}
|
||||
packet.setPlayers(col.toArray(new String[0]));
|
||||
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
public class BungeeChannelDuplexHandler extends ChannelDuplexHandler {
|
||||
|
||||
//injected player
|
||||
private TabPlayer player;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player
|
||||
* @param player - player to inject
|
||||
*/
|
||||
public BungeeChannelDuplexHandler(TabPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) throws Exception {
|
||||
try {
|
||||
if (packet instanceof PlayerListItem) {
|
||||
super.write(context, tab.getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
|
||||
return;
|
||||
}
|
||||
Object modifiedPacket = packet;
|
||||
long time = System.nanoTime();
|
||||
if (packet instanceof ByteBuf) {
|
||||
modifiedPacket = deserialize((ByteBuf) packet);
|
||||
}
|
||||
tab.getCPUManager().addTime(TabFeature.PACKET_DESERIALIZING, UsageType.PACKET_READING_OUT, System.nanoTime()-time);
|
||||
if (tab.getFeatureManager().isFeatureEnabled("nametag16") && antiOverrideTeams) {
|
||||
time = System.nanoTime();
|
||||
if (modifiedPacket instanceof Team) {
|
||||
modifyPlayers((Team) modifiedPacket);
|
||||
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
super.write(context, modifiedPacket, channelPromise);
|
||||
return;
|
||||
}
|
||||
//for now protocolsupport will break the plugin
|
||||
/* if (modifiedPacket instanceof ByteBuf) {
|
||||
ByteBuf buf = ((ByteBuf) modifiedPacket);
|
||||
int marker = buf.readerIndex();
|
||||
int packetId = buf.readByte();
|
||||
if (packetId + 128 == ((BungeeTabPlayer)player).getPacketId(Team.class)){
|
||||
//compressed team packet when using protocolsupport, proper decompression will be needed
|
||||
buf.release();
|
||||
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
return;
|
||||
}
|
||||
buf.readerIndex(marker);
|
||||
}*/
|
||||
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
}
|
||||
if (modifiedPacket instanceof ScoreboardDisplay && antiOverrideObjectives && tab.getFeatureManager().onDisplayObjective(player, modifiedPacket)) {
|
||||
return;
|
||||
}
|
||||
if (modifiedPacket instanceof ScoreboardObjective && antiOverrideObjectives) {
|
||||
tab.getFeatureManager().onObjective(player, modifiedPacket);
|
||||
}
|
||||
//client reset packet
|
||||
if (modifiedPacket instanceof Login) {
|
||||
//making sure to not send own packets before login packet is actually sent
|
||||
super.write(context, modifiedPacket, channelPromise);
|
||||
tab.getFeatureManager().onLoginPacket(player);
|
||||
return;
|
||||
}
|
||||
} catch (Throwable e){
|
||||
tab.getErrorManager().printError("An error occurred when analyzing packets for player " + player.getName() + " with client version " + player.getVersion().getFriendlyName(), e);
|
||||
}
|
||||
super.write(context, packet, channelPromise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes bytebuf in case it is one of the tracked packets coming from backend server and returns it.
|
||||
* If the packet is not one of them, returns input
|
||||
* @param buf - bytebuf to deserialize
|
||||
* @return deserialized packet or input bytebuf if packet is not tracked
|
||||
*/
|
||||
private Object deserialize(ByteBuf buf) {
|
||||
int marker = buf.readerIndex();
|
||||
int packetId = buf.readByte();
|
||||
if (packetId == ((BungeeTabPlayer)player).getPacketId(Team.class)) {
|
||||
Team team = new Team();
|
||||
team.read(buf, null, ((ProxiedPlayer)player.getPlayer()).getPendingConnection().getVersion());
|
||||
buf.release();
|
||||
return team;
|
||||
}
|
||||
if (packetId == ((BungeeTabPlayer)player).getPacketId(ScoreboardDisplay.class)) {
|
||||
ScoreboardDisplay display = new ScoreboardDisplay();
|
||||
display.read(buf, null, ((ProxiedPlayer)player.getPlayer()).getPendingConnection().getVersion());
|
||||
buf.release();
|
||||
return display;
|
||||
}
|
||||
if (packetId == ((BungeeTabPlayer)player).getPacketId(ScoreboardObjective.class)) {
|
||||
ScoreboardObjective objective = new ScoreboardObjective();
|
||||
objective.read(buf, null, ((ProxiedPlayer)player.getPlayer()).getPendingConnection().getVersion());
|
||||
buf.release();
|
||||
return objective;
|
||||
}
|
||||
buf.readerIndex(marker);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-17
@@ -4,7 +4,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.placeholders.Placeholder;
|
||||
@@ -23,22 +22,24 @@ public class BungeePlaceholderRegistry implements PlaceholderRegistry {
|
||||
@Override
|
||||
public List<Placeholder> registerPlaceholders() {
|
||||
List<Placeholder> placeholders = new ArrayList<Placeholder>();
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin("PremiumVanish") != null) {
|
||||
placeholders.add(new ServerPlaceholder("%canseeonline%", 1000) {
|
||||
public String get() {
|
||||
return TAB.getInstance().getPlayers().size() - BungeeVanishAPI.getInvisiblePlayers().size()+"";
|
||||
placeholders.add(new PlayerPlaceholder("%canseeonline%", 2000) {
|
||||
public String get(TabPlayer p) {
|
||||
int var = 0;
|
||||
for (TabPlayer all : TAB.getInstance().getPlayers()){
|
||||
if (!all.isVanished() || p.hasPermission("tab.seevanished")) var++;
|
||||
}
|
||||
});
|
||||
placeholders.add(new ServerPlaceholder("%canseestaffonline%", 1000) {
|
||||
public String get() {
|
||||
int count = 0;
|
||||
for (TabPlayer all : TAB.getInstance().getPlayers()) {
|
||||
if (!((BungeeTabPlayer)all).isVanished() && all.isStaff()) count++;
|
||||
}
|
||||
return count+"";
|
||||
return String.valueOf(var);
|
||||
}
|
||||
});
|
||||
placeholders.add(new PlayerPlaceholder("%canseestaffonline%", 2000) {
|
||||
public String get(TabPlayer p) {
|
||||
int var = 0;
|
||||
for (TabPlayer all : TAB.getInstance().getPlayers()){
|
||||
if (all.isStaff() && (!all.isVanished() || p.hasPermission("tab.seevanished"))) var++;
|
||||
}
|
||||
});
|
||||
}
|
||||
return String.valueOf(var);
|
||||
}
|
||||
});
|
||||
placeholders.add(new PlayerPlaceholder("%displayname%", 500) {
|
||||
public String get(TabPlayer p) {
|
||||
return ((ProxiedPlayer) p.getPlayer()).getDisplayName();
|
||||
@@ -47,7 +48,7 @@ public class BungeePlaceholderRegistry implements PlaceholderRegistry {
|
||||
for (Entry<String, ServerInfo> server : ProxyServer.getInstance().getServers().entrySet()) {
|
||||
placeholders.add(new ServerPlaceholder("%online_" + server.getKey() + "%", 1000) {
|
||||
public String get() {
|
||||
return server.getValue().getPlayers().size()+"";
|
||||
return String.valueOf(server.getValue().getPlayers().size());
|
||||
}
|
||||
});
|
||||
placeholders.add(new ServerPlaceholder("%canseeonline_" + server.getKey() + "%", 1000) {
|
||||
@@ -56,7 +57,7 @@ public class BungeePlaceholderRegistry implements PlaceholderRegistry {
|
||||
for (ProxiedPlayer p : server.getValue().getPlayers()) {
|
||||
if (((BungeeTabPlayer)TAB.getInstance().getPlayer(p.getUniqueId())).isVanished()) count--;
|
||||
}
|
||||
return count+"";
|
||||
return String.valueOf(count);
|
||||
}
|
||||
});
|
||||
}
|
||||
+21
-30
@@ -1,20 +1,19 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.event.BungeeTABLoadEvent;
|
||||
import me.neznamy.tab.shared.Platform;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.config.ConfigurationFile;
|
||||
import me.neznamy.tab.shared.features.BelowName;
|
||||
import me.neznamy.tab.shared.features.GlobalPlayerlist;
|
||||
import me.neznamy.tab.shared.features.NameTag16;
|
||||
import me.neznamy.tab.shared.features.NameTag;
|
||||
import me.neznamy.tab.shared.features.PingSpoof;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManager;
|
||||
import me.neznamy.tab.shared.permission.BungeePerms;
|
||||
import me.neznamy.tab.shared.features.SpectatorFix;
|
||||
import me.neznamy.tab.shared.features.TabObjective;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
|
||||
import me.neznamy.tab.shared.permission.LuckPerms;
|
||||
import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
import me.neznamy.tab.shared.permission.UltraPermissions;
|
||||
@@ -43,12 +42,12 @@ public class BungeePlatform implements Platform {
|
||||
|
||||
@Override
|
||||
public PermissionPlugin detectPermissionPlugin() {
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms") != null) {
|
||||
if (TAB.getInstance().getConfiguration().bukkitPermissions) {
|
||||
return new VaultBridge(Main.plm);
|
||||
} else if (ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms") != null) {
|
||||
return new LuckPerms(ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms").getDescription().getVersion());
|
||||
} else if (ProxyServer.getInstance().getPluginManager().getPlugin("UltraPermissions") != null) {
|
||||
return new UltraPermissions(ProxyServer.getInstance().getPluginManager().getPlugin("UltraPermissions").getDescription().getVersion());
|
||||
} else if (ProxyServer.getInstance().getPluginManager().getPlugin("BungeePerms") != null) {
|
||||
return new BungeePerms(ProxyServer.getInstance().getPluginManager().getPlugin("BungeePerms").getDescription().getVersion());
|
||||
} else {
|
||||
return new VaultBridge(Main.plm);
|
||||
}
|
||||
@@ -60,9 +59,14 @@ public class BungeePlatform implements Platform {
|
||||
tab.getPlaceholderManager().addRegistry(new BungeePlaceholderRegistry());
|
||||
tab.getPlaceholderManager().addRegistry(new UniversalPlaceholderRegistry());
|
||||
tab.getPlaceholderManager().registerPlaceholders();
|
||||
tab.getFeatureManager().registerFeature("injection", new BungeePipelineInjector(tab));
|
||||
if (tab.getConfiguration().config.getBoolean("change-nametag-prefix-suffix", true)) tab.getFeatureManager().registerFeature("nametag16", new NameTag16(tab));
|
||||
if (tab.getConfiguration().pipelineInjection) tab.getFeatureManager().registerFeature("injection", new BungeePipelineInjector(tab));
|
||||
if (tab.getConfiguration().config.getBoolean("change-nametag-prefix-suffix", true)) tab.getFeatureManager().registerFeature("nametag16", new NameTag(tab));
|
||||
loadUniversalFeatures();
|
||||
if (tab.getConfiguration().config.getBoolean("ping-spoof.enabled", false)) tab.getFeatureManager().registerFeature("pingspoof", new PingSpoof());
|
||||
if (tab.getConfiguration().config.getString("yellow-number-in-tablist", "%ping%").length() > 0) tab.getFeatureManager().registerFeature("tabobjective", new TabObjective(tab));
|
||||
if (tab.getConfiguration().config.getBoolean("do-not-move-spectators", false)) tab.getFeatureManager().registerFeature("spectatorfix", new SpectatorFix());
|
||||
if (tab.getConfiguration().config.getBoolean("classic-vanilla-belowname.enabled", true)) tab.getFeatureManager().registerFeature("belowname", new BelowName(tab));
|
||||
if (tab.getConfiguration().premiumconfig != null && tab.getConfiguration().premiumconfig.getBoolean("scoreboard.enabled", false)) tab.getFeatureManager().registerFeature("scoreboard", new ScoreboardManager(tab));
|
||||
if (tab.getConfiguration().config.getBoolean("global-playerlist.enabled", false)) tab.getFeatureManager().registerFeature("globalplayerlist", new GlobalPlayerlist(tab));
|
||||
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
|
||||
tab.addPlayer(new BungeeTabPlayer(p));
|
||||
@@ -96,24 +100,6 @@ public class BungeePlatform implements Platform {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertConfig(ConfigurationFile config) {
|
||||
convertUniversalOptions(config);
|
||||
if (config.getName().equals("config.yml")) {
|
||||
if (config.getObject("global-playerlist") instanceof Boolean) {
|
||||
rename(config, "global-playerlist", "global-playerlist.enabled");
|
||||
config.set("global-playerlist.spy-servers", Arrays.asList("spyserver1", "spyserver2"));
|
||||
Map<String, List<String>> serverGroups = new HashMap<String, List<String>>();
|
||||
serverGroups.put("lobbies", Arrays.asList("lobby1", "lobby2"));
|
||||
serverGroups.put("group2", Arrays.asList("server1", "server2"));
|
||||
config.set("global-playerlist.server-groups", serverGroups);
|
||||
config.set("global-playerlist.display-others-as-spectators", false);
|
||||
TAB.getInstance().print('2', "Converted old global-playerlist section to new one in config.yml.");
|
||||
}
|
||||
rename(config, "tablist-objective-value", "yellow-number-in-tablist");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerVersion() {
|
||||
return ProxyServer.getInstance().getVersion();
|
||||
@@ -138,4 +124,9 @@ public class BungeePlatform implements Platform {
|
||||
public int getMaxPlayers() {
|
||||
return ProxyServer.getInstance().getConfigurationAdapter().getListeners().iterator().next().getMaxPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigName() {
|
||||
return "bungeeconfig.yml";
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -39,9 +39,8 @@ public class BungeePluginMessageHandler implements Listener, PluginMessageHandle
|
||||
long time = System.nanoTime();
|
||||
TabPlayer receiver = TAB.getInstance().getPlayer(((ProxiedPlayer) event.getReceiver()).getUniqueId());
|
||||
if (receiver == null) return;
|
||||
if (onPluginMessage(receiver, ByteStreams.newDataInput(event.getData()))) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
onPluginMessage(receiver, ByteStreams.newDataInput(event.getData()));
|
||||
event.setCancelled(true);
|
||||
TAB.getInstance().getCPUManager().addTime(TabFeature.PLUGIN_MESSAGE_HANDLING, UsageType.PLUGIN_MESSAGE_EVENT, System.nanoTime()-time);
|
||||
}
|
||||
}
|
||||
+25
-1
@@ -7,6 +7,7 @@ import de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
@@ -20,10 +21,14 @@ import net.md_5.bungee.protocol.Protocol;
|
||||
*/
|
||||
public class BungeeTabPlayer extends ITabPlayer {
|
||||
|
||||
//bungee internals to get player channel
|
||||
private static Field wrapperField;
|
||||
private static Object directionData;
|
||||
private static Method getId;
|
||||
|
||||
/**
|
||||
* Initializes fields to get player channel later
|
||||
*/
|
||||
static {
|
||||
try {
|
||||
Field f = Protocol.class.getDeclaredField("TO_CLIENT");
|
||||
@@ -60,6 +65,12 @@ public class BungeeTabPlayer extends ITabPlayer {
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
if (TAB.getInstance().getConfiguration().bukkitPermissions) {
|
||||
String merge = "hasPermission:" + permission;
|
||||
Main.plm.requestAttribute(this, merge);
|
||||
if (!attributes.containsKey(merge)) return false;
|
||||
return Boolean.parseBoolean(attributes.get(merge));
|
||||
}
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
|
||||
@@ -92,6 +103,11 @@ public class BungeeTabPlayer extends ITabPlayer {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns packet ID for this player of provided packet class
|
||||
* @param clazz - packet class
|
||||
* @return - packet ID
|
||||
*/
|
||||
public int getPacketId(Class<? extends DefinedPacket> clazz) {
|
||||
try {
|
||||
return (int) getId.invoke(directionData, clazz, player.getPendingConnection().getVersion());
|
||||
@@ -108,7 +124,10 @@ public class BungeeTabPlayer extends ITabPlayer {
|
||||
|
||||
@Override
|
||||
public boolean isVanished() {
|
||||
return ProxyServer.getInstance().getPluginManager().getPlugin("PremiumVanish") != null && BungeeVanishAPI.isInvisible(player);
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin("PremiumVanish") != null && BungeeVanishAPI.isInvisible(player)) return true;
|
||||
Main.plm.requestAttribute(this, "vanished");
|
||||
if (!attributes.containsKey("vanished")) return false;
|
||||
return Boolean.parseBoolean(attributes.get("vanished"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,4 +148,9 @@ public class BungeeTabPlayer extends ITabPlayer {
|
||||
public boolean isOnline() {
|
||||
return player.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGamemode() {
|
||||
return ((UserConnection)player).getGamemode();
|
||||
}
|
||||
}
|
||||
+19
-3
@@ -5,6 +5,7 @@ import java.util.ArrayList;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.PluginMessageHandler;
|
||||
@@ -61,8 +62,14 @@ public class Main extends Plugin {
|
||||
if (TAB.getInstance() != null) TAB.getInstance().unload();
|
||||
}
|
||||
|
||||
/**
|
||||
* TAB command for bungeecord
|
||||
*/
|
||||
public class BTABCommand extends Command implements TabExecutor {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public BTABCommand() {
|
||||
super("btab", null);
|
||||
}
|
||||
@@ -75,14 +82,23 @@ public class Main extends Plugin {
|
||||
sender.sendMessage(message.replace('&', '\u00a7'));
|
||||
}
|
||||
} else {
|
||||
TAB.getInstance().command.execute(sender instanceof ProxiedPlayer ? TAB.getInstance().getPlayer(((ProxiedPlayer)sender).getUniqueId()) : null, args);
|
||||
TabPlayer p = null;
|
||||
if (sender instanceof ProxiedPlayer) {
|
||||
p = TAB.getInstance().getPlayer(((ProxiedPlayer)sender).getUniqueId());
|
||||
if (p == null) return; //player not loaded correctly
|
||||
}
|
||||
TAB.getInstance().command.execute(p, args);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
if (TAB.getInstance().isDisabled()) return new ArrayList<String>();
|
||||
return TAB.getInstance().command.complete(sender instanceof ProxiedPlayer ? TAB.getInstance().getPlayer(((ProxiedPlayer)sender).getUniqueId()) : null, args);
|
||||
TabPlayer p = null;
|
||||
if (sender instanceof ProxiedPlayer) {
|
||||
p = TAB.getInstance().getPlayer(((ProxiedPlayer)sender).getUniqueId());
|
||||
if (p == null) return new ArrayList<String>(); //player not loaded correctly
|
||||
}
|
||||
return TAB.getInstance().command.complete(p, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -28,7 +28,7 @@ public class PremiumVanishListener implements Listener {
|
||||
if (list == null) return;
|
||||
TabPlayer vanished = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
for (TabPlayer all : TAB.getInstance().getPlayers()) {
|
||||
boolean perm = all.hasPermission(list.PREMIUMVANISH_SEE_VANISHED_PERMISSION);
|
||||
boolean perm = all.hasPermission("tab.seevanished");
|
||||
if (all == vanished || perm) {
|
||||
if (perm && list.vanishedAsSpectators) {
|
||||
all.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_GAME_MODE, new PlayerInfoData(vanished.getUniqueId(), EnumGamemode.SPECTATOR)), TabFeature.GLOBAL_PLAYERLIST);
|
||||
@@ -50,7 +50,7 @@ public class PremiumVanishListener implements Listener {
|
||||
if (list == null) return;
|
||||
TabPlayer unvanished = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
for (TabPlayer viewer : TAB.getInstance().getPlayers()) {
|
||||
if (list.shouldSee(viewer, unvanished)) viewer.sendCustomPacket(list.getAddPacket(unvanished), TabFeature.GLOBAL_PLAYERLIST);
|
||||
if (list.shouldSee(viewer, unvanished)) viewer.sendCustomPacket(list.getAddPacket(unvanished, viewer), TabFeature.GLOBAL_PLAYERLIST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
name: ${project.parent.name}
|
||||
main: me.neznamy.tab.platforms.bungeecord.Main
|
||||
version: ${project.parent.version}
|
||||
author: NEZNAMY
|
||||
+214
-197
@@ -1,197 +1,214 @@
|
||||
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
|
||||
|
||||
#changing nametags, affecting sorting, collision rule
|
||||
change-nametag-prefix-suffix: true
|
||||
|
||||
#prefix/suffix in tablist
|
||||
change-tablist-prefix-suffix: true
|
||||
|
||||
#header/footer
|
||||
enable-header-footer: true
|
||||
|
||||
global-playerlist:
|
||||
enabled: false
|
||||
display-others-as-spectators: false
|
||||
display-vanished-players-as-spectators: true
|
||||
isolate-unlisted-servers: false
|
||||
spy-servers:
|
||||
- spyserver1
|
||||
- spyserver2
|
||||
server-groups:
|
||||
lobbies:
|
||||
- lobby1
|
||||
- lobby2
|
||||
group2:
|
||||
- server1
|
||||
- server2
|
||||
|
||||
header:
|
||||
- "&3&lServer name"
|
||||
- "&6&lRank: %rank%"
|
||||
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
|
||||
- "&7Online players: &f%online%"
|
||||
- "&6Online staff: &e%staffonline%"
|
||||
- ""
|
||||
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
|
||||
- "%animation:MyAnimation1%"
|
||||
footer:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "%animation:time%"
|
||||
- ""
|
||||
- "&7Visit our webpage %animation:web%"
|
||||
|
||||
#supports any numeric placeholder, set to "" to disable the feature
|
||||
yellow-number-in-tablist: "%ping%"
|
||||
|
||||
#literally what it says
|
||||
group-sorting-priority-list:
|
||||
- owner
|
||||
- admin
|
||||
- mod
|
||||
- helper
|
||||
- builder
|
||||
- premium
|
||||
- player
|
||||
- default
|
||||
|
||||
Groups:
|
||||
Owner:
|
||||
tabprefix: "&0&l[&a&lOwner&0&l] &a"
|
||||
tagprefix: "&2&lOwner &a"
|
||||
Admin:
|
||||
tabprefix: "&0&l[&4&lAdmin&0&l] &1"
|
||||
tagprefix: "%animation:admin_prefix%"
|
||||
Player:
|
||||
tabprefix: "&0&l[&7&lPlayer&0&l] &3"
|
||||
tagprefix: "&2&lPlayer &3"
|
||||
example_group:
|
||||
header:
|
||||
- "This is an example of per-group header/footer"
|
||||
footer:
|
||||
- "applied to a group"
|
||||
|
||||
#default settings for all groups, all groups will take properties from this section unless player's primary group overrides a specific setting
|
||||
_OTHER_:
|
||||
tabprefix: "%rank% "
|
||||
tagprefix: "%rank% "
|
||||
|
||||
Users:
|
||||
_NEZNAMY_:
|
||||
tabprefix: "&6&lTAB Author &a"
|
||||
tagprefix: "&6&lTAB &r&a"
|
||||
|
||||
#additional very limited line of text below nametag
|
||||
#don't forget this enables on all player entities, which includes NPCs
|
||||
#tags so you can find this section using the mighty ctrl + f feature: [NPC, Citizens, below_name, health]
|
||||
classic-vanilla-belowname:
|
||||
|
||||
#enabling the feature
|
||||
enabled: true
|
||||
|
||||
#number or numeric placeholder, supports per-player placeholders
|
||||
number: "%ping%"
|
||||
|
||||
#text after number, must be same on all players
|
||||
text: "Ping"
|
||||
|
||||
#controlling the collision rule
|
||||
enable-collision: true
|
||||
|
||||
#using the opposite of the setting above for worlds
|
||||
revert-collision-rule-in-worlds:
|
||||
- revertedworld
|
||||
|
||||
#preventing players with spectator gamemode from being moved to the bottom of tablist with transparent name
|
||||
do-not-move-spectators: false
|
||||
|
||||
#allows players with tab.spectatorbypass permission to see spectators moved despite the option above enabled
|
||||
allow-spectator-bypass-permission: false
|
||||
|
||||
per-server-settings:
|
||||
server1:
|
||||
header:
|
||||
- "an example of world with custom"
|
||||
footer:
|
||||
- "header/footer and prefix/suffix"
|
||||
Groups:
|
||||
Owner:
|
||||
tabprefix: "&0&l[&a&lOwner&0&l] &a"
|
||||
tagprefix: "&2&lOwner &a"
|
||||
server2-server3:
|
||||
header:
|
||||
- "This is a shared header for"
|
||||
- "server2 and server3"
|
||||
placeholders:
|
||||
#%date%
|
||||
date-format: "dd.MM.yyyy"
|
||||
|
||||
#%time%
|
||||
time-format: "[HH:mm:ss / h:mm a]"
|
||||
|
||||
#in hours, in case of wrong timezone
|
||||
time-offset: 0
|
||||
|
||||
#don't forget color codes
|
||||
remove-strings:
|
||||
- "[]"
|
||||
- "< >"
|
||||
|
||||
#aliases for %server%
|
||||
server-aliases:
|
||||
hub: "&eHub"
|
||||
survival: "&bSurvival"
|
||||
plot: "&cPlot"
|
||||
|
||||
#aliases for %rank%, this is not the place where group prefix is defined!
|
||||
rank-aliases:
|
||||
Admin: "&4&lADMIN"
|
||||
Mod: "&b&lMOD"
|
||||
Premium: "&6&lPREMIUM"
|
||||
Ultra: "&b&lULTRA"
|
||||
Legend: "&a&lLEGEND"
|
||||
Titan: "&c&lTITAN"
|
||||
Youtuber: "&c&lYOUTUBE"
|
||||
_OTHER_: "&7No Rank"
|
||||
|
||||
disable-features-in-servers:
|
||||
header-footer:
|
||||
- disabledserver
|
||||
tablist-names:
|
||||
- disabledserver
|
||||
nametag:
|
||||
- disabledserver
|
||||
yellow-number:
|
||||
- disabledserver
|
||||
bossbar:
|
||||
- disabledserver
|
||||
belowname:
|
||||
- disabledserver
|
||||
|
||||
placeholderapi-refresh-intervals:
|
||||
default-refresh-interval: 100
|
||||
server:
|
||||
"%server_uptime%": 1000
|
||||
"%server_tps_1_colored%": 1000
|
||||
player:
|
||||
"%player_health%": 200
|
||||
"%player_ping%": 1000
|
||||
"%vault_prefix%": 1000
|
||||
relational:
|
||||
"%rel_factionsuuid_relation_color%": 500
|
||||
|
||||
|
||||
#permission node is tab.sort.<permission group from sorting list>
|
||||
sort-players-by-permissions: false
|
||||
|
||||
#taking primary group from permission plugin
|
||||
use-primary-group: true
|
||||
|
||||
#if the option above is false, all groups are taken from permission plugin and the one higher in this list is used as primary
|
||||
primary-group-finding-list:
|
||||
- Owner
|
||||
- Admin
|
||||
- Helper
|
||||
- default
|
||||
|
||||
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list above> permission
|
||||
assign-groups-by-permissions: false
|
||||
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
|
||||
|
||||
#taking permission nodes and groups from backend servers
|
||||
use-bukkit-permissions-manager: false
|
||||
|
||||
#changing nametags, affecting sorting, collision rule
|
||||
change-nametag-prefix-suffix: true
|
||||
|
||||
#prefix/suffix in tablist
|
||||
change-tablist-prefix-suffix: true
|
||||
|
||||
#header/footer
|
||||
enable-header-footer: true
|
||||
|
||||
global-playerlist:
|
||||
enabled: false
|
||||
display-others-as-spectators: false
|
||||
display-vanished-players-as-spectators: true
|
||||
isolate-unlisted-servers: false
|
||||
spy-servers:
|
||||
- spyserver1
|
||||
- spyserver2
|
||||
server-groups:
|
||||
lobbies:
|
||||
- lobby1
|
||||
- lobby2
|
||||
group2:
|
||||
- server1
|
||||
- server2
|
||||
|
||||
header:
|
||||
- "&3&lServer name"
|
||||
- "&6&lRank: %rank%"
|
||||
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
|
||||
- "&7Online players: &f%online%"
|
||||
- "&6Online staff: &e%staffonline%"
|
||||
- ""
|
||||
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
|
||||
- "%animation:MyAnimation1%"
|
||||
footer:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "%animation:time%"
|
||||
- ""
|
||||
- "&7Visit our webpage %animation:web%"
|
||||
|
||||
#supports any numeric placeholder, set to "" to disable the feature
|
||||
yellow-number-in-tablist: "%ping%"
|
||||
|
||||
group-sorting-priority-list:
|
||||
- owner
|
||||
- admin
|
||||
- mod
|
||||
- helper
|
||||
- builder
|
||||
- premium
|
||||
- player
|
||||
- default
|
||||
|
||||
Groups:
|
||||
Owner:
|
||||
tabprefix: "&0&l[&a&lOwner&0&l] &a"
|
||||
tagprefix: "&2&lOwner &a"
|
||||
Admin:
|
||||
tabprefix: "&0&l[&4&lAdmin&0&l] &1"
|
||||
tagprefix: "%animation:admin_prefix%"
|
||||
Player:
|
||||
tabprefix: "&0&l[&7&lPlayer&0&l] &3"
|
||||
tagprefix: "&2&lPlayer &3"
|
||||
example_group:
|
||||
header:
|
||||
- "This is an example of per-group header/footer"
|
||||
footer:
|
||||
- "applied to a group"
|
||||
|
||||
#default settings for all groups, all groups will take properties from this section unless player's primary group overrides a specific setting
|
||||
_OTHER_:
|
||||
tabprefix: "%rank% "
|
||||
tagprefix: "%rank% "
|
||||
|
||||
Users:
|
||||
_NEZNAMY_:
|
||||
tabprefix: "&6&lTAB Author &a"
|
||||
tagprefix: "&6&lTAB &r&a"
|
||||
|
||||
#additional very limited line of text below nametag
|
||||
#don't forget this enables on all player entities, which includes NPCs
|
||||
#tags so you can find this section using the mighty ctrl + f feature: [NPC, Citizens, below_name, health]
|
||||
classic-vanilla-belowname:
|
||||
|
||||
#enabling the feature
|
||||
enabled: true
|
||||
|
||||
#number or numeric placeholder, supports per-player placeholders
|
||||
number: "%ping%"
|
||||
|
||||
#text after number, must be same on all players
|
||||
text: "Ping"
|
||||
|
||||
#controlling the collision rule
|
||||
enable-collision: true
|
||||
|
||||
#using the opposite of the setting above for servers
|
||||
revert-collision-rule-in-servers:
|
||||
- revertedserver
|
||||
|
||||
#preventing players with spectator gamemode from being moved to the bottom of tablist with transparent name
|
||||
do-not-move-spectators: false
|
||||
|
||||
#allows players with tab.spectatorbypass permission to see spectators moved despite the option above enabled
|
||||
allow-spectator-bypass-permission: false
|
||||
|
||||
per-server-settings:
|
||||
server1:
|
||||
header:
|
||||
- "an example of world with custom"
|
||||
footer:
|
||||
- "header/footer and prefix/suffix"
|
||||
Groups:
|
||||
Owner:
|
||||
tabprefix: "&0&l[&a&lOwner&0&l] &a"
|
||||
tagprefix: "&2&lOwner &a"
|
||||
server2-server3:
|
||||
header:
|
||||
- "This is a shared header for"
|
||||
- "server2 and server3"
|
||||
placeholders:
|
||||
#%date%
|
||||
date-format: "dd.MM.yyyy"
|
||||
|
||||
#%time%
|
||||
time-format: "[HH:mm:ss / h:mm a]"
|
||||
|
||||
#in hours, in case of wrong timezone
|
||||
time-offset: 0
|
||||
|
||||
#don't forget color codes
|
||||
remove-strings:
|
||||
- "[]"
|
||||
- "< >"
|
||||
|
||||
#aliases for %server%
|
||||
server-aliases:
|
||||
hub: "&eHub"
|
||||
survival: "&bSurvival"
|
||||
plot: "&cPlot"
|
||||
|
||||
#aliases for %rank%, this is not the place where group prefix is defined!
|
||||
rank-aliases:
|
||||
Admin: "&4&lADMIN"
|
||||
Mod: "&b&lMOD"
|
||||
Premium: "&6&lPREMIUM"
|
||||
Ultra: "&b&lULTRA"
|
||||
Legend: "&a&lLEGEND"
|
||||
Titan: "&c&lTITAN"
|
||||
Youtuber: "&c&lYOUTUBE"
|
||||
_OTHER_: "&7No Rank"
|
||||
|
||||
disable-features-in-servers:
|
||||
header-footer:
|
||||
- disabledserver
|
||||
tablist-names:
|
||||
- disabledserver
|
||||
nametag:
|
||||
- disabledserver
|
||||
yellow-number:
|
||||
- disabledserver
|
||||
bossbar:
|
||||
- disabledserver
|
||||
belowname:
|
||||
- disabledserver
|
||||
|
||||
placeholderapi-refresh-intervals:
|
||||
default-refresh-interval: 100
|
||||
server:
|
||||
"%server_uptime%": 1000
|
||||
"%server_tps_1_colored%": 1000
|
||||
player:
|
||||
"%player_health%": 200
|
||||
"%player_ping%": 1000
|
||||
"%vault_prefix%": 1000
|
||||
relational:
|
||||
"%rel_factionsuuid_relation_color%": 500
|
||||
|
||||
|
||||
#permission node is tab.sort.<permission group from sorting list>
|
||||
sort-players-by-permissions: false
|
||||
|
||||
#taking primary group from permission plugin
|
||||
use-primary-group: true
|
||||
|
||||
#if the option above is false, all groups are taken from permission plugin and the one higher in this list is used as primary
|
||||
primary-group-finding-list:
|
||||
- Owner
|
||||
- Admin
|
||||
- Helper
|
||||
- default
|
||||
|
||||
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list above> permission
|
||||
assign-groups-by-permissions: false
|
||||
|
||||
#preventing other plugins from overriding TAB
|
||||
anti-override:
|
||||
scoreboard-teams: true
|
||||
scoreboard-objectives: true
|
||||
tablist-names: true
|
||||
usernames: true
|
||||
|
||||
#permanently invisible nametags
|
||||
invisible-nametags: false
|
||||
|
||||
#faking ping of players
|
||||
ping-spoof:
|
||||
enabled: false
|
||||
value: 0
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>TAB</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</parent>
|
||||
<artifactId>jar</artifactId>
|
||||
<build>
|
||||
<finalName>TAB-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>com.googlecode.json-simple:json-simple</include>
|
||||
<include>com.github.NEZNAMY:YamlAssist</include>
|
||||
<include>com.github.NEZNAMY:VelocityPacketInjector</include>
|
||||
<include>org.bstats:bstats-base</include>
|
||||
<include>org.bstats:bstats-bungeecord</include>
|
||||
<include>org.bstats:bstats-velocity</include>
|
||||
<include>me.neznamy:*</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>me.neznamy.tab.platforms</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>bungeecord</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>shared</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>velocity-1.1.0</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,13 +1,25 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>TAB</artifactId>
|
||||
<version>2.8.11-pre6</version>
|
||||
<name>TAB</name>
|
||||
<version>2.9.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<description>An all-in-one solution that works</description>
|
||||
|
||||
<modules>
|
||||
<module>shared</module>
|
||||
<module>bukkit</module>
|
||||
<module>bungeecord</module>
|
||||
<module>velocity-1.1.0</module>
|
||||
<module>jar</module>
|
||||
</modules>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
@@ -15,431 +27,5 @@
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<configuration>
|
||||
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>com.googlecode.json-simple:json-simple</include>
|
||||
<include>com.github.NEZNAMY:YamlAssist</include>
|
||||
<include>com.github.NEZNAMY:VelocityPacketInjector</include>
|
||||
<include>org.bstats:bstats-base</include>
|
||||
<include>org.bstats:bstats-bungeecord</include>
|
||||
<include>org.bstats:bstats-velocity</include>
|
||||
|
||||
</includes>
|
||||
</artifactSet>
|
||||
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>me.neznamy.tab.platforms</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<repositories>
|
||||
|
||||
<!-- Spigot API -->
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
|
||||
<!-- PlaceholderAPI -->
|
||||
<repository>
|
||||
<id>placeholderapi</id>
|
||||
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
||||
</repository>
|
||||
|
||||
<!-- Vault -->
|
||||
<!-- PremiumVanishAPI -->
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
|
||||
<!-- LuckPerms5 -->
|
||||
<!-- BungeeCord -->
|
||||
<repository>
|
||||
<id>luck-repo</id>
|
||||
<url>https://ci.lucko.me/plugin/repository/everything/</url>
|
||||
</repository>
|
||||
|
||||
<!-- ViaVersion -->
|
||||
<repository>
|
||||
<id>viaversion</id>
|
||||
<url>https://repo.viaversion.com/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>velocity</id>
|
||||
<url>https://nexus.velocitypowered.com/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- Spigot API -->
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.16.5-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- json-simple, shaded -->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- BungeeCord -->
|
||||
<!-- com.google.common.io -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-proxy</artifactId>
|
||||
<version>1.16-R0.5-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-haproxy</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-epoll</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>brigadier</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>jline</groupId>
|
||||
<artifactId>jline</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-transport</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-query</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>se.llbit</groupId>
|
||||
<artifactId>jo-nbt</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
<artifactId>jopt-simple</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- PlaceholderAPI -->
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.10.9</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Vault -->
|
||||
<dependency>
|
||||
<groupId>com.github.MilkBowl</groupId>
|
||||
<artifactId>VaultAPI</artifactId>
|
||||
<version>1.7</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- LuckPerms5 -->
|
||||
<dependency>
|
||||
<groupId>net.luckperms</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>5.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- ViaVersion -->
|
||||
<dependency>
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion-common</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion-bukkit</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion-bungee</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion-fabric</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion-sponge</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>us.myles</groupId>
|
||||
<artifactId>viaversion-velocity</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- PremiumVanishAPI -->
|
||||
<dependency>
|
||||
<groupId>com.github.MyzelYam</groupId>
|
||||
<artifactId>PremiumVanishAPI</artifactId>
|
||||
<version>2.0.3</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- YamlAssist -->
|
||||
<dependency>
|
||||
<groupId>com.github.NEZNAMY</groupId>
|
||||
<artifactId>YamlAssist</artifactId>
|
||||
<version>1.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Velocity API -->
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.moandjiezana.toml</groupId>
|
||||
<artifactId>toml4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-gson</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-hocon</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>brigadier</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-yaml</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>
|
||||
adventure-text-serializer-legacy-text3
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>
|
||||
adventure-text-serializer-legacy
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>
|
||||
adventure-text-serializer-plain
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>text-serializer-plain</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.j2objc</groupId>
|
||||
<artifactId>j2objc-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>text-serializer-legacy</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>text-serializer-gson</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>examination-string</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>aopalliance</groupId>
|
||||
<artifactId>aopalliance</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Bungee Metrics -->
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Velocity Metrics -->
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-velocity</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Missing velocity packets -->
|
||||
<dependency>
|
||||
<groupId>com.github.NEZNAMY</groupId>
|
||||
<artifactId>VelocityPacketInjector</artifactId>
|
||||
<version>1.0.0_fix</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>me.neznamy</groupId>
|
||||
<artifactId>TAB</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</parent>
|
||||
<artifactId>shared</artifactId>
|
||||
|
||||
<repositories>
|
||||
|
||||
<!-- LuckPerms5 -->
|
||||
<repository>
|
||||
<id>luck-repo</id>
|
||||
<url>https://ci.lucko.me/plugin/repository/everything/</url>
|
||||
</repository>
|
||||
|
||||
<!-- YamlAssist -->
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.63.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.16.5-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.17-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-config</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>
|
||||
netty-transport-native-unix-common
|
||||
</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-protocol</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-event</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- json-simple, shaded -->
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- LuckPerms5 -->
|
||||
<dependency>
|
||||
<groupId>net.luckperms</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>5.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- YamlAssist -->
|
||||
<dependency>
|
||||
<groupId>com.github.NEZNAMY</groupId>
|
||||
<artifactId>YamlAssist</artifactId>
|
||||
<version>1.0.4</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
File renamed without changes.
@@ -0,0 +1,157 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A helper class for easy management of armor stands of a player
|
||||
*/
|
||||
public class ArmorStandManager {
|
||||
|
||||
//map of registered armor stands
|
||||
private Map<String, ArmorStand> armorStands = Collections.synchronizedMap(new LinkedHashMap<String, ArmorStand>());
|
||||
|
||||
/**
|
||||
* Adds armor stand into list
|
||||
* @param name - key name of the armor stand
|
||||
* @param as - armor stand to add
|
||||
*/
|
||||
public void addArmorStand(String name, ArmorStand as) {
|
||||
armorStands.put(name, as);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (viewer.getVersion().getMinorVersion() < 8) return;
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> a.spawn(viewer));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets sneak value of armor stands to specified value
|
||||
* @param sneaking - new sneaking status
|
||||
*/
|
||||
public void sneak(boolean sneaking) {
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> a.sneak(sneaking));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports armor stands to player's current location for all nearby players
|
||||
*/
|
||||
public void teleport() {
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> 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) {
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> a.teleport(viewer));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes name of all armor stands for all nearby players
|
||||
*/
|
||||
public void refresh() {
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> 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) {
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> a.updateVisibility(force));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes specified player from list of nearby players
|
||||
* @param viewer - player to remove
|
||||
*/
|
||||
public void unregisterPlayer(TabPlayer viewer) {
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> a.removeFromRegistered(viewer));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends destroy packet of all armor stands to everyone and clears nearby players list
|
||||
*/
|
||||
public void destroy() {
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> a.destroy());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
synchronized (armorStands) {
|
||||
armorStands.values().forEach(a -> a.destroy(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) {
|
||||
synchronized (armorStands) {
|
||||
for (ArmorStand as : armorStands.values()) {
|
||||
if (as.getEntityId() == entityId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of list of all registered armor stands
|
||||
* @return copy of armor stands
|
||||
*/
|
||||
public Collection<ArmorStand> getArmorStands(){
|
||||
return armorStands.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of nearby players
|
||||
* @return list of nearby players
|
||||
*/
|
||||
public Set<TabPlayer> getNearbyPlayers(){
|
||||
if (armorStands.values().size() == 0) return new HashSet<>(); //not initialized yet
|
||||
synchronized (armorStands) {
|
||||
return armorStands.values().iterator().next().getNearbyPlayers();
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,33 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An interface to be worked with to manipulate scoreboard
|
||||
*/
|
||||
public interface Scoreboard {
|
||||
|
||||
/**
|
||||
* Unregisters this scoreboard from specified player
|
||||
* @param p - player to unregister scoreboard for
|
||||
*/
|
||||
public void unregister(TabPlayer p);
|
||||
|
||||
/**
|
||||
* Registers this scoreboard to specified player
|
||||
* @param p - player to register scoreboard for
|
||||
*/
|
||||
public void register(TabPlayer p);
|
||||
|
||||
/**
|
||||
* Returns name of this scoreboard
|
||||
* @return name of this scoreboard
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Returns list of all players who can see this scoreboard
|
||||
* @return list of players who can see this scoreboard
|
||||
*/
|
||||
public Set<TabPlayer> getRegisteredUsers();
|
||||
}
|
||||
+31
-34
@@ -24,49 +24,39 @@ import me.neznamy.tab.shared.placeholders.conditions.simple.SimpleCondition;
|
||||
*/
|
||||
public class TABAPI {
|
||||
|
||||
|
||||
//placeholders registered via API
|
||||
public static Map<String, Placeholder> APIPlaceholders = new HashMap<String, Placeholder>();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns player object from given UUID
|
||||
* @return player object from given UUID
|
||||
* @param id - Player UUID
|
||||
* @since 2.8.3
|
||||
*/
|
||||
public static TabPlayer getPlayer(UUID id) {
|
||||
return TAB.getInstance().getPlayer(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns player object from given name
|
||||
* @return player object from given name
|
||||
* @param name - Player name
|
||||
* @since 2.8.3
|
||||
*/
|
||||
public static TabPlayer getPlayer(String name) {
|
||||
return TAB.getInstance().getPlayer(name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if enabled, false if disabled
|
||||
* @return Whether unlimited nametag mode is enabled or not
|
||||
* @see enableUnlimitedNameTagModePermanently
|
||||
* @since 2.4.12
|
||||
*/
|
||||
public static boolean isUnlimitedNameTagModeEnabled() {
|
||||
return TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables unlimited nametag mode permanently in config
|
||||
* @throws IllegalStateException if called from a proxy
|
||||
* @see isUnlimitedNameTagModeEnabled
|
||||
* @since 2.4.12
|
||||
*/
|
||||
public static void enableUnlimitedNameTagModePermanently() {
|
||||
if (isUnlimitedNameTagModeEnabled()) return;
|
||||
@@ -76,11 +66,9 @@ public class TABAPI {
|
||||
TAB.getInstance().load();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a player placeholder (placeholder with player-specific output)
|
||||
* @param placeholder - Placeholder handler
|
||||
* @since 2.6.5
|
||||
* @see registerServerPlaceholder
|
||||
* @see registerServerConstant
|
||||
*/
|
||||
@@ -89,13 +77,12 @@ public class TABAPI {
|
||||
PlaceholderManager pl = TAB.getInstance().getPlaceholderManager();
|
||||
pl.registerPlaceholder(placeholder);
|
||||
pl.allUsedPlaceholderIdentifiers.add(placeholder.getIdentifier());
|
||||
pl.refreshPlaceholderUsage();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a server placeholder (placeholder with same output for all players)
|
||||
* @param placeholder - Placeholder handler
|
||||
* @since 2.6.5
|
||||
* @see registerPlayerPlaceholder
|
||||
* @see registerServerConstant
|
||||
*/
|
||||
@@ -104,34 +91,27 @@ public class TABAPI {
|
||||
PlaceholderManager pl = TAB.getInstance().getPlaceholderManager();
|
||||
pl.registerPlaceholder(placeholder);
|
||||
pl.allUsedPlaceholderIdentifiers.add(placeholder.getIdentifier());
|
||||
pl.refreshPlaceholderUsage();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a relational placeholder
|
||||
* @param placeholder - Placeholder handler
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public static void registerRelationalPlaceholder(RelationalPlaceholder placeholder) {
|
||||
APIPlaceholders.put(placeholder.getIdentifier(), placeholder);
|
||||
PlaceholderManager pl = TAB.getInstance().getPlaceholderManager();
|
||||
pl.registerPlaceholder(placeholder);
|
||||
pl.allUsedPlaceholderIdentifiers.add(placeholder.getIdentifier());
|
||||
pl.refreshPlaceholderUsage();
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static Scoreboard createScoreboard(String title, List<String> lines) {
|
||||
return createScoreboard("Unnamed scoreboard", title, lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new scoreboard
|
||||
* @param name - name of the scoreboard
|
||||
* @param title - the scoreboard title
|
||||
* @param lines - up to 15 lines of text (supports placeholders)
|
||||
* @return The new scoreboard
|
||||
* @since 2.8.8
|
||||
*/
|
||||
public static Scoreboard createScoreboard(String name, String title, List<String> lines) {
|
||||
for (String line : lines) {
|
||||
@@ -143,28 +123,45 @@ public class TABAPI {
|
||||
sbm.APIscoreboards.add(sb);
|
||||
return sb;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a custom permission plugin
|
||||
* @param permission - permission plugin provider
|
||||
* @since 2.8.3
|
||||
*/
|
||||
public static void registerPermissionPlugin(PermissionPlugin permission) {
|
||||
TAB.getInstance().setPermissionPlugin(permission);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates bossbar with specified parameters
|
||||
* @param name - internal name of bossbar
|
||||
* @param title - title
|
||||
* @param progress - progress (0-1)
|
||||
* @param color - color
|
||||
* @param style - style
|
||||
* @return the bossbar
|
||||
*/
|
||||
public static BossBar createBossBar(String name, String title, float progress, BarColor color, BarStyle style) {
|
||||
return createBossBar(name, title, progress+"", color.toString(), style.toString());
|
||||
return createBossBar(name, title, String.valueOf(progress), color.toString(), style.toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates bossbar with specified parameters as strings to allow placeholder support
|
||||
* @param name - internal name of bossbar
|
||||
* @param title - title
|
||||
* @param progress - progress
|
||||
* @param color - color
|
||||
* @param style - style
|
||||
* @return the bossbar
|
||||
*/
|
||||
public static BossBar createBossBar(String name, String title, String progress, String color, String style) {
|
||||
me.neznamy.tab.shared.features.bossbar.BossBar feature = (me.neznamy.tab.shared.features.bossbar.BossBar) TAB.getInstance().getFeatureManager().getFeature("bossbar");
|
||||
if (feature == null) throw new IllegalStateException("Bossbar feature is not enabled");
|
||||
BossBar bar = new BossBarLine(TAB.getInstance(), name, null, color, style, title, progress);
|
||||
BossBar bar = new BossBarLine(name, null, color, style, title, progress);
|
||||
feature.lines.put(bar.getName(), (BossBarLine) bar);
|
||||
return bar;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns placeholders registered via API
|
||||
* @return placeholders registered via API
|
||||
@@ -172,7 +169,7 @@ public class TABAPI {
|
||||
public static Map<String, Placeholder> getAPIPlaceholders(){
|
||||
return APIPlaceholders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers custom condition type and return true if it was registered or false if it was already registered. Useful since
|
||||
* /tab reload does not remove registered condition types.
|
||||
@@ -0,0 +1,467 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.api.bossbar.BossBar;
|
||||
import me.neznamy.tab.shared.Property;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.packets.UniversalPacketPlayOut;
|
||||
|
||||
/**
|
||||
* An interface representing a player
|
||||
*/
|
||||
public interface TabPlayer {
|
||||
|
||||
/**
|
||||
* Changes the requested property of a player temporarily (until next restart, reload or /tab reload)
|
||||
* @param type Type of property
|
||||
* @param value The value to be used
|
||||
*/
|
||||
public void setValueTemporarily(EnumProperty type, String value);
|
||||
|
||||
/**
|
||||
* Changes the requested property of a player permanently (saved into config too)
|
||||
* @param type Type of property
|
||||
* @param value The value to be used
|
||||
*/
|
||||
public void setValuePermanently(EnumProperty type, String value);
|
||||
|
||||
/**
|
||||
* Returns temporary value of player's property or null if not set
|
||||
* @param type Type of property
|
||||
* @return Temporary value of player's property or null if not set
|
||||
* @see hasTemporaryValue
|
||||
* @see setValueTemporarily
|
||||
*/
|
||||
public String getTemporaryValue(EnumProperty type);
|
||||
|
||||
/**
|
||||
* Returns Whether player has temporary value or not
|
||||
* @param type Type of property
|
||||
* @return Whether player has temporary value or not
|
||||
*/
|
||||
public boolean hasTemporaryValue(EnumProperty type);
|
||||
|
||||
/**
|
||||
* Removes temporary value from player if set
|
||||
* @param type Type of property
|
||||
*/
|
||||
public void removeTemporaryValue(EnumProperty type);
|
||||
|
||||
/**
|
||||
* Returns original value of property of player
|
||||
* @param type Type of property
|
||||
* @return Original value of property of player
|
||||
*/
|
||||
public String getOriginalValue(EnumProperty type);
|
||||
|
||||
/**
|
||||
* Makes player's nametag globally invisible
|
||||
* @see showNametag
|
||||
* @see hasHiddenNametag
|
||||
*/
|
||||
public void hideNametag();
|
||||
|
||||
/**
|
||||
* Hides player's nametag for specified player until it's shown again
|
||||
* @param viewer - player to hide nametag for
|
||||
*/
|
||||
public void hideNametag(UUID viewer);
|
||||
|
||||
/**
|
||||
* Makes player's nametag visible again
|
||||
* @see hideNametag
|
||||
* @see hasHiddenNametag
|
||||
*/
|
||||
public void showNametag();
|
||||
|
||||
/**
|
||||
* Shows player's nametag for specified viewer if it was hidden before
|
||||
* @param viewer - player to show nametag back for
|
||||
*/
|
||||
public void showNametag(UUID viewer);
|
||||
|
||||
|
||||
/**
|
||||
* Return whether player has hidden nametag or not
|
||||
* @return Whether player has hidden nametag or not
|
||||
* @see hideNametag
|
||||
* @see showNametag
|
||||
*/
|
||||
public boolean hasHiddenNametag();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public boolean hasHiddenNametag(UUID viewer);
|
||||
|
||||
|
||||
/**
|
||||
* Refreshes all visuals on the player
|
||||
*/
|
||||
public void forceRefresh();
|
||||
|
||||
|
||||
/**
|
||||
* Displays a scoreboard created using TABAPI.createScoreboard method and disables
|
||||
* automatic scoreboard assigning until this is reverted using removeCustomScoreboard()
|
||||
* @see removeCustomScoreboard
|
||||
*/
|
||||
public void showScoreboard(Scoreboard scoreboard);
|
||||
|
||||
|
||||
/**
|
||||
* Displays a scoreboard defined in premiumconfig.yml and disabled automatic
|
||||
* scoreboard assignment until this is reverted using removeCustomScoreboard()
|
||||
* @see removeCustomScoreboard
|
||||
*/
|
||||
public void showScoreboard(String name);
|
||||
|
||||
|
||||
/**
|
||||
* Removes forced scoreboard sent using one of the showScoreboard methods
|
||||
* @see showScoreboard
|
||||
*/
|
||||
public void removeCustomScoreboard();
|
||||
|
||||
|
||||
/**
|
||||
* Returns player's name
|
||||
* @return Player's name
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
|
||||
/**
|
||||
* Returns player's UUID
|
||||
* @return Player's UUID
|
||||
*/
|
||||
public UUID getUniqueId();
|
||||
|
||||
/**
|
||||
* Returns player's uuid used in tablist. This may only be different than real uuid if
|
||||
* TAB is installed on velocity with some specific velocity setup
|
||||
* @return player's uuid in tablist
|
||||
*/
|
||||
public UUID getTablistUUID();
|
||||
|
||||
/**
|
||||
* Returns player's protocol version
|
||||
* @return player's protocol version
|
||||
*/
|
||||
public ProtocolVersion getVersion();
|
||||
|
||||
/**
|
||||
* Returns platform-specific entity
|
||||
* @return an instance of bukkit/bungee/velocity player depending on platform
|
||||
*/
|
||||
public Object getPlayer();
|
||||
|
||||
/**
|
||||
* Returns player's current world name (server on bungeecord)
|
||||
* @return name of world (server on bungeecord) where player is currently in
|
||||
*/
|
||||
public String getWorldName();
|
||||
|
||||
/**
|
||||
* Performs platform-specific API call to check for permission and returns the result
|
||||
* @param permission - the permission to check for
|
||||
* @return - true if has, false if not
|
||||
*/
|
||||
public boolean hasPermission(String permission);
|
||||
|
||||
/**
|
||||
* Sends the player a custom universal packet
|
||||
* @param packet - packet to send
|
||||
*/
|
||||
public void sendCustomPacket(UniversalPacketPlayOut 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
|
||||
*/
|
||||
public void sendCustomPacket(UniversalPacketPlayOut packet, TabFeature feature);
|
||||
|
||||
/**
|
||||
* Sends the player a platform-specific packet
|
||||
* @param packet - an instance of packet depending on platform
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
public void sendPacket(Object packet, TabFeature feature);
|
||||
|
||||
/**
|
||||
* Returns player's property by name
|
||||
* @param name - name of property
|
||||
* @return the property or null if not found
|
||||
*/
|
||||
public Property getProperty(String name);
|
||||
|
||||
/**
|
||||
* Sends a message to the player
|
||||
* @param message - message to be sent
|
||||
* @param translateColors - whether colors should be translated or not
|
||||
*/
|
||||
public void sendMessage(String message, boolean translateColors);
|
||||
|
||||
/**
|
||||
* Sends specified component as a chat message
|
||||
* @param message - message to send
|
||||
*/
|
||||
public void sendMessage(IChatBaseComponent message);
|
||||
|
||||
/**
|
||||
* Returns player's primary permission group
|
||||
* @return player's group depending on configuration
|
||||
*/
|
||||
public String getGroup();
|
||||
|
||||
/**
|
||||
* Toggles armor stands preview mode for the player
|
||||
*/
|
||||
public void toggleNametagPreview();
|
||||
|
||||
/**
|
||||
* Returns whether player is previewing nametag or not
|
||||
* @return true if previewing, false if not
|
||||
*/
|
||||
public boolean isPreviewingNametag();
|
||||
|
||||
/**
|
||||
* Returns whether player has tab.staff permission or not
|
||||
* @return whether player is staff or not
|
||||
*/
|
||||
public boolean isStaff();
|
||||
|
||||
/**
|
||||
* Returns player's channel or null if server is 1.7 or older
|
||||
* @return player's channel
|
||||
*/
|
||||
public Channel getChannel();
|
||||
|
||||
/**
|
||||
* Returns player's ping calculated by server
|
||||
* @return player's ping
|
||||
*/
|
||||
public long getPing();
|
||||
|
||||
/**
|
||||
* Returns player's platform-specific skin data
|
||||
* @return player's skin
|
||||
*/
|
||||
public Object getSkin();
|
||||
|
||||
/**
|
||||
* Returns true once the player is successfully loaded (onJoin method ran through all methods)
|
||||
* @return true if player is fully loaded, false otherwise
|
||||
*/
|
||||
public boolean isLoaded();
|
||||
|
||||
/**
|
||||
* Returns true if player has bossbar visible, false if player hid bossbar(s) using bossbar
|
||||
* toggle command
|
||||
* @return true if visible, false if toggled
|
||||
*/
|
||||
public boolean hasBossbarVisible();
|
||||
|
||||
/**
|
||||
* Sets bossbar visibility for the player and performs all packet sending if new value is
|
||||
* different than previous value (true->false, false->true)
|
||||
* @param visible - whether bossbar should be visible or not
|
||||
* @param sendToggleMessage - if toggle message should be sent or not
|
||||
*/
|
||||
public void setBossbarVisible(boolean visible, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Returns list of all bossbars player can currently see
|
||||
* @return list of all bossbars player can see
|
||||
*/
|
||||
public Set<BossBar> getActiveBossBars();
|
||||
|
||||
/**
|
||||
* Sets property with specified name to new value. If property did no exist before, it is
|
||||
* created. If it existed, it is overridden
|
||||
* @param identifier - property name
|
||||
* @param rawValue - new raw value
|
||||
*/
|
||||
public void setProperty(String identifier, String rawValue);
|
||||
|
||||
/**
|
||||
* Loads property from config using standard property loading algorithm
|
||||
* @param property - property name to load
|
||||
*/
|
||||
public void loadPropertyFromConfig(String property);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void loadPropertyFromConfig(String property, String ifNotSet);
|
||||
|
||||
/**
|
||||
* Returns name of player's scoreboard team or null if nametag feature is disabled
|
||||
* @return name of player's team
|
||||
*/
|
||||
public String getTeamName();
|
||||
|
||||
/**
|
||||
* Returns user-friendly explanation of team name
|
||||
* @return explanation behind team name
|
||||
*/
|
||||
public String getTeamNameNote();
|
||||
|
||||
/**
|
||||
* Forces collision rule for the player. Setting it to null will remove forced value
|
||||
* @param collision - forced collision rule
|
||||
*/
|
||||
public void setCollisionRule(Boolean collision);
|
||||
|
||||
/**
|
||||
* Returns forced collision rule or null if collision is not forced using setCollisionRule
|
||||
* @return forced value or null if not forced
|
||||
*/
|
||||
public Boolean getCollisionRule();
|
||||
|
||||
/**
|
||||
* Returns true if player is disguised using iDisguide or LibsDisguises on bukkit, LibsDisguises
|
||||
* only on bungeecord via bukkit bridge
|
||||
* @return true if player is disguised, false if not
|
||||
*/
|
||||
public boolean isDisguised();
|
||||
|
||||
/**
|
||||
* Sets scoreboard visibility and sends packets if value changed. Sends toggle message configured
|
||||
* in premiumconfig if value changed and sendToggleMessage is true
|
||||
* @param visible - new visibility value
|
||||
* @param sendToggleMessage - whether toggle message should be sent or not
|
||||
*/
|
||||
public void setScoreboardVisible(boolean visible, boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Toggles scoreboard visibility and sends toggle message if sendToggleMessage is true
|
||||
* @param sendToggleMessage - whether toggle message should be sent or not
|
||||
*/
|
||||
public void toggleScoreboard(boolean sendToggleMessage);
|
||||
|
||||
/**
|
||||
* Returns true if player can see scoreboard, false if toggled
|
||||
* @return true if visible, false if not
|
||||
*/
|
||||
public boolean isScoreboardVisible();
|
||||
|
||||
/**
|
||||
* Returns true if player has invisiblity potion, false if not. For bukkit, API is used, for bungeecord
|
||||
* bukkit bridge is used
|
||||
* @return true if has invisiblity potion, false if not
|
||||
*/
|
||||
public boolean hasInvisibilityPotion();
|
||||
|
||||
/**
|
||||
* Returns true if scoreboard is forced using API method, false if not
|
||||
* @return true if forced via API, false if not
|
||||
*/
|
||||
public boolean hasForcedScoreboard();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public boolean isVanished();
|
||||
|
||||
/**
|
||||
* Shows specificed bossbar to the player
|
||||
* @param bossbar - bossbar to show
|
||||
*/
|
||||
public void showBossBar(BossBar bossbar);
|
||||
|
||||
/**
|
||||
* Hides specified bossbar if previously shown using showBossBar method
|
||||
* @param bossbar - bossbar to hide back
|
||||
*/
|
||||
public void removeBossBar(BossBar bossbar);
|
||||
|
||||
/**
|
||||
* Internal method for bukkit bridge data collection (disguise status, invisiblity status)
|
||||
* @param attribute - attribute name
|
||||
* @param value - attribute value
|
||||
*/
|
||||
public void setAttribute(String attribute, String value);
|
||||
|
||||
/**
|
||||
* Returns true if player is online according to server
|
||||
* @return true if online, false if not
|
||||
*/
|
||||
public boolean isOnline();
|
||||
|
||||
/**
|
||||
* Returns player's armor stand manager if unlimited nametag mode is enabled, null if disabled
|
||||
* @return player's armor stand manager
|
||||
*/
|
||||
public ArmorStandManager getArmorStandManager();
|
||||
|
||||
/**
|
||||
* Sets player's armor stands manager to new instance
|
||||
* @param armorStandManager - new instance
|
||||
*/
|
||||
public void setArmorStandManager(ArmorStandManager armorStandManager);
|
||||
|
||||
/**
|
||||
* Internal method that returns player's active scoreboard
|
||||
* @return player's active scoreboard
|
||||
*/
|
||||
public Scoreboard getActiveScoreboard();
|
||||
|
||||
/**
|
||||
* 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 name - forced team name
|
||||
*/
|
||||
public void forceTeamName(String name);
|
||||
|
||||
/**
|
||||
* Returns forced team name or null if not forced
|
||||
* @return forced team name or null if not forced
|
||||
*/
|
||||
public String getForcedTeamName();
|
||||
|
||||
/**
|
||||
* Returns gamemode of the player (0 for survival, 1 creative, 2 adventure, 3 spectator)
|
||||
* @return gamemode of the player
|
||||
*/
|
||||
public int getGamemode();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void pauseTeamHandling();
|
||||
|
||||
/**
|
||||
* Resumes team handling if it was before paused using pauseTeamHandling(), if not, nothing happens
|
||||
*/
|
||||
public void resumeTeamHandling();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public boolean hasTeamHandlingPaused();
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,87 @@
|
||||
package me.neznamy.tab.api.bossbar;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface BossBar {
|
||||
|
||||
/**
|
||||
* Returns name of this bossbar
|
||||
* @return name of bossbar
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Returns uuid of this bossbar
|
||||
* @return uuid of this bossbar
|
||||
*/
|
||||
public UUID getUniqueId();
|
||||
|
||||
/**
|
||||
* Sets bossbar title to specified string, supporting placeholders
|
||||
* @param title - new title
|
||||
*/
|
||||
public void setTitle(String title);
|
||||
|
||||
/**
|
||||
* Sets progress to specified string with placeholder support
|
||||
* @param progress - progress placeholder
|
||||
*/
|
||||
public void setProgress(String progress);
|
||||
|
||||
/**
|
||||
* Sets progress to specified value
|
||||
* @param progress - new progress
|
||||
*/
|
||||
public void setProgress(float progress);
|
||||
|
||||
/**
|
||||
* Sets color to specified value supporting placeholders
|
||||
* @param color - color placeholder / value
|
||||
*/
|
||||
public void setColor(String color);
|
||||
|
||||
/**
|
||||
* Sets color to specified value
|
||||
* @param color - new color
|
||||
*/
|
||||
public void setColor(BarColor color);
|
||||
|
||||
/**
|
||||
* Sets style to specified value supporting placeholders
|
||||
* @param style - style placeholder / value
|
||||
*/
|
||||
public void setStyle(String style);
|
||||
|
||||
/**
|
||||
* Sets style to specified value
|
||||
* @param style - new style
|
||||
*/
|
||||
public void setStyle(BarStyle style);
|
||||
|
||||
/**
|
||||
* Returns title of the bossbar
|
||||
* @return title of the bossbar
|
||||
*/
|
||||
public String getTitle();
|
||||
|
||||
/**
|
||||
* Returns progress of the bossbar as string, which is either entered string
|
||||
* containing placeholders or entered number converted to string
|
||||
* @return entered progress as string
|
||||
*/
|
||||
public String getProgress();
|
||||
|
||||
/**
|
||||
* Returns color of the bossbar as string, which is either entered string
|
||||
* containing placeholders or entered enum value converted to string
|
||||
* @return entered color as string
|
||||
*/
|
||||
public String getColor();
|
||||
|
||||
/**
|
||||
* Returns style of the bossbar as string, which is either entered string
|
||||
* containing placeholders or entered enum value converted to string
|
||||
* @return entered style as string
|
||||
*/
|
||||
public String getStyle();
|
||||
}
|
||||
+4
@@ -3,6 +3,10 @@ package me.neznamy.tab.api.event;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Bukkit event that is called each time TAB fully loads. This includes server startup, reload,
|
||||
* /tab reload and /plugman reload tab
|
||||
*/
|
||||
public class BukkitTABLoadEvent extends Event {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
@@ -0,0 +1,11 @@
|
||||
package me.neznamy.tab.api.event;
|
||||
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Bungee event that is called each time TAB fully loads. This includes server startup, reload,
|
||||
* /tab reload and /plugman reload tab
|
||||
*/
|
||||
public class BungeeTABLoadEvent extends Event {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package me.neznamy.tab.api.event;
|
||||
|
||||
/**
|
||||
* Velocity event that is called each time TAB fully loads. This includes server startup, reload,
|
||||
* /tab reload and /plugman reload tab
|
||||
*/
|
||||
public class VelocityTABLoadEvent {
|
||||
|
||||
}
|
||||
+24
-17
@@ -10,6 +10,7 @@ import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import me.neznamy.tab.api.bossbar.BarColor;
|
||||
@@ -37,6 +38,10 @@ public class ErrorManager {
|
||||
//plugin instance
|
||||
private TAB tab;
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
public ErrorManager(TAB tab) {
|
||||
this.tab = tab;
|
||||
errorLog = new File(tab.getPlatform().getDataFolder(), "errors.log");
|
||||
@@ -55,16 +60,6 @@ public class ErrorManager {
|
||||
printError(message, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an error message into errors.txt file and returns first argument
|
||||
* @param defaultValue - value to return
|
||||
* @param message - message to print
|
||||
* @return first argument
|
||||
*/
|
||||
public <T> T printError(T defaultValue, String message) {
|
||||
return printError(defaultValue, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an error message and stack trace into errors.txt file and returns first argument
|
||||
* @param defaultValue - value to return
|
||||
@@ -113,12 +108,12 @@ public class ErrorManager {
|
||||
if (file.length() > 1000000) return; //not going over 1 MB
|
||||
BufferedWriter buf = new BufferedWriter(new FileWriter(file, true));
|
||||
if (message != null) {
|
||||
write(buf, "&c[TAB v" + tab.getPluginVersion() + (tab.isPremium() ? " Premium": "") + "] " + message, intoConsoleToo);
|
||||
write(buf, "&c[TAB v" + tab.getPluginVersion() + (tab.isPremium() ? " Premium": "") + "] ", message.replace('\u00a7', '&'), intoConsoleToo);
|
||||
}
|
||||
if (error != null) {
|
||||
write(buf, "&c" + error.getClass().getName() + ": " + error.getMessage(), intoConsoleToo);
|
||||
write(buf, "&c", error.getClass().getName() + ": " + error.getMessage(), intoConsoleToo);
|
||||
for (StackTraceElement ste : error.getStackTrace()) {
|
||||
write(buf, "&c at " + ste.toString(), intoConsoleToo);
|
||||
write(buf, "&c at ", ste.toString(), intoConsoleToo);
|
||||
}
|
||||
}
|
||||
buf.close();
|
||||
@@ -154,11 +149,16 @@ public class ErrorManager {
|
||||
* @param forceConsole - send into console even without debug mode
|
||||
* @throws IOException - if IO writer operation fails
|
||||
*/
|
||||
private void write(BufferedWriter buf, String message, boolean forceConsole) throws IOException {
|
||||
buf.write(getCurrentTime() + removeColors(message) + System.getProperty("line.separator"));
|
||||
if (tab.debugMode || forceConsole) tab.getPlatform().sendConsoleMessage(message, true);
|
||||
private void write(BufferedWriter buf, String prefix, String message, boolean forceConsole) throws IOException {
|
||||
buf.write(getCurrentTime() + removeColors(prefix) + message + System.getProperty("line.separator"));
|
||||
if (tab.debugMode || forceConsole) tab.getPlatform().sendConsoleMessage(prefix.replace('&', '\u00a7') + message, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all color codes from defined text
|
||||
* @param text - text to remove colors from
|
||||
* @return text without colors
|
||||
*/
|
||||
private String removeColors(String text) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i=0; i<text.length(); i++) {
|
||||
@@ -257,6 +257,7 @@ public class ErrorManager {
|
||||
* @return parsed double
|
||||
*/
|
||||
public double parseDouble(String string, double defaultValue, String place) {
|
||||
if (string == null || string.length() == 0) return 0; //preventing error message on bungee with papi placeholders due to them not being initialized yet
|
||||
try {
|
||||
return Double.parseDouble(string);
|
||||
} catch (Throwable e) {
|
||||
@@ -330,6 +331,12 @@ public class ErrorManager {
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes interval divisible by 50 and sends error message if it was not already or was 0 or less
|
||||
* @param identifier - placeholder identifier
|
||||
* @param interval - configured interval
|
||||
* @return fixed interval
|
||||
*/
|
||||
public int fixPlaceholderInterval(String identifier, int interval) {
|
||||
if (interval == 0) {
|
||||
startupWarn("Placeholder \"&e" + identifier + "&c\" has refresh interval of 0 milliseconds! Is that misconfiguration? &bUsing 100.");
|
||||
@@ -404,7 +411,7 @@ public class ErrorManager {
|
||||
*/
|
||||
public SimpleDateFormat createDateFormat(String value, String defaultValue) {
|
||||
try {
|
||||
return new SimpleDateFormat(value);
|
||||
return new SimpleDateFormat(value, Locale.ENGLISH);
|
||||
} catch (Exception e) {
|
||||
startupWarn("Format \"" + value + "\" is not a valid date/time format. Did you try to use color codes?");
|
||||
return new SimpleDateFormat(defaultValue);
|
||||
+51
-62
@@ -2,9 +2,11 @@ package me.neznamy.tab.shared;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
@@ -13,21 +15,16 @@ import me.neznamy.tab.shared.features.NameTag;
|
||||
import me.neznamy.tab.shared.features.types.Feature;
|
||||
import me.neznamy.tab.shared.features.types.Loadable;
|
||||
import me.neznamy.tab.shared.features.types.Refreshable;
|
||||
import me.neznamy.tab.shared.features.types.event.ChatEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.CommandListener;
|
||||
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.RespawnEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.SneakEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.DisplayObjectivePacketListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.HeaderFooterPacketListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.LoginPacketListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.ObjectivePacketListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.PlayerInfoPacketListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.RawPacketListener;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerListHeaderFooter;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardDisplayObjective;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective;
|
||||
|
||||
@@ -39,8 +36,13 @@ public class FeatureManager {
|
||||
//list of registered features
|
||||
private Map<String, Feature> features = new LinkedHashMap<String, Feature>();
|
||||
|
||||
//tab instance
|
||||
private TAB tab;
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
public FeatureManager(TAB tab) {
|
||||
this.tab = tab;
|
||||
}
|
||||
@@ -55,6 +57,11 @@ public class FeatureManager {
|
||||
features.put(featureName, featureHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters feature making it no longer receive events. This does not run unload method nor cancel
|
||||
* tasks created by the feature
|
||||
* @param featureName
|
||||
*/
|
||||
public void unregisterFeature(String featureName) {
|
||||
features.remove(featureName);
|
||||
}
|
||||
@@ -84,7 +91,11 @@ public class FeatureManager {
|
||||
* @return list of all loaded features
|
||||
*/
|
||||
public Collection<Feature> getAllFeatures(){
|
||||
return features.values();
|
||||
try {
|
||||
return features.values();
|
||||
} catch (ConcurrentModificationException e) {
|
||||
return getAllFeatures();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +153,7 @@ public class FeatureManager {
|
||||
* @throws Exception
|
||||
*/
|
||||
public Object onPacketPlayOutPlayerInfo(TabPlayer receiver, Object packet) throws Exception {
|
||||
if (receiver.getVersion().getMinorVersion() < 8) return packet;
|
||||
List<PlayerInfoPacketListener> listeners = new ArrayList<PlayerInfoPacketListener>();
|
||||
for (Feature f : getAllFeatures()) {
|
||||
if (f instanceof PlayerInfoPacketListener) listeners.add((PlayerInfoPacketListener) f);
|
||||
@@ -154,8 +166,8 @@ public class FeatureManager {
|
||||
tab.getCPUManager().addTime(TabFeature.PACKET_DESERIALIZING, UsageType.PACKET_PLAYER_INFO, System.nanoTime()-time);
|
||||
for (PlayerInfoPacketListener f : listeners) {
|
||||
time = System.nanoTime();
|
||||
((PlayerInfoPacketListener)f).onPacketSend(receiver, info);
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PACKET_READING, System.nanoTime()-time);
|
||||
f.onPacketSend(receiver, info);
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PACKET_READING_OUT, System.nanoTime()-time);
|
||||
}
|
||||
time = System.nanoTime();
|
||||
Object pack = info.create(receiver.getVersion());
|
||||
@@ -185,6 +197,11 @@ public class FeatureManager {
|
||||
* @param connectedPlayer - player who connected
|
||||
*/
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
if (!connectedPlayer.isOnline()) {
|
||||
tab.debug("Player " + connectedPlayer.getName() + " was offline during login process.");
|
||||
return;
|
||||
}
|
||||
long millis = System.currentTimeMillis();
|
||||
tab.addPlayer(connectedPlayer);
|
||||
for (Feature f : getAllFeatures()) {
|
||||
if (!(f instanceof JoinEventListener)) continue;
|
||||
@@ -192,7 +209,8 @@ public class FeatureManager {
|
||||
((JoinEventListener)f).onJoin(connectedPlayer);
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PLAYER_JOIN_EVENT, System.nanoTime()-time);
|
||||
}
|
||||
connectedPlayer.markAsLoaded();
|
||||
((ITabPlayer)connectedPlayer).markAsLoaded();
|
||||
tab.debug("Player join of " + connectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,10 +220,14 @@ public class FeatureManager {
|
||||
* @param from - name of the previous world/server
|
||||
* @param to - name of the new world/server
|
||||
*/
|
||||
public void onWorldChange(TabPlayer changed, String to) {
|
||||
if (changed == null || !changed.isLoaded()) return;
|
||||
public void onWorldChange(UUID playerUUID, String to) {
|
||||
TabPlayer changed = TAB.getInstance().getPlayer(playerUUID);
|
||||
if (changed == null || !changed.isLoaded()) {
|
||||
tab.getCPUManager().runTaskLater(100, "processing delayed world/server switch", TabFeature.OTHER, UsageType.WORLD_SWITCH_EVENT, () -> onWorldChange(playerUUID, to));
|
||||
return;
|
||||
}
|
||||
String from = changed.getWorldName();
|
||||
changed.setWorldName(to);
|
||||
((ITabPlayer)changed).setWorldName(to);
|
||||
for (Feature f : getAllFeatures()) {
|
||||
if (!(f instanceof WorldChangeListener)) continue;
|
||||
long time = System.nanoTime();
|
||||
@@ -250,7 +272,7 @@ public class FeatureManager {
|
||||
} catch (Throwable e) {
|
||||
tab.getErrorManager().printError("Feature " + f.getFeatureType() + " failed to read packet", e);
|
||||
}
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PACKET_READING, System.nanoTime()-time);
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PACKET_READING_IN, System.nanoTime()-time);
|
||||
}
|
||||
return newPacket;
|
||||
}
|
||||
@@ -270,21 +292,7 @@ public class FeatureManager {
|
||||
} catch (Throwable e) {
|
||||
tab.getErrorManager().printError("Feature " + f.getFeatureType() + " failed to read packet", e);
|
||||
}
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PACKET_READING, System.nanoTime()-time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onRespawn on all featurs that implement RespawnEventListener and measures how long it took them to process
|
||||
* @param respawned - player who respawned
|
||||
*/
|
||||
public void onRespawn(TabPlayer respawned) {
|
||||
if (respawned == null) return;
|
||||
for (Feature f : getAllFeatures()) {
|
||||
if (!(f instanceof RespawnEventListener)) continue;
|
||||
long time = System.nanoTime();
|
||||
((RespawnEventListener)f).onRespawn(respawned);
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PLAYER_RESPAWN_EVENT, System.nanoTime()-time);
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PACKET_READING_OUT, System.nanoTime()-time);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,6 +309,13 @@ public class FeatureManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onPacketSend on all featurs that implement DisplayObjectivePacketListener and measures how long it took them to process
|
||||
* @param packetReceiver - player who received the packet
|
||||
* @param packet - the packet
|
||||
* @return true if packet should be cancelled, false if not
|
||||
* @throws Exception - if something fails
|
||||
*/
|
||||
public boolean onDisplayObjective(TabPlayer packetReceiver, Object packet) throws Exception {
|
||||
long time = System.nanoTime();
|
||||
PacketPlayOutScoreboardDisplayObjective display = tab.getPacketBuilder().readDisplayObjective(packet, packetReceiver.getVersion());
|
||||
@@ -315,6 +330,10 @@ public class FeatureManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onObjective on all featurs that implement ObjectivePacketListener and measures how long it took them to process
|
||||
* @param packetReceiver - player who received the packet
|
||||
*/
|
||||
public void onObjective(TabPlayer packetReceiver, Object packet) throws Exception {
|
||||
long time = System.nanoTime();
|
||||
PacketPlayOutScoreboardObjective display = tab.getPacketBuilder().readObjective(packet, packetReceiver.getVersion());
|
||||
@@ -327,40 +346,10 @@ public class FeatureManager {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onHeaderFooter(TabPlayer packetReceiver, Object packet) throws Exception {
|
||||
long time = System.nanoTime();
|
||||
PacketPlayOutPlayerListHeaderFooter display = tab.getPacketBuilder().readHeaderFooter(packet, packetReceiver.getVersion());
|
||||
tab.getCPUManager().addTime(TabFeature.PACKET_DESERIALIZING, UsageType.PACKET_HEADER_FOOTER, System.nanoTime()-time);
|
||||
for (Feature f : getAllFeatures()) {
|
||||
if (!(f instanceof HeaderFooterPacketListener)) continue;
|
||||
time = System.nanoTime();
|
||||
boolean cancel = ((HeaderFooterPacketListener)f).onPacketSend(packetReceiver, display);
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
if (cancel) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onChat(TabPlayer sender, String message, boolean cancelled) {
|
||||
boolean cancel = false;
|
||||
for (Feature f : getAllFeatures()) {
|
||||
if (!(f instanceof ChatEventListener)) continue;
|
||||
long time = System.nanoTime();
|
||||
if (((ChatEventListener)f).onChat(sender, message, cancelled)) cancel = true;
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PLAYER_CHAT_EVENT, System.nanoTime()-time);
|
||||
}
|
||||
return cancel;
|
||||
}
|
||||
|
||||
public void onSneak(TabPlayer sender, boolean isSneaking) {
|
||||
for (Feature f : getAllFeatures()) {
|
||||
if (!(f instanceof SneakEventListener)) continue;
|
||||
long time = System.nanoTime();
|
||||
((SneakEventListener)f).onSneak(sender, isSneaking);
|
||||
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PLAYER_TOGGLE_SNEAK_EVENT, System.nanoTime()-time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns currently loaded nametag feature or null if disabled
|
||||
* @return currently loaded nametag feature
|
||||
*/
|
||||
public NameTag getNameTagFeature() {
|
||||
if (isFeatureEnabled("nametag16")) return (NameTag) getFeature("nametag16");
|
||||
return (NameTag) getFeature("nametagx");
|
||||
+127
-164
@@ -1,6 +1,6 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -16,11 +16,11 @@ import me.neznamy.tab.api.bossbar.BossBar;
|
||||
import me.neznamy.tab.shared.command.level1.PlayerCommand;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.features.GroupRefresher;
|
||||
import me.neznamy.tab.shared.features.NameTag;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarLine;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
|
||||
import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutChat;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardTeam;
|
||||
import me.neznamy.tab.shared.packets.UniversalPacketPlayOut;
|
||||
|
||||
/**
|
||||
@@ -34,6 +34,7 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
private String permissionGroup = "<null>";
|
||||
private String teamName;
|
||||
private String teamNameNote;
|
||||
private String forcedTeamName;
|
||||
|
||||
private Map<String, Property> properties = new HashMap<String, Property>();
|
||||
private ArmorStandManager armorStandManager;
|
||||
@@ -43,30 +44,34 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
|
||||
private boolean previewingNametag;
|
||||
private Set<BossBar> activeBossBars = new HashSet<BossBar>();
|
||||
private boolean collision;
|
||||
private Boolean forcedCollision;
|
||||
private boolean onJoinFinished;
|
||||
private boolean hiddenNametag;
|
||||
private Set<UUID> hiddenNametagFor = new HashSet<UUID>();
|
||||
private boolean onBoat;
|
||||
|
||||
private Scoreboard activeScoreboard;
|
||||
private boolean scoreboardVisible;
|
||||
private Scoreboard forcedScoreboard;
|
||||
private String otherPluginScoreboard;
|
||||
|
||||
private boolean teamHandlingPaused;
|
||||
|
||||
protected Map<String, String> attributes = new HashMap<String, String>();
|
||||
|
||||
public void init() {
|
||||
protected void init() {
|
||||
setGroup(((GroupRefresher)TAB.getInstance().getFeatureManager().getFeature("group")).detectPermissionGroup(this), false);
|
||||
}
|
||||
|
||||
public abstract boolean hasPermission(String permission);
|
||||
|
||||
public abstract long getPing();
|
||||
|
||||
public abstract void sendPacket(Object nmsPacket);
|
||||
|
||||
private void setProperty(String identifier, String rawValue, String source) {
|
||||
Property p = getProperty(identifier);
|
||||
if (p == null) {
|
||||
properties.put(identifier, new Property(this, rawValue, source));
|
||||
} else {
|
||||
p.changeRawValue(rawValue);
|
||||
p.setSource(source);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message, boolean translateColors) {
|
||||
if (message == null || message.length() == 0) return;
|
||||
IChatBaseComponent component;
|
||||
@@ -77,34 +82,12 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
}
|
||||
sendCustomPacket(new PacketPlayOutChat(component));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendMessage(IChatBaseComponent message) {
|
||||
sendCustomPacket(new PacketPlayOutChat(message));
|
||||
}
|
||||
|
||||
public abstract Object getSkin();
|
||||
|
||||
private boolean getTeamVisibility(TabPlayer viewer) {
|
||||
if (TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx") && !onBoat) return false;
|
||||
if (hiddenNametag || (boolean) TAB.getInstance().getConfiguration().getSecretOption("invisible-nametags", false) || hiddenNametagFor.contains(viewer.getUniqueId())) return false;
|
||||
return !TAB.getInstance().getFeatureManager().getNameTagFeature().getInvisiblePlayers().contains(getName());
|
||||
}
|
||||
|
||||
public void setProperty(String identifier, String rawValue, String source) {
|
||||
Property p = getProperty(identifier);
|
||||
if (p == null) {
|
||||
properties.put(identifier, new Property(this, rawValue, source));
|
||||
} else {
|
||||
p.changeRawValue(rawValue);
|
||||
p.setSource(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Implementing interface
|
||||
*/
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -114,7 +97,7 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
public UUID getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UUID getTablistUUID() {
|
||||
return uniqueId;
|
||||
@@ -122,19 +105,20 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
|
||||
@Override
|
||||
public void setValueTemporarily(EnumProperty type, String value) {
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
TAB.getInstance().debug("Received API request to set property " + type + " of " + getName() + " temporarily to " + value + " by " + Thread.currentThread().getStackTrace()[2].toString());
|
||||
Property pr = getProperty(type.toString());
|
||||
if (pr == null) throw new IllegalStateException("Feature handling this property is not enabled");
|
||||
if (pr == null) throw new IllegalStateException("Feature handling this property (" + type + ") is not enabled");
|
||||
pr.setTemporaryValue(value);
|
||||
if (TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx") && type.toString().contains("tag")) {
|
||||
setProperty("nametag",getProperty("tagprefix").getCurrentRawValue() + getProperty("customtagname").getCurrentRawValue() + getProperty("tagsuffix").getCurrentRawValue(), null);
|
||||
}
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValuePermanently(EnumProperty type, String value) {
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
TAB.getInstance().debug("Received API request to set property " + type + " of " + getName() + " permanently to " + value + " by " + Thread.currentThread().getStackTrace()[2].toString());
|
||||
((PlayerCommand)TAB.getInstance().command.subcommands.get("player")).savePlayer(null, getName(), type.toString(), value);
|
||||
if (TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx") && type.toString().contains("tag")) {
|
||||
setProperty("nametag", getProperty("tagprefix").getCurrentRawValue() + getProperty("customtagname").getCurrentRawValue() + getProperty("tagsuffix").getCurrentRawValue(), null);
|
||||
@@ -142,6 +126,7 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
Property pr = getProperty(type.toString());
|
||||
if (pr == null) return;
|
||||
pr.changeRawValue(value);
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
forceRefresh();
|
||||
}
|
||||
|
||||
@@ -167,14 +152,16 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
|
||||
@Override
|
||||
public void hideNametag() {
|
||||
if (TAB.getInstance().getFeatureManager().getNameTagFeature() == null) return;
|
||||
hiddenNametag = true;
|
||||
updateTeamData();
|
||||
TAB.getInstance().getFeatureManager().getNameTagFeature().updateTeamData(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showNametag() {
|
||||
if (TAB.getInstance().getFeatureManager().getNameTagFeature() == null) return;
|
||||
hiddenNametag = false;
|
||||
updateTeamData();
|
||||
TAB.getInstance().getFeatureManager().getNameTagFeature().updateTeamData(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -231,19 +218,21 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorldName(String name) {
|
||||
world = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCustomPacket(UniversalPacketPlayOut packet) {
|
||||
sendPacket(packet.create(getVersion()));
|
||||
Object p = packet.create(getVersion());
|
||||
long time = System.nanoTime();
|
||||
sendPacket(p);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("sendPacket", System.nanoTime()-time);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendCustomPacket(UniversalPacketPlayOut packet, TabFeature feature) {
|
||||
sendPacket(packet.create(getVersion()));
|
||||
sendCustomPacket(packet);
|
||||
TAB.getInstance().getCPUManager().packetSent(feature);
|
||||
}
|
||||
|
||||
@@ -285,79 +274,11 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam() {
|
||||
if (teamName == null) return;
|
||||
for (TabPlayer viewer : TAB.getInstance().getPlayers()) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName).setTeamOptions(69), TabFeature.NAMETAGS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTeam(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName).setTeamOptions(69), TabFeature.NAMETAGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerTeam() {
|
||||
Property tagprefix = getProperty("tagprefix");
|
||||
Property tagsuffix = getProperty("tagsuffix");
|
||||
for (TabPlayer viewer : TAB.getInstance().getPlayers()) {
|
||||
String currentPrefix = tagprefix.getFormat(viewer);
|
||||
String currentSuffix = tagsuffix.getFormat(viewer);
|
||||
PacketAPI.registerScoreboardTeam(viewer, teamName, currentPrefix, currentSuffix, getTeamVisibility(viewer), collision, Arrays.asList(getName()), null, TabFeature.NAMETAGS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerTeam(TabPlayer viewer) {
|
||||
Property tagprefix = getProperty("tagprefix");
|
||||
Property tagsuffix = getProperty("tagsuffix");
|
||||
String replacedPrefix = tagprefix.getFormat(viewer);
|
||||
String replacedSuffix = tagsuffix.getFormat(viewer);
|
||||
PacketAPI.registerScoreboardTeam(viewer, teamName, replacedPrefix, replacedSuffix, getTeamVisibility(viewer), collision, Arrays.asList(getName()), null, TabFeature.NAMETAGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam() {
|
||||
if (teamName == null) return; //player not loaded yet
|
||||
String newName = TAB.getInstance().getFeatureManager().getNameTagFeature().sorting.getTeamName(this);
|
||||
if (teamName.equals(newName)) {
|
||||
updateTeamData();
|
||||
} else {
|
||||
unregisterTeam();
|
||||
teamName = newName;
|
||||
registerTeam();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeamData() {
|
||||
Property tagprefix = getProperty("tagprefix");
|
||||
Property tagsuffix = getProperty("tagsuffix");
|
||||
for (TabPlayer viewer : TAB.getInstance().getPlayers()) {
|
||||
String currentPrefix = tagprefix.getFormat(viewer);
|
||||
String currentSuffix = tagsuffix.getFormat(viewer);
|
||||
boolean visible = getTeamVisibility(viewer);
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName, currentPrefix, currentSuffix, visible?"always":"never", collision?"always":"never", 69), TabFeature.NAMETAGS);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTeamData(TabPlayer viewer) {
|
||||
Property tagprefix = getProperty("tagprefix");
|
||||
Property tagsuffix = getProperty("tagsuffix");
|
||||
boolean visible = getTeamVisibility(viewer);
|
||||
String currentPrefix = tagprefix.getFormat(viewer);
|
||||
String currentSuffix = tagsuffix.getFormat(viewer);
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName, currentPrefix, currentSuffix, visible?"always":"never", collision?"always":"never", 69), TabFeature.NAMETAGS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return onJoinFinished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markAsLoaded() {
|
||||
onJoinFinished = true;
|
||||
}
|
||||
@@ -368,8 +289,28 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBossbarVisible(boolean visible) {
|
||||
public void setBossbarVisible(boolean visible, boolean sendToggleMessage) {
|
||||
bossbarVisible = visible;
|
||||
me.neznamy.tab.shared.features.bossbar.BossBar feature = (me.neznamy.tab.shared.features.bossbar.BossBar) TAB.getInstance().getFeatureManager().getFeature("bossbar");
|
||||
if (feature == null) return;
|
||||
if (hasBossbarVisible()) {
|
||||
feature.detectBossBarsAndSend(this);
|
||||
if (sendToggleMessage) sendMessage(TAB.getInstance().getConfiguration().translation.getString("bossbar-toggle-on"), true);
|
||||
if (feature.remember_toggle_choice) {
|
||||
feature.bossbar_off_players.remove(getName());
|
||||
TAB.getInstance().getConfiguration().playerdata.set("bossbar-off", feature.bossbar_off_players);
|
||||
}
|
||||
} else {
|
||||
for (BossBar line : getActiveBossBars().toArray(new BossBar[0])) {
|
||||
removeBossBar(line);
|
||||
}
|
||||
getActiveBossBars().clear();
|
||||
if (sendToggleMessage) sendMessage(TAB.getInstance().getConfiguration().translation.getString("bossbar-toggle-off"), true);
|
||||
if (feature.remember_toggle_choice && !feature.bossbar_off_players.contains(getName())) {
|
||||
feature.bossbar_off_players.add(getName());
|
||||
TAB.getInstance().getConfiguration().playerdata.set("bossbar-off", feature.bossbar_off_players);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -437,17 +378,16 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
this.armorStandManager = armorStandManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTeamName(String name) {
|
||||
teamName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTeamName() {
|
||||
if (forcedTeamName != null) return forcedTeamName;
|
||||
return teamName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTeamNameNote(String note) {
|
||||
teamNameNote = note;
|
||||
}
|
||||
@@ -467,31 +407,26 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
return forcedCollision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnBoat(boolean onBoat) {
|
||||
this.onBoat = onBoat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnBoat() {
|
||||
return onBoat;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setScoreboardVisible(boolean visible, boolean sendToggleMessage) {
|
||||
if (scoreboardVisible == visible) return;
|
||||
scoreboardVisible = visible;
|
||||
ScoreboardManager scoreboardManager = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
|
||||
if (scoreboardManager == null) throw new IllegalStateException("Scoreboard feature is not enabled");
|
||||
if (scoreboardManager == null) return;
|
||||
if (visible) {
|
||||
scoreboardManager.sendHighestScoreboard(this);
|
||||
if (sendToggleMessage) {
|
||||
sendMessage(scoreboardManager.scoreboard_on, true);
|
||||
}
|
||||
if (scoreboardManager.remember_toggle_choice) {
|
||||
scoreboardManager.sb_off_players.remove(getName());
|
||||
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", scoreboardManager.sb_off_players);
|
||||
if (scoreboardManager.hiddenByDefault) {
|
||||
scoreboardManager.sb_off_players.add(getName());
|
||||
} else {
|
||||
scoreboardManager.sb_off_players.remove(getName());
|
||||
}
|
||||
synchronized (scoreboardManager.sb_off_players){
|
||||
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", new ArrayList<>(scoreboardManager.sb_off_players));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scoreboardManager.unregisterScoreboard(this, true);
|
||||
@@ -499,8 +434,14 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
sendMessage(scoreboardManager.scoreboard_off, true);
|
||||
}
|
||||
if (scoreboardManager.remember_toggle_choice) {
|
||||
scoreboardManager.sb_off_players.add(getName());
|
||||
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", scoreboardManager.sb_off_players);
|
||||
if (scoreboardManager.hiddenByDefault) {
|
||||
scoreboardManager.sb_off_players.remove(getName());
|
||||
} else {
|
||||
scoreboardManager.sb_off_players.add(getName());
|
||||
}
|
||||
synchronized (scoreboardManager.sb_off_players){
|
||||
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", new ArrayList<>(scoreboardManager.sb_off_players));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,7 +456,6 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
return scoreboardVisible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveScoreboard(Scoreboard board) {
|
||||
activeScoreboard = board;
|
||||
}
|
||||
@@ -525,7 +465,6 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
return activeScoreboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGroup(String permissionGroup, boolean refreshIfChanged) {
|
||||
if (this.permissionGroup.equals(permissionGroup)) return;
|
||||
if (permissionGroup != null) {
|
||||
@@ -544,11 +483,6 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
return forcedScoreboard != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showBossBar(BossBar bossbar) {
|
||||
BossBarLine line = (BossBarLine) bossbar;
|
||||
@@ -563,58 +497,87 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
activeBossBars.remove(line);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideNametag(UUID viewer) {
|
||||
if (TAB.getInstance().getFeatureManager().getNameTagFeature() == null) return;
|
||||
if (hiddenNametagFor.add(viewer)) {
|
||||
updateTeamData(TAB.getInstance().getPlayer(viewer));
|
||||
TAB.getInstance().getFeatureManager().getNameTagFeature().updateTeamData(this, TAB.getInstance().getPlayer(viewer));
|
||||
if (armorStandManager != null) armorStandManager.updateVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showNametag(UUID viewer) {
|
||||
if (TAB.getInstance().getFeatureManager().getNameTagFeature() == null) return;
|
||||
if (hiddenNametagFor.remove(viewer)) {
|
||||
updateTeamData(TAB.getInstance().getPlayer(viewer));
|
||||
TAB.getInstance().getFeatureManager().getNameTagFeature().updateTeamData(this, TAB.getInstance().getPlayer(viewer));
|
||||
if (armorStandManager != null) armorStandManager.updateVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasHiddenNametag(UUID viewer) {
|
||||
return hiddenNametagFor.contains(viewer);
|
||||
}
|
||||
|
||||
public void updateCollision() {
|
||||
if (forcedCollision != null) {
|
||||
if (collision != forcedCollision) {
|
||||
collision = forcedCollision;
|
||||
updateTeamData();
|
||||
}
|
||||
} else {
|
||||
boolean collision = !isDisguised() && TAB.getInstance().getConfiguration().revertedCollision.contains(world) ? !TAB.getInstance().getConfiguration().collisionRule : TAB.getInstance().getConfiguration().collisionRule;
|
||||
if (this.collision != collision) {
|
||||
this.collision = collision;
|
||||
updateTeamData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attribute, String value) {
|
||||
attributes.put(attribute, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void setOtherPluginScoreboard(String objectiveName) {
|
||||
this.otherPluginScoreboard = objectiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public String getOtherPluginScoreboard() {
|
||||
return otherPluginScoreboard;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendPacket(Object nmsPacket, TabFeature feature) {
|
||||
sendPacket(nmsPacket);
|
||||
TAB.getInstance().getCPUManager().packetSent(feature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceTeamName(String name) {
|
||||
if (String.valueOf(forcedTeamName).equals(name)) return;
|
||||
if (name != null && name.length() > 16) throw new IllegalArgumentException("Team name cannot be more than 16 characters long.");
|
||||
NameTag nametags = TAB.getInstance().getFeatureManager().getNameTagFeature();
|
||||
if (nametags == null) return;
|
||||
nametags.unregisterTeam(this);
|
||||
forcedTeamName = name;
|
||||
nametags.registerTeam(this);
|
||||
if (name != null) teamNameNote = "Set using API";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForcedTeamName() {
|
||||
return forcedTeamName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseTeamHandling() {
|
||||
if (teamHandlingPaused) return;
|
||||
NameTag f = TAB.getInstance().getFeatureManager().getNameTagFeature();
|
||||
if (f != null && !f.isDisabledWorld(getWorldName())) {
|
||||
f.unregisterTeam(this);
|
||||
}
|
||||
teamHandlingPaused = true; //setting to true after, so unregisterTeam method runs
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeTeamHandling() {
|
||||
if (!teamHandlingPaused) return;
|
||||
teamHandlingPaused = false; //setting to false before, so registerTeam method runs
|
||||
NameTag f = TAB.getInstance().getFeatureManager().getNameTagFeature();
|
||||
if (f != null && !f.isDisabledWorld(getWorldName())) {
|
||||
f.registerTeam(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTeamHandlingPaused() {
|
||||
return teamHandlingPaused;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -27,7 +27,7 @@ public class PacketAPI {
|
||||
* @param color - color field (1.13+)
|
||||
*/
|
||||
public static synchronized void registerScoreboardTeam(TabPlayer to, String teamName, String prefix, String suffix, boolean enumNameTagVisibility, boolean enumTeamPush, Collection<String> players, EnumChatFormat color, TabFeature feature) {
|
||||
if (to.getVersion().getMinorVersion() >= 8 && (boolean) TAB.getInstance().getConfiguration().getSecretOption("unregister-before-register", true) && TAB.getInstance().getPlatform().getSeparatorType().equals("world")) {
|
||||
if (to.getVersion().getMinorVersion() >= 8 && TAB.getInstance().getConfiguration().unregisterBeforeRegister && TAB.getInstance().getPlatform().getSeparatorType().equals("world")) {
|
||||
to.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName).setTeamOptions(69), feature);
|
||||
}
|
||||
to.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName, prefix, suffix, enumNameTagVisibility?"always":"never", enumTeamPush?"always":"never", players, 69).setColor(color), feature);
|
||||
@@ -42,7 +42,7 @@ public class PacketAPI {
|
||||
* @param displayType - display type of the value (only supported in Playerlist)
|
||||
*/
|
||||
public static synchronized void registerScoreboardObjective(TabPlayer to, String objectiveName, String title, int position, EnumScoreboardHealthDisplay displayType, TabFeature feature) {
|
||||
if (to.getVersion().getMinorVersion() >= 8 && (boolean) TAB.getInstance().getConfiguration().getSecretOption("unregister-before-register", true) && TAB.getInstance().getPlatform().getSeparatorType().equals("world")) {
|
||||
if (to.getVersion().getMinorVersion() >= 8 && TAB.getInstance().getConfiguration().unregisterBeforeRegister && TAB.getInstance().getPlatform().getSeparatorType().equals("world")) {
|
||||
to.sendCustomPacket(new PacketPlayOutScoreboardObjective(objectiveName), feature);
|
||||
}
|
||||
to.sendCustomPacket(new PacketPlayOutScoreboardObjective(0, objectiveName, title, displayType), feature);
|
||||
+12
-97
@@ -1,25 +1,19 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.config.ConfigurationFile;
|
||||
import me.neznamy.tab.shared.features.AlignedSuffix;
|
||||
import me.neznamy.tab.shared.features.BelowName;
|
||||
import me.neznamy.tab.shared.features.GhostPlayerFix;
|
||||
import me.neznamy.tab.shared.features.GroupRefresher;
|
||||
import me.neznamy.tab.shared.features.HeaderFooter;
|
||||
import me.neznamy.tab.shared.features.Playerlist;
|
||||
import me.neznamy.tab.shared.features.PluginInfo;
|
||||
import me.neznamy.tab.shared.features.SpectatorFix;
|
||||
import me.neznamy.tab.shared.features.TabObjective;
|
||||
import me.neznamy.tab.shared.features.UpdateChecker;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBar;
|
||||
import me.neznamy.tab.shared.features.layout.Layout;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
|
||||
import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
import me.neznamy.tab.shared.placeholders.Placeholder;
|
||||
import me.neznamy.tab.shared.placeholders.PlayerPlaceholder;
|
||||
@@ -55,12 +49,6 @@ public interface Platform {
|
||||
*/
|
||||
public void registerUnknownPlaceholder(String identifier);
|
||||
|
||||
/**
|
||||
* Converts configuration file into the latest version by removing old options, adding new ones or renaming
|
||||
* @param config - the configuration file to be converted
|
||||
*/
|
||||
public void convertConfig(ConfigurationFile config);
|
||||
|
||||
/**
|
||||
* Returns server's version
|
||||
* @return server's version
|
||||
@@ -91,6 +79,12 @@ public interface Platform {
|
||||
*/
|
||||
public int getMaxPlayers();
|
||||
|
||||
/**
|
||||
* Returns name of config file in the jar file on specific platform
|
||||
* @return name of config file of the platform
|
||||
*/
|
||||
public String getConfigName();
|
||||
|
||||
/**
|
||||
* Removes an old config option that is not present anymore
|
||||
* @param config - configuration file
|
||||
@@ -142,83 +136,10 @@ public interface Platform {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts premiumconfig variables to latest version
|
||||
* @param config - the file
|
||||
* Converts configuration options same on all platforms
|
||||
* @param file - file to convert
|
||||
*/
|
||||
public default void convertPremiumConfig(ConfigurationFile config) {
|
||||
removeOld(config, "scoreboard.refresh-interval-ticks");
|
||||
if (!config.hasConfigOption("placeholder-output-replacements")) {
|
||||
Map<String, Map<String, String>> replacements = new HashMap<String, Map<String, String>>();
|
||||
Map<String, String> essVanished = new HashMap<String, String>();
|
||||
essVanished.put("Yes", "&7| Vanished");
|
||||
essVanished.put("No", "");
|
||||
replacements.put("%essentials_vanished%", essVanished);
|
||||
Map<String, String> tps = new HashMap<String, String>();
|
||||
tps.put("20", "&aPerfect");
|
||||
replacements.put("%tps%", tps);
|
||||
config.set("placeholder-output-replacements", replacements);
|
||||
TAB.getInstance().print('2', "Added new missing \"placeholder-output-replacements\" premiumconfig.yml section.");
|
||||
}
|
||||
boolean scoreboardsConverted = false;
|
||||
for (Object scoreboard : config.getConfigurationSection("scoreboards").keySet()) {
|
||||
Boolean permReq = config.getBoolean("scoreboards." + scoreboard + ".permission-required");
|
||||
if (permReq != null) {
|
||||
if (permReq) {
|
||||
config.set("scoreboards." + scoreboard + ".display-condition", "permission:tab.scoreboard." + scoreboard);
|
||||
}
|
||||
config.set("scoreboards." + scoreboard + ".permission-required", null);
|
||||
scoreboardsConverted = true;
|
||||
}
|
||||
String childBoard = config.getString("scoreboards." + scoreboard + ".if-permission-missing");
|
||||
if (childBoard != null) {
|
||||
config.set("scoreboards." + scoreboard + ".if-permission-missing", null);
|
||||
config.set("scoreboards." + scoreboard + ".if-condition-not-met", childBoard);
|
||||
scoreboardsConverted = true;
|
||||
}
|
||||
}
|
||||
if (scoreboardsConverted) {
|
||||
TAB.getInstance().print('2', "Converted old premiumconfig.yml scoreboard display condition system to new one.");
|
||||
}
|
||||
removeOld(config, "scoreboard.refresh-interval-milliseconds");
|
||||
rename(config, "allign-tabsuffix-on-the-right", "align-tabsuffix-on-the-right");
|
||||
}
|
||||
|
||||
public default void convertUniversalOptions(ConfigurationFile file) {
|
||||
if (file.getName().equals("config.yml")) {
|
||||
removeOld(file, "nametag-refresh-interval-milliseconds");
|
||||
removeOld(file, "tablist-refresh-interval-milliseconds");
|
||||
removeOld(file, "header-footer-refresh-interval-milliseconds");
|
||||
removeOld(file, "classic-vanilla-belowname.refresh-interval-milliseconds");
|
||||
rename(file, "belowname", "classic-vanilla-belowname");
|
||||
rename(file, "papi-placeholder-cooldowns", "placeholderapi-refresh-intervals");
|
||||
rename(file, "safe-team-register", "unregister-before-register");
|
||||
rename(file, "disable-features-in-worlds.tablist-objective", "disable-features-in-worlds.yellow-number");
|
||||
rename(file, "ntx-space", "unlimited-nametag-prefix-suffix-mode.space-between-lines");
|
||||
if (!file.hasConfigOption("placeholderapi-refresh-intervals")) {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
map.put("default-refresh-interval", 100);
|
||||
Map<String, Integer> server = new HashMap<String, Integer>();
|
||||
server.put("%server_uptime%", 1000);
|
||||
server.put("%server_tps_1_colored%", 1000);
|
||||
map.put("server", server);
|
||||
Map<String, Integer> player = new HashMap<String, Integer>();
|
||||
player.put("%player_health%", 200);
|
||||
player.put("%player_ping%", 1000);
|
||||
player.put("%vault_prefix%", 1000);
|
||||
map.put("player", player);
|
||||
Map<String, Integer> relational = new HashMap<String, Integer>();
|
||||
relational.put("%rel_factionsuuid_relation_color%", 500);
|
||||
map.put("relational", relational);
|
||||
file.set("placeholderapi-refresh-intervals", map);
|
||||
TAB.getInstance().print('2', "Added new missing \"placeholderapi-refresh-intervals\" config.yml section.");
|
||||
}
|
||||
}
|
||||
if (file.getName().equals("premiumconfig.yml")) {
|
||||
convertPremiumConfig(file);
|
||||
}
|
||||
if (file.getName().equals("bossbar.yml")) {
|
||||
removeOld(file, "refresh-interval-milliseconds");
|
||||
}
|
||||
if (file.getName().equals("animations.yml")) {
|
||||
Map<String, Object> values = file.getValues();
|
||||
if (values.size() == 1 && values.containsKey("animations")) {
|
||||
@@ -235,22 +156,16 @@ public interface Platform {
|
||||
public default void loadUniversalFeatures() {
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.getConfiguration().config.getBoolean("enable-header-footer", true)) tab.getFeatureManager().registerFeature("headerfooter", new HeaderFooter(tab));
|
||||
if (tab.getConfiguration().config.getBoolean("do-not-move-spectators", false)) tab.getFeatureManager().registerFeature("spectatorfix", new SpectatorFix(tab));
|
||||
if (tab.getConfiguration().config.getBoolean("classic-vanilla-belowname.enabled", true)) tab.getFeatureManager().registerFeature("belowname", new BelowName(tab));
|
||||
if (tab.getConfiguration().premiumconfig != null && tab.getConfiguration().premiumconfig.getBoolean("scoreboard.enabled", false)) tab.getFeatureManager().registerFeature("scoreboard", new ScoreboardManager(tab));
|
||||
if ((boolean)tab.getConfiguration().getSecretOption("remove-ghost-players", false)) tab.getFeatureManager().registerFeature("ghostplayerfix", new GhostPlayerFix(tab));
|
||||
if (tab.getConfiguration().config.getString("yellow-number-in-tablist", "%ping%").length() > 0) tab.getFeatureManager().registerFeature("tabobjective", new TabObjective(tab));
|
||||
if (tab.getConfiguration().removeGhostPlayers) tab.getFeatureManager().registerFeature("ghostplayerfix", new GhostPlayerFix());
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 8 && tab.getConfiguration().config.getBoolean("change-tablist-prefix-suffix", true)) {
|
||||
Playerlist playerlist = new Playerlist(tab);
|
||||
tab.getFeatureManager().registerFeature("playerlist", playerlist);
|
||||
if (tab.getConfiguration().premiumconfig != null && tab.getConfiguration().premiumconfig.getBoolean("align-tabsuffix-on-the-right", false)) tab.getFeatureManager().registerFeature("alignedsuffix", new AlignedSuffix(playerlist, tab));
|
||||
}
|
||||
tab.getFeatureManager().registerFeature("group", new GroupRefresher(tab));
|
||||
tab.getFeatureManager().registerFeature("info", new PluginInfo(tab));
|
||||
tab.getFeatureManager().registerFeature("info", new PluginInfo());
|
||||
new UpdateChecker(tab);
|
||||
if ((boolean)tab.getConfiguration().getSecretOption("layout", false)) {
|
||||
tab.getFeatureManager().registerFeature("layout", new Layout(tab));
|
||||
}
|
||||
if (tab.getConfiguration().BossBarEnabled) tab.getFeatureManager().registerFeature("bossbar", new BossBar(tab));
|
||||
if (tab.getConfiguration().layout) tab.getFeatureManager().registerFeature("layout", new Layout(tab));
|
||||
if (tab.getConfiguration().bossbar.getBoolean("bossbar-enabled", false)) tab.getFeatureManager().registerFeature("bossbar", new BossBar(tab));
|
||||
}
|
||||
}
|
||||
+16
-9
@@ -6,6 +6,7 @@ import java.util.List;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.placeholders.Placeholder;
|
||||
import me.neznamy.tab.shared.placeholders.RelationalPlaceholder;
|
||||
import me.neznamy.tab.shared.rgb.RGBUtils;
|
||||
|
||||
/**
|
||||
* A string with placeholders
|
||||
@@ -40,18 +41,18 @@ public class Property {
|
||||
public Property(TabPlayer owner, String rawValue, String source) {
|
||||
this.owner = owner;
|
||||
this.source = source;
|
||||
this.rawValue = (rawValue == null ? "" : rawValue);
|
||||
this.rawValue = RGBUtils.getInstance().applyFormats((rawValue == null ? "" : rawValue), true);
|
||||
analyze(this.rawValue);
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all placeholders used in the value
|
||||
* @param value - raw value to be checked
|
||||
*/
|
||||
private void analyze(String value) {
|
||||
placeholders = new ArrayList<String>();
|
||||
relPlaceholders = new ArrayList<String>();
|
||||
List<String> placeholders = new ArrayList<String>();
|
||||
List<String> relPlaceholders = new ArrayList<String>();
|
||||
for (String identifier : TAB.getInstance().getPlaceholderManager().getUsedPlaceholderIdentifiersRecursive(value)) {
|
||||
if (identifier.startsWith("%rel_")) {
|
||||
relPlaceholders.add(identifier);
|
||||
@@ -59,6 +60,9 @@ public class Property {
|
||||
placeholders.add(identifier);
|
||||
}
|
||||
}
|
||||
//avoiding rare concurrent modification in #update
|
||||
this.placeholders = placeholders;
|
||||
this.relPlaceholders = relPlaceholders;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,10 +70,11 @@ public class Property {
|
||||
* @param temporaryValue - temporary value to be assigned
|
||||
*/
|
||||
public void setTemporaryValue(String temporaryValue) {
|
||||
this.temporaryValue = temporaryValue;
|
||||
if (temporaryValue != null) {
|
||||
analyze(temporaryValue);
|
||||
this.temporaryValue = RGBUtils.getInstance().applyFormats(temporaryValue, true);
|
||||
analyze(this.temporaryValue);
|
||||
} else {
|
||||
this.temporaryValue = null;
|
||||
analyze(rawValue);
|
||||
}
|
||||
update();
|
||||
@@ -81,7 +86,7 @@ public class Property {
|
||||
*/
|
||||
public void changeRawValue(String newValue) {
|
||||
if (rawValue.equals(newValue)) return;
|
||||
rawValue = newValue;
|
||||
rawValue = RGBUtils.getInstance().applyFormats(newValue, true);
|
||||
if (temporaryValue == null) {
|
||||
analyze(rawValue);
|
||||
update();
|
||||
@@ -142,6 +147,7 @@ public class Property {
|
||||
* @return if updating changed value or not
|
||||
*/
|
||||
public boolean update() {
|
||||
long time = System.nanoTime();
|
||||
String string = getCurrentRawValue();
|
||||
for (String identifier : placeholders) {
|
||||
Placeholder pl = TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
|
||||
@@ -151,8 +157,10 @@ public class Property {
|
||||
string = applyRemoveStrings(string);
|
||||
if (lastReplacedValue == null || !lastReplacedValue.equals(string)) {
|
||||
lastReplacedValue = string;
|
||||
TAB.getInstance().getCPUManager().addMethodTime("Property#update", System.nanoTime()-time);
|
||||
return true;
|
||||
}
|
||||
TAB.getInstance().getCPUManager().addMethodTime("Property#update", System.nanoTime()-time);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -181,11 +189,10 @@ public class Property {
|
||||
* @return format for the viewer
|
||||
*/
|
||||
public String getFormat(TabPlayer viewer) {
|
||||
if (viewer == null) return lastReplacedValue;
|
||||
String format = lastReplacedValue;
|
||||
for (String identifier : relPlaceholders) {
|
||||
RelationalPlaceholder pl = (RelationalPlaceholder) TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
|
||||
if (pl != null) format = format.replace(pl.getIdentifier(), pl.getLastValue(viewer, owner));
|
||||
if (pl != null) format = format.replace(pl.getIdentifier(), viewer == null ? "" : pl.getLastValue(viewer, owner));
|
||||
}
|
||||
return format;
|
||||
}
|
||||
+20
-4
@@ -6,6 +6,7 @@ package me.neznamy.tab.shared;
|
||||
public enum ProtocolVersion {
|
||||
|
||||
UNKNOWN (999),
|
||||
v1_17 (755),
|
||||
v1_16_5 (754),
|
||||
v1_16_4 (754),
|
||||
v1_16_3 (753),
|
||||
@@ -60,9 +61,26 @@ public enum ProtocolVersion {
|
||||
|
||||
//version's network id found at https://wiki.vg/Protocol_version_numbers
|
||||
private int networkId;
|
||||
|
||||
//minor version, such as 16
|
||||
private int minorVersion;
|
||||
|
||||
//friendly name displayed in %player-version% placeholder
|
||||
private String friendlyName;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given network id
|
||||
* @param networkId - network id of this version
|
||||
*/
|
||||
private ProtocolVersion(int networkId){
|
||||
this.networkId = networkId;
|
||||
if (toString().equals("UNKNOWN")) {
|
||||
this.minorVersion = 17;
|
||||
this.friendlyName = "Unknown";
|
||||
} else {
|
||||
this.minorVersion = Integer.parseInt(toString().split("_")[1]);
|
||||
this.friendlyName = toString().substring(1).replace("_", ".");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,8 +96,7 @@ public enum ProtocolVersion {
|
||||
* @return
|
||||
*/
|
||||
public String getFriendlyName() {
|
||||
if (this == UNKNOWN) return "Unknown";
|
||||
return toString().substring(1).replace("_", ".");
|
||||
return friendlyName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,8 +104,7 @@ public enum ProtocolVersion {
|
||||
* @return version's minor version
|
||||
*/
|
||||
public int getMinorVersion() {
|
||||
if (this == UNKNOWN) return 16;
|
||||
return Integer.parseInt(toString().split("_")[1]);
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
+7
-6
@@ -25,14 +25,14 @@ public class TAB {
|
||||
private static TAB instance;
|
||||
|
||||
//version of plugin
|
||||
public static final String pluginVersion = "2.8.11-pre6";
|
||||
public static final String pluginVersion = "2.9.0";
|
||||
|
||||
//player data
|
||||
public final Map<UUID, TabPlayer> data = new ConcurrentHashMap<UUID, TabPlayer>();
|
||||
|
||||
//the command
|
||||
public final TabCommand command;
|
||||
|
||||
public TabCommand command;
|
||||
|
||||
//command used if plugin is disabled due to a broken configuration file
|
||||
public final DisabledCommand disabledCommand = new DisabledCommand();
|
||||
|
||||
@@ -68,7 +68,6 @@ public class TAB {
|
||||
public TAB(Platform platform, PacketBuilder packetBuilder) {
|
||||
this.platform = platform;
|
||||
this.packetBuilder = packetBuilder;
|
||||
this.command = new TabCommand(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +114,7 @@ public class TAB {
|
||||
*/
|
||||
public TabPlayer getPlayerByTablistUUID(UUID tablistId) {
|
||||
for (TabPlayer p : data.values()) {
|
||||
if (p.getTablistUUID().toString().equals(tablistId.toString())) return p;
|
||||
if (p.getTablistUUID().equals(tablistId)) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -152,8 +151,9 @@ public class TAB {
|
||||
placeholderManager = new PlaceholderManager(this);
|
||||
featureManager.registerFeature("placeholders", placeholderManager);
|
||||
platform.loadFeatures();
|
||||
command = new TabCommand(this);
|
||||
featureManager.load();
|
||||
getPlayers().forEach(p -> p.markAsLoaded());
|
||||
getPlayers().forEach(p -> ((ITabPlayer)p).markAsLoaded());
|
||||
errorManager.printConsoleWarnCount();
|
||||
print('a', "Enabled in " + (System.currentTimeMillis()-time) + "ms");
|
||||
platform.callLoadEvent();
|
||||
@@ -172,6 +172,7 @@ public class TAB {
|
||||
*/
|
||||
public void unload() {
|
||||
if (disabled) return;
|
||||
disabled = true;
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
cpu.cancelAllTasks();
|
||||
+10
@@ -5,8 +5,18 @@ import java.util.List;
|
||||
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* Command processor when TAB is disabled due to broken configuration file
|
||||
*/
|
||||
public class DisabledCommand {
|
||||
|
||||
/**
|
||||
* Performs command and return messages to be sent back
|
||||
* @param args - command arguments
|
||||
* @param hasReloadPermission - if player has reload permission or not
|
||||
* @param hasAdminPermission - if player has admin permission or not
|
||||
* @return list of messages to send back
|
||||
*/
|
||||
public List<String> execute(String[] args, boolean hasReloadPermission, boolean hasAdminPermission) {
|
||||
List<String> messages = new ArrayList<String>();
|
||||
if (args.length == 1 && args[0].equalsIgnoreCase("reload")) {
|
||||
+19
-1
@@ -12,7 +12,10 @@ import me.neznamy.tab.shared.TAB;
|
||||
*/
|
||||
public abstract class SubCommand {
|
||||
|
||||
protected final String[] allProperties = {"tabprefix", "tabsuffix", "tagprefix", "tagsuffix", "customtabname", "abovename", "belowname", "customtagname"};
|
||||
//all properties assignable with a command
|
||||
public static String[] allProperties = {"tabprefix", "tabsuffix", "tagprefix", "tagsuffix", "customtabname", "abovename", "belowname", "customtagname"};
|
||||
|
||||
//properties that require unlimited nametag mode
|
||||
protected final List<String> extraProperties = Arrays.asList("abovename", "belowname", "customtagname");
|
||||
|
||||
//name of this subcommand
|
||||
@@ -24,6 +27,11 @@ public abstract class SubCommand {
|
||||
//subcommands of this command
|
||||
public Map<String, SubCommand> subcommands = new HashMap<String, SubCommand>();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
* @param name - command name
|
||||
* @param permission - permission requirement
|
||||
*/
|
||||
public SubCommand(String name, String permission) {
|
||||
this.name = name;
|
||||
this.permission = permission;
|
||||
@@ -135,6 +143,11 @@ public abstract class SubCommand {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts string array into a single string with "" and '' support for spaces
|
||||
* @param args - array of arguments
|
||||
* @return argument from array
|
||||
*/
|
||||
public String buildArgument(String[] args) {
|
||||
String value = String.join(" ", args);
|
||||
if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
|
||||
@@ -143,6 +156,11 @@ public abstract class SubCommand {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns message with given key
|
||||
* @param key - message key
|
||||
* @return translation from file
|
||||
*/
|
||||
public String getTranslation(String key) {
|
||||
return TAB.getInstance().getConfiguration().translation.getString(key);
|
||||
}
|
||||
+14
-3
@@ -3,6 +3,9 @@ package me.neznamy.tab.shared.command;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
@@ -20,15 +23,19 @@ import me.neznamy.tab.shared.command.level1.ReloadCommand;
|
||||
import me.neznamy.tab.shared.command.level1.ScoreboardCommand;
|
||||
import me.neznamy.tab.shared.command.level1.SendCommand;
|
||||
import me.neznamy.tab.shared.command.level1.SetCollisionCommand;
|
||||
import me.neznamy.tab.shared.command.level1.WidthCommand;
|
||||
|
||||
/**
|
||||
* The core command handler
|
||||
*/
|
||||
public class TabCommand extends SubCommand {
|
||||
|
||||
//tab instance
|
||||
private TAB tab;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter and registers all subcommands
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
public TabCommand(TAB tab) {
|
||||
super("tab", null);
|
||||
this.tab = tab;
|
||||
@@ -47,8 +54,12 @@ public class TabCommand extends SubCommand {
|
||||
registerSubCommand(new SetCollisionCommand());
|
||||
if (tab.isPremium()) {
|
||||
registerSubCommand(new ScoreboardCommand());
|
||||
registerSubCommand(new WidthCommand());
|
||||
}
|
||||
Set<String> properties = Sets.newHashSet("tabprefix", "tabsuffix", "tagprefix", "tagsuffix", "customtabname", "abovename", "belowname", "customtagname");
|
||||
for (Object line : ((DebugCommand)subcommands.get("debug")).getExtraLines()) {
|
||||
properties.add(line.toString());
|
||||
}
|
||||
SubCommand.allProperties = properties.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +112,7 @@ public class TabCommand extends SubCommand {
|
||||
|
||||
@Override
|
||||
public List<String> complete(TabPlayer sender, String[] arguments) {
|
||||
if (!(boolean) tab.getConfiguration().getSecretOption("auto-command-complete", true)) return new ArrayList<String>();
|
||||
if (!hasPermission(sender, "tab.tabcomplete")) return new ArrayList<String>();
|
||||
return super.complete(sender, arguments);
|
||||
}
|
||||
}
|
||||
+3
@@ -12,6 +12,9 @@ import me.neznamy.tab.shared.command.level2.AnnounceScoreboardCommand;
|
||||
*/
|
||||
public class AnnounceCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance and registers subcommands
|
||||
*/
|
||||
public AnnounceCommand() {
|
||||
super("announce", null);
|
||||
subcommands.put("bar", new AnnounceBarCommand());
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.neznamy.tab.shared.command.level1;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.command.SubCommand;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBar;
|
||||
|
||||
/**
|
||||
* Handler for "/tab bossbar" subcommand
|
||||
*/
|
||||
public class BossBarCommand extends SubCommand{
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public BossBarCommand() {
|
||||
super("bossbar", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(TabPlayer sender, String[] args) {
|
||||
BossBar bossbar = (BossBar) TAB.getInstance().getFeatureManager().getFeature("bossbar");
|
||||
if (bossbar == null) {
|
||||
sendMessage(sender, "&cBossbar feature is not enabled, therefore toggle command cannot be used.");
|
||||
return;
|
||||
}
|
||||
if (sender == null) {
|
||||
sendMessage(sender, "&cThis command must be ran from the game");
|
||||
return;
|
||||
}
|
||||
if (!bossbar.permToToggle || sender.hasPermission("tab.togglebar")) {
|
||||
sender.setBossbarVisible(!sender.hasBossbarVisible(), true);
|
||||
} else {
|
||||
sender.sendMessage(getTranslation("no_permission"), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package me.neznamy.tab.shared.command.level1;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.command.SubCommand;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.placeholders.Placeholder;
|
||||
|
||||
/**
|
||||
* Handler for "/tab cpu" subcommand
|
||||
*/
|
||||
public class CpuCommand extends SubCommand {
|
||||
|
||||
private final DecimalFormat decimal3 = new DecimalFormat("#.###");
|
||||
|
||||
private final char LINE_CHAR = (char)9553;
|
||||
private final String SEPARATOR = "&8&l" + LINE_CHAR + "&8&m ";
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public CpuCommand() {
|
||||
super("cpu", "tab.cpu");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(TabPlayer sender, String[] args) {
|
||||
TAB tab = TAB.getInstance();
|
||||
Map<String, Float> placeholders = tab.getCPUManager().getPlaceholderUsage();
|
||||
float placeholdersTotal = 0;
|
||||
for (Float time : placeholders.values()) placeholdersTotal += time;
|
||||
|
||||
Map<String, Float> bridgeplaceholders = tab.getCPUManager().getBridgeUsage();
|
||||
float bridgeplaceholdersTotal = 0;
|
||||
for (Float time : bridgeplaceholders.values()) bridgeplaceholdersTotal += time;
|
||||
|
||||
Map<Object, Map<UsageType, Float>> features = tab.getCPUManager().getFeatureUsage();
|
||||
float featuresTotal = 0;
|
||||
for (Map<UsageType, Float> map : features.values()) {
|
||||
for (Float time : map.values()) {
|
||||
featuresTotal += time;
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(sender, " ");
|
||||
sendMessage(sender, "&8&l" + LINE_CHAR + "&8&m &r&8&l[ &bTAB CPU Stats &8&l]&r&8&l&m ");
|
||||
sendMessage(sender, "&8&l" + LINE_CHAR + " &6CPU stats from the last 10 seconds");
|
||||
sendMessage(sender, SEPARATOR);
|
||||
sendMessage(sender, "&8&l" + LINE_CHAR + " &6Top 5 placeholders:");
|
||||
int printCounter = 0;
|
||||
for (Entry<String, Float> entry : placeholders.entrySet()) {
|
||||
if (printCounter++ == 5) break;
|
||||
String refresh = "";
|
||||
Placeholder p = TAB.getInstance().getPlaceholderManager().getPlaceholder(entry.getKey().toString());
|
||||
if (p != null) refresh = " &8(" + p.getRefresh() + ")&7";
|
||||
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", entry.getKey() + refresh, colorize(decimal3.format(entry.getValue()), 1, 0.3f)));
|
||||
}
|
||||
sendMessage(sender, SEPARATOR);
|
||||
sendMessage(sender, "&8&l" + LINE_CHAR + " &6Some internal separately measured methods:");
|
||||
for (Entry<String, Float> entry : tab.getCPUManager().getMethodUsage().entrySet()) {
|
||||
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7%s: %s%%", entry.getKey(), colorize(decimal3.format(entry.getValue()), 5, 2)));
|
||||
}
|
||||
sendMessage(sender, SEPARATOR);
|
||||
if (tab.getPlatform().getSeparatorType().equals("server")) {
|
||||
sendMessage(sender, "&8&l" + LINE_CHAR + " &6Placeholder usage on Bukkit servers:");
|
||||
for (Entry<String, Float> entry : bridgeplaceholders.entrySet()) {
|
||||
if (entry.getValue() < 0.1) continue;
|
||||
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", entry.getKey(), colorize(decimal3.format(entry.getValue()), 1, 0.3f)));
|
||||
}
|
||||
sendMessage(sender, SEPARATOR);
|
||||
}
|
||||
if (sender != null) {
|
||||
sendToPlayer(sender, features);
|
||||
} else {
|
||||
sendToConsole(features);
|
||||
}
|
||||
sendMessage(sender, SEPARATOR);
|
||||
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7Threads created by the plugin (active/total): &7%s", tab.getCPUManager().getThreadCount()));
|
||||
if (sender != null) {
|
||||
sendPacketCountToPlayer(sender);
|
||||
} else {
|
||||
sendPacketCountToConsole();
|
||||
}
|
||||
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lPlaceholders Total: &a&l%s%%", colorize(decimal3.format(placeholdersTotal), 10, 5)));
|
||||
if (tab.getPlatform().getSeparatorType().equals("server")) {
|
||||
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lBukkit bridge placeholders Total: &a&l%s%%", colorize(decimal3.format(bridgeplaceholdersTotal), 10, 5)));
|
||||
}
|
||||
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lPlugin internals: &a&l%s%%", colorize(decimal3.format(featuresTotal-placeholdersTotal), 10, 5)));
|
||||
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lTotal: &e&l%s%%", colorize(decimal3.format(featuresTotal + bridgeplaceholdersTotal), 10, 5)));
|
||||
sendMessage(sender, "&8&l" + LINE_CHAR + "&8&m &r&8&l[ &bTAB CPU Stats &8&l]&r&8&l&m ");
|
||||
sendMessage(sender, " ");
|
||||
}
|
||||
|
||||
public void sendToConsole(Map<Object, Map<UsageType, Float>> features) {
|
||||
TAB.getInstance().getPlatform().sendConsoleMessage("&8&l" + LINE_CHAR + " &6Features:", true);
|
||||
for (Entry<Object, Map<UsageType, Float>> entry : features.entrySet()) {
|
||||
float featureTotal = 0;
|
||||
for (Float f : entry.getValue().values()) {
|
||||
featureTotal += f;
|
||||
}
|
||||
String core = String.format("&8&l" + LINE_CHAR + " &7%s (%s%%&7):", entry.getKey(), colorize(decimal3.format(featureTotal), 5, 1));
|
||||
List<String> messages = new ArrayList<String>();
|
||||
for (Entry<UsageType, Float> type : entry.getValue().entrySet()){
|
||||
messages.add(String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", type.getKey(), colorize(decimal3.format(type.getValue()), 5, 1)));
|
||||
}
|
||||
TAB.getInstance().getPlatform().sendConsoleMessage(core, true);
|
||||
for (String message : messages) {
|
||||
TAB.getInstance().getPlatform().sendConsoleMessage(message, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendToPlayer(TabPlayer sender, Map<Object, Map<UsageType, Float>> features) {
|
||||
sendMessage(sender, "&8&l" + LINE_CHAR + " &6Features (hover with cursor for more info):");
|
||||
for (Entry<Object, Map<UsageType, Float>> entry : features.entrySet()) {
|
||||
float featureTotal = 0;
|
||||
for (Float f : entry.getValue().values()) {
|
||||
featureTotal += f;
|
||||
}
|
||||
String core = String.format("&8&l" + LINE_CHAR + " &7%s (%s%%&7):", entry.getKey(), colorize(decimal3.format(featureTotal), 5, 1));
|
||||
List<String> messages = new ArrayList<String>();
|
||||
for (Entry<UsageType, Float> type : entry.getValue().entrySet()){
|
||||
messages.add("&3" + type.getKey().toString() + " - " + colorize(decimal3.format(type.getValue()), 5, 1) + "%");
|
||||
}
|
||||
IChatBaseComponent message = new IChatBaseComponent(core.replace('&', '\u00a7'));
|
||||
message.onHoverShowText(String.join("\n", messages).replace('&', '\u00a7'));
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketCountToConsole() {
|
||||
Map<Object, AtomicInteger> packets = TAB.getInstance().getCPUManager().getSentPackets();
|
||||
int count = 0;
|
||||
List<String> messages = new ArrayList<String>();
|
||||
for (Entry<Object, AtomicInteger> entry : packets.entrySet()) {
|
||||
count += entry.getValue().get();
|
||||
messages.add("&8&l" + LINE_CHAR + " &7" + entry.getKey().toString() + " - " + entry.getValue());
|
||||
}
|
||||
TAB.getInstance().getPlatform().sendConsoleMessage("&8&l" + LINE_CHAR + " &r&7Packets sent by the plugin: " + count, true);
|
||||
for (String message : messages) {
|
||||
TAB.getInstance().getPlatform().sendConsoleMessage(message, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketCountToPlayer(TabPlayer sender) {
|
||||
Map<Object, AtomicInteger> packets = TAB.getInstance().getCPUManager().getSentPackets();
|
||||
int count = 0;
|
||||
List<String> messages = new ArrayList<String>();
|
||||
for (Entry<Object, AtomicInteger> entry : packets.entrySet()) {
|
||||
count += entry.getValue().get();
|
||||
messages.add("&3" + entry.getKey().toString() + " - " + entry.getValue());
|
||||
}
|
||||
IChatBaseComponent message = new IChatBaseComponent(("&8&l" + LINE_CHAR + " &r&7Packets sent by the plugin (hover for more info): " + count).replace('&', '\u00a7'));
|
||||
message.onHoverShowText(String.join("\n", messages).replace('&', '\u00a7'));
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns colored usage from provided usage
|
||||
* @param usage - usage
|
||||
* @return colored usage
|
||||
*/
|
||||
private String colorize(String usage, float threshold1, float threshold2) {
|
||||
float percent = Float.parseFloat(usage.replace(",", "."));
|
||||
if (percent > threshold1) return "&c" + usage;
|
||||
if (percent > threshold2) return "&e" + usage;
|
||||
return "&a" + usage;
|
||||
}
|
||||
}
|
||||
+6
@@ -10,6 +10,9 @@ import me.neznamy.tab.shared.command.SubCommand;
|
||||
*/
|
||||
public class CpuTestCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public CpuTestCommand() {
|
||||
super("cputest", "tab.cputest");
|
||||
}
|
||||
@@ -28,6 +31,9 @@ public class CpuTestCommand extends SubCommand {
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the test
|
||||
*/
|
||||
public void test() {
|
||||
String test = UUID.randomUUID().toString();
|
||||
int i=0;
|
||||
+38
-11
@@ -19,6 +19,9 @@ import me.neznamy.tab.shared.features.Playerlist;
|
||||
*/
|
||||
public class DebugCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public DebugCommand() {
|
||||
super("debug", "tab.debug");
|
||||
}
|
||||
@@ -48,14 +51,15 @@ public class DebugCommand extends SubCommand {
|
||||
TAB tab = TAB.getInstance();
|
||||
sendMessage(sender, "&3[TAB] &a&lShowing debug information");
|
||||
sendMessage(sender, "&7&m>-------------------------------<");
|
||||
sendMessage(sender, "&6Server version: &a" + tab.getPlatform().getServerVersion());
|
||||
sendMessage(sender, "&6Plugin version: &a" + tab.getPluginVersion() + (tab.isPremium() ? " Premium" : ""));
|
||||
String c2 = tab.isPremium() ? "&b" : "&a";
|
||||
sendMessage(sender, "&6Server version: " + c2 + tab.getPlatform().getServerVersion());
|
||||
sendMessage(sender, "&6Plugin version: " + c2 + tab.getPluginVersion() + (tab.isPremium() ? " Premium" : ""));
|
||||
if (tab.getErrorManager().errorLog.exists()) {
|
||||
sendMessage(sender, "&6" + tab.getErrorManager().errorLog.getPath() + " size: &c" + tab.getErrorManager().errorLog.length()/1024 + "KB");
|
||||
}
|
||||
sendMessage(sender, "&6Permission plugin: &a" + tab.getPermissionPlugin().getName());
|
||||
sendMessage(sender, getGroupChoiceLogic());
|
||||
sendMessage(sender, "&6Sorting system: &a" + getSortingType());
|
||||
sendMessage(sender, "&6Permission plugin: " + c2 + tab.getPermissionPlugin().getName());
|
||||
sendMessage(sender, "&6Permission group choice logic: " + c2 + getGroupChoiceLogic());
|
||||
sendMessage(sender, "&6Sorting system: " + c2 + getSortingType());
|
||||
sendMessage(sender, "&7&m>-------------------------------<");
|
||||
if (analyzed == null) return;
|
||||
sendMessage(sender, "&ePlayer: &a" + analyzed.getName());
|
||||
@@ -77,7 +81,7 @@ public class DebugCommand extends SubCommand {
|
||||
showProperty(sender, analyzed, "tagprefix", disabledNametags);
|
||||
showProperty(sender, analyzed, "tagsuffix", disabledNametags);
|
||||
for (Object line : getExtraLines()) {
|
||||
showProperty(sender, analyzed, line+"", disabledNametags);
|
||||
showProperty(sender, analyzed, line.toString(), disabledNametags);
|
||||
}
|
||||
} else {
|
||||
sendMessage(sender, "&atagprefix: &cDisabled");
|
||||
@@ -86,17 +90,25 @@ public class DebugCommand extends SubCommand {
|
||||
sendMessage(sender, "&7&m>-------------------------------<");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns group choice logic
|
||||
* @return group choice logic
|
||||
*/
|
||||
private String getGroupChoiceLogic() {
|
||||
GroupRefresher group = (GroupRefresher) TAB.getInstance().getFeatureManager().getFeature("group");
|
||||
if (group.groupsByPermissions) {
|
||||
return "&6Permission group choice logic: &8&mPrimary group&8 / &r&8&mChoose from list&8 / &aPermissions";
|
||||
return "Permissions";
|
||||
} else if (group.usePrimaryGroup) {
|
||||
return "&6Permission group choice logic: &aPrimary group&8 / &r&8&mChoose from list&8 / &r&8&mPermissions";
|
||||
return "Primary group";
|
||||
} else {
|
||||
return "&6Permission group choice logic: &8&mPrimary group&r&8 / &aChoose from list&8 / &r&8&mPermissions";
|
||||
return "Choose from list";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sorting type
|
||||
* @return sorting type
|
||||
*/
|
||||
private String getSortingType() {
|
||||
NameTag nametag = TAB.getInstance().getFeatureManager().getNameTagFeature();
|
||||
if (nametag != null) {
|
||||
@@ -116,6 +128,11 @@ public class DebugCommand extends SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all info about player's group
|
||||
* @param analyzed - player to check group of
|
||||
* @return all info about player's group
|
||||
*/
|
||||
private String getGroup(TabPlayer analyzed) {
|
||||
GroupRefresher group = (GroupRefresher) TAB.getInstance().getFeatureManager().getFeature("group");
|
||||
if (group.groupsByPermissions) {
|
||||
@@ -131,6 +148,11 @@ public class DebugCommand extends SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns team name of specified player
|
||||
* @param analyzed - player to get team name of
|
||||
* @return team name of specified player
|
||||
*/
|
||||
private String getTeamName(TabPlayer analyzed) {
|
||||
if (TAB.getInstance().getFeatureManager().getNameTagFeature() != null) {
|
||||
if (TAB.getInstance().getFeatureManager().getNameTagFeature().isDisabledWorld(analyzed.getWorldName())) {
|
||||
@@ -144,8 +166,12 @@ public class DebugCommand extends SubCommand {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of extra properties if unlimited nametag mode is enabled
|
||||
* @return list of extra properties
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Object> getExtraLines(){
|
||||
public List<Object> getExtraLines(){
|
||||
if (!TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx")) return new ArrayList<Object>();
|
||||
if (TAB.getInstance().isPremium()) {
|
||||
List<Object> lines = Lists.newArrayList((List<Object>) TAB.getInstance().getConfiguration().premiumconfig.getObject("unlimited-nametag-mode-dynamic-lines"));
|
||||
@@ -171,7 +197,8 @@ public class DebugCommand extends SubCommand {
|
||||
} else {
|
||||
Property pr = analyzed.getProperty(property);
|
||||
String rawValue = pr.getCurrentRawValue().replace('\u00a7', '&');
|
||||
String value = TAB.getInstance().getPlaceholderManager().color("&a" + property + ": &e\"&r%rawValue%&r&e\" &7(" + rawValue.length() + ") &9(Source: " + pr.getSource() + ")").replace("%rawValue%", rawValue);
|
||||
String c = TAB.getInstance().isPremium() ? "&7" : "&9";
|
||||
String value = String.format(("&a%s: &e\"&r%s&r&e\" &7(%s) " + c + "(Source: %s)").replace('&', '\u00a7'), property, rawValue, rawValue.length(), pr.getSource());
|
||||
sendRawMessage(sender, value);
|
||||
}
|
||||
}
|
||||
+9
-5
@@ -13,6 +13,9 @@ import me.neznamy.tab.shared.command.SubCommand;
|
||||
*/
|
||||
public class GroupCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public GroupCommand() {
|
||||
super("group", null);
|
||||
}
|
||||
@@ -65,6 +68,12 @@ public class GroupCommand extends SubCommand {
|
||||
* @param value - new value
|
||||
*/
|
||||
private void saveGroup(TabPlayer sender, String group, String type, String value){
|
||||
if (value.length() > 0){
|
||||
sendMessage(sender, getTranslation("value_assigned").replace("%type%", type).replace("%value%", value).replace("%unit%", group).replace("%category%", "group"));
|
||||
} else {
|
||||
sendMessage(sender, getTranslation("value_removed").replace("%type%", type).replace("%unit%", group).replace("%category%", "group"));
|
||||
}
|
||||
if (String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(TAB.getInstance().getConfiguration().config.getObject("Groups." + group.replace(".", "@#@") + "." + type)))) return;
|
||||
TAB.getInstance().getConfiguration().config.set("Groups." + group.replace(".", "@#@") + "." + type, value.length() == 0 ? null : value);
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
for (TabPlayer pl : TAB.getInstance().getPlayers()) {
|
||||
@@ -72,11 +81,6 @@ public class GroupCommand extends SubCommand {
|
||||
pl.forceRefresh();
|
||||
}
|
||||
}
|
||||
if (value.length() > 0){
|
||||
sendMessage(sender, getTranslation("value_assigned").replace("%type%", type).replace("%value%", value).replace("%unit%", group).replace("%category%", "group"));
|
||||
} else {
|
||||
sendMessage(sender, getTranslation("value_removed").replace("%type%", type).replace("%unit%", group).replace("%category%", "group"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
+3
@@ -9,6 +9,9 @@ import me.neznamy.tab.shared.command.SubCommand;
|
||||
*/
|
||||
public class NTPreviewCommand extends SubCommand{
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public NTPreviewCommand() {
|
||||
super("ntpreview", "tab.ntpreview");
|
||||
}
|
||||
+5
-2
@@ -10,6 +10,9 @@ import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
*/
|
||||
public class ParseCommand extends SubCommand{
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public ParseCommand() {
|
||||
super("parse", "tab.parse");
|
||||
}
|
||||
@@ -18,10 +21,10 @@ public class ParseCommand extends SubCommand{
|
||||
public void execute(TabPlayer sender, String[] args) {
|
||||
if (args.length > 0) {
|
||||
String replaced = String.join(" ", args);
|
||||
String message = TAB.getInstance().getPlaceholderManager().color("&6Replacing placeholder &e%placeholder%" + (sender == null ? "" : "&6 for player &e" + sender.getName())).replace("%placeholder%", replaced);
|
||||
String message = ("&6Replacing placeholder &e%placeholder%" + (sender == null ? "" : "&6 for player &e" + sender.getName())).replace('&', '\u00a7').replace("%placeholder%", replaced);
|
||||
sendRawMessage(sender, message);
|
||||
replaced = TAB.getInstance().getPlatform().replaceAllPlaceholders(replaced, sender);
|
||||
IChatBaseComponent colored = IChatBaseComponent.fromColoredText("With colors: " + replaced);
|
||||
IChatBaseComponent colored = IChatBaseComponent.optimizedComponent("With colors: " + replaced);
|
||||
if (sender != null) {
|
||||
sender.sendMessage(colored);
|
||||
} else {
|
||||
+10
-6
@@ -13,6 +13,9 @@ import me.neznamy.tab.shared.command.SubCommand;
|
||||
*/
|
||||
public class PlayerCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public PlayerCommand() {
|
||||
super("player", null);
|
||||
}
|
||||
@@ -64,17 +67,18 @@ public class PlayerCommand extends SubCommand {
|
||||
* @param value - new value
|
||||
*/
|
||||
public void savePlayer(TabPlayer sender, String player, String type, String value){
|
||||
TabPlayer pl = TAB.getInstance().getPlayer(player);
|
||||
TAB.getInstance().getConfiguration().config.set("Users." + player + "." + type, value.length() == 0 ? null : value);
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
if (pl != null) {
|
||||
pl.forceRefresh();
|
||||
}
|
||||
if (value.length() > 0){
|
||||
sendMessage(sender, getTranslation("value_assigned").replace("%type%", type).replace("%value%", value).replace("%unit%", player).replace("%category%", "player"));
|
||||
} else {
|
||||
sendMessage(sender, getTranslation("value_removed").replace("%type%", type).replace("%unit%", player).replace("%category%", "player"));
|
||||
}
|
||||
if (String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(TAB.getInstance().getConfiguration().config.getObject("Users." + player + "." + type)))) return;
|
||||
TAB.getInstance().getConfiguration().config.set("Users." + player + "." + type, value.length() == 0 ? null : value);
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
TabPlayer pl = TAB.getInstance().getPlayer(player);
|
||||
if (pl != null) {
|
||||
pl.forceRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
+7
-3
@@ -13,6 +13,9 @@ import me.neznamy.tab.shared.command.SubCommand;
|
||||
*/
|
||||
public class PlayerUUIDCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public PlayerUUIDCommand() {
|
||||
super("playeruuid", null);
|
||||
}
|
||||
@@ -66,14 +69,15 @@ public class PlayerUUIDCommand extends SubCommand {
|
||||
* @param value - new value
|
||||
*/
|
||||
public void savePlayer(TabPlayer sender, TabPlayer player, String type, String value){
|
||||
TAB.getInstance().getConfiguration().config.set("Users." + player.getUniqueId() + "." + type, value.length() == 0 ? null : value);
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
player.forceRefresh();
|
||||
if (value.length() > 0){
|
||||
sendMessage(sender, getTranslation("value_assigned").replace("%type%", type).replace("%value%", value).replace("%unit%", player.getName() + "(" + player.getUniqueId().toString() + ")").replace("%category%", "UUID"));
|
||||
} else {
|
||||
sendMessage(sender, getTranslation("value_removed").replace("%type%", type).replace("%unit%", player.getName() + "(" + player.getUniqueId().toString() + ")").replace("%category%", "UUID"));
|
||||
}
|
||||
if (String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(TAB.getInstance().getConfiguration().config.getObject("Users." + player.getUniqueId() + "." + type)))) return;
|
||||
TAB.getInstance().getConfiguration().config.set("Users." + player.getUniqueId() + "." + type, value.length() == 0 ? null : value);
|
||||
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
|
||||
player.forceRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
+3
@@ -9,6 +9,9 @@ import me.neznamy.tab.shared.command.SubCommand;
|
||||
*/
|
||||
public class ReloadCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public ReloadCommand() {
|
||||
super("reload", "tab.reload");
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package me.neznamy.tab.shared.command.level1;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.command.SubCommand;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
|
||||
|
||||
/**
|
||||
* Handler for "/tab scoreboard [on/off/toggle] [player] [options]" subcommand
|
||||
*/
|
||||
public class ScoreboardCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public ScoreboardCommand() {
|
||||
super("scoreboard", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(TabPlayer sender, String[] args) {
|
||||
ScoreboardManager scoreboard = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
|
||||
if (scoreboard == null) {
|
||||
sendMessage(sender, "&cScoreboard feature is not enabled, therefore toggle command cannot be used.");
|
||||
return;
|
||||
}
|
||||
if (scoreboard.permToToggle && !hasPermission(sender, "tab.togglescoreboard")) {
|
||||
sendMessage(sender, getTranslation("no_permission"));
|
||||
return;
|
||||
}
|
||||
if (args.length == 0) {
|
||||
if (sender == null) {
|
||||
sendMessage(sender, "Toggle command must be ran from the game");
|
||||
} else {
|
||||
sender.toggleScoreboard(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
TabPlayer p = sender;
|
||||
if (args.length >= 2 && TAB.getInstance().getPlayer(args[1]) != null) {
|
||||
if (hasPermission(sender, "tab.togglescoreboard.other")) {
|
||||
p = TAB.getInstance().getPlayer(args[1]);
|
||||
} else {
|
||||
sendMessage(sender, getTranslation("no_permission"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (((ITabPlayer)p).getOtherPluginScoreboard() != null) return; //not overriding other plugins
|
||||
boolean silent = args.length >= 3 && args[2].equals("-s");
|
||||
if (args.length >= 1) {
|
||||
switch(args[0]) {
|
||||
case "on":
|
||||
p.setScoreboardVisible(true, !silent);
|
||||
break;
|
||||
case "off":
|
||||
p.setScoreboardVisible(false, !silent);
|
||||
break;
|
||||
case "toggle":
|
||||
p.toggleScoreboard(!silent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -11,6 +11,9 @@ import me.neznamy.tab.shared.command.level2.SendBarCommand;
|
||||
*/
|
||||
public class SendCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public SendCommand() {
|
||||
super("send", null);
|
||||
subcommands.put("bar", new SendBarCommand());
|
||||
+4
-1
@@ -6,6 +6,9 @@ import me.neznamy.tab.shared.command.SubCommand;
|
||||
|
||||
public class SetCollisionCommand extends SubCommand {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public SetCollisionCommand() {
|
||||
super("setcollision", "tab.setcollision");
|
||||
}
|
||||
@@ -21,7 +24,7 @@ public class SetCollisionCommand extends SubCommand {
|
||||
try {
|
||||
boolean value = Boolean.parseBoolean(args[1]);
|
||||
target.setCollisionRule(value);
|
||||
target.updateTeamData();
|
||||
TAB.getInstance().getFeatureManager().getNameTagFeature().updateTeamData(target);
|
||||
} catch (Exception e) {
|
||||
sendMessage(sender, "&c\"" + args[1] + "\" is not a valid true/false value");
|
||||
}
|
||||
+5
-3
@@ -15,7 +15,9 @@ import me.neznamy.tab.shared.features.bossbar.BossBarLine;
|
||||
*/
|
||||
public class AnnounceBarCommand extends SubCommand{
|
||||
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public AnnounceBarCommand() {
|
||||
super("bar", "tab.announce.bar");
|
||||
}
|
||||
@@ -36,12 +38,12 @@ public class AnnounceBarCommand extends SubCommand{
|
||||
try {
|
||||
duration = Integer.parseInt(args[1]);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(args[1] + " is not a number!", false);
|
||||
sendMessage(sender, args[1] + " is not a number!");
|
||||
return;
|
||||
}
|
||||
BossBarLine bar = feature.lines.get(barname);
|
||||
if (bar == null) {
|
||||
sender.sendMessage("Bar not found", false);
|
||||
sendMessage(sender, "Bar not found");
|
||||
return;
|
||||
}
|
||||
new Thread(() -> {
|
||||
+11
-3
@@ -17,7 +17,9 @@ import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
|
||||
*/
|
||||
public class AnnounceScoreboardCommand extends SubCommand{
|
||||
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public AnnounceScoreboardCommand() {
|
||||
super("scoreboard", "tab.announce.scoreboard");
|
||||
}
|
||||
@@ -38,17 +40,23 @@ public class AnnounceScoreboardCommand extends SubCommand{
|
||||
try {
|
||||
duration = Integer.parseInt(args[1]);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(args[1] + " is not a number!", false);
|
||||
sendMessage(sender, args[1] + " is not a number!");
|
||||
return;
|
||||
}
|
||||
Scoreboard sb = feature.getScoreboards().get(scoreboard);
|
||||
if (sb == null) {
|
||||
sender.sendMessage("Scoreboard not found", false);
|
||||
sendMessage(sender, "Scoreboard not found");
|
||||
return;
|
||||
}
|
||||
announce(feature, sb, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs accounce
|
||||
* @param feature - scoreboard feature
|
||||
* @param sb - scoreboard to announce
|
||||
* @param duration - duration in seconds
|
||||
*/
|
||||
private void announce(ScoreboardManager feature, Scoreboard sb, int duration) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
+4
-2
@@ -15,7 +15,9 @@ import me.neznamy.tab.shared.features.bossbar.BossBarLine;
|
||||
*/
|
||||
public class SendBarCommand extends SubCommand{
|
||||
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
*/
|
||||
public SendBarCommand() {
|
||||
super("bar", "tab.send.bar");
|
||||
}
|
||||
@@ -46,7 +48,7 @@ public class SendBarCommand extends SubCommand{
|
||||
}
|
||||
BossBarLine bar = feature.lines.get(barname);
|
||||
if (bar == null) {
|
||||
sender.sendMessage("Bar not found", false);
|
||||
sendMessage(sender, "No bar found with name \"" + bar + "\"");
|
||||
return;
|
||||
}
|
||||
new Thread(() -> {
|
||||
+85
-33
@@ -19,48 +19,79 @@ import java.util.Set;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* A mess of config options
|
||||
* Looking to clean this up in the future
|
||||
* Core of loading configuration files
|
||||
*/
|
||||
public class Configs {
|
||||
|
||||
private TAB tab;
|
||||
|
||||
//config.yml file
|
||||
public ConfigurationFile config;
|
||||
public boolean collisionRule;
|
||||
public List<String> revertedCollision;
|
||||
public List<String> removeStrings;
|
||||
|
||||
//remove-strings option
|
||||
public List<String> removeStrings;
|
||||
|
||||
//hidden config options
|
||||
public boolean rgbSupport;
|
||||
public boolean unregisterBeforeRegister;
|
||||
public String multiWorldSeparator;
|
||||
public String essentialsNickPrefix;
|
||||
public boolean armorStandsAlwaysVisible; //paid private addition
|
||||
public boolean removeGhostPlayers;
|
||||
public boolean layout;
|
||||
public boolean pipelineInjection;
|
||||
public boolean bukkitPermissions;
|
||||
|
||||
//animations.yml file
|
||||
public ConfigurationFile animation;
|
||||
|
||||
//bossbar.yml file
|
||||
public ConfigurationFile bossbar;
|
||||
public boolean BossBarEnabled;
|
||||
|
||||
//translation.yml file
|
||||
public ConfigurationFile translation;
|
||||
|
||||
//default reload message in case plugin did not load translation file due to an error
|
||||
public String reloadFailed = "&4Failed to reload, file %file% has broken syntax. Check console for more info.";
|
||||
|
||||
//premiumconfig.yml
|
||||
public ConfigurationFile premiumconfig;
|
||||
|
||||
//playerdata.yml, used for bossbar & scoreboard toggle saving
|
||||
public ConfigurationFile playerdata;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* @param tab - tab instance
|
||||
*/
|
||||
public Configs(TAB tab) {
|
||||
this.tab = tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all configuration files and converts files to latest version
|
||||
* @throws Exception - if I/O file opration fails or some file has broken yaml syntax
|
||||
*/
|
||||
public void loadFiles() throws Exception {
|
||||
ClassLoader loader = Configs.class.getClassLoader();
|
||||
removeAdvancedConfig();
|
||||
loadConfig();
|
||||
animation = new YamlConfigurationFile(loader.getResourceAsStream("animations.yml"), new File(tab.getPlatform().getDataFolder(), "animations.yml"));
|
||||
tab.getPlatform().convertConfig(animation);
|
||||
loadBossbar();
|
||||
Map<String, Object> values = animation.getValues();
|
||||
if (values.size() == 1 && values.containsKey("animations")) {
|
||||
animation.setValues(animation.getConfigurationSection("animations"));
|
||||
animation.save();
|
||||
TAB.getInstance().print('2', "Converted animations.yml to new format.");
|
||||
}
|
||||
bossbar = new YamlConfigurationFile(loader.getResourceAsStream("bossbar.yml"), new File(tab.getPlatform().getDataFolder(), "bossbar.yml"));
|
||||
translation = new YamlConfigurationFile(loader.getResourceAsStream("translation.yml"), new File(tab.getPlatform().getDataFolder(), "translation.yml"));
|
||||
reloadFailed = translation.getString("reload-failed", "&4Failed to reload, file %file% has broken syntax. Check console for more info.");
|
||||
if (tab.isPremium()) {
|
||||
premiumconfig = new YamlConfigurationFile(loader.getResourceAsStream("premiumconfig.yml"), new File(tab.getPlatform().getDataFolder(), "premiumconfig.yml"));
|
||||
tab.getPlatform().convertConfig(premiumconfig);
|
||||
}
|
||||
if (tab.isPremium()) premiumconfig = new YamlConfigurationFile(loader.getResourceAsStream("premiumconfig.yml"), new File(tab.getPlatform().getDataFolder(), "premiumconfig.yml"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion from old advancedconfig.yml file
|
||||
*/
|
||||
private void removeAdvancedConfig() {
|
||||
File f = new File(tab.getPlatform().getDataFolder(), "advancedconfig.yml");
|
||||
if (f.exists()) {
|
||||
@@ -74,26 +105,37 @@ public class Configs {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Loads config.yml and some of it's values
|
||||
* @throws Exception - if file has broken syntax or I/O file operation fails
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadConfig() throws Exception {
|
||||
String source = tab.getPlatform().getSeparatorType().equals("world") ? "bukkitconfig.yml" : "bungeeconfig.yml";
|
||||
config = new YamlConfigurationFile(Configs.class.getClassLoader().getResourceAsStream(source), new File(tab.getPlatform().getDataFolder(), "config.yml"), Arrays.asList("# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml", ""));
|
||||
tab.getPlatform().convertConfig(config);
|
||||
collisionRule = config.getBoolean("enable-collision", true);
|
||||
config = new YamlConfigurationFile(Configs.class.getClassLoader().getResourceAsStream(tab.getPlatform().getConfigName()), new File(tab.getPlatform().getDataFolder(), "config.yml"), Arrays.asList("# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml", ""));
|
||||
removeStrings = new ArrayList<>();
|
||||
for (String s : config.getStringList("placeholders.remove-strings", Arrays.asList("[] ", "< > "))) {
|
||||
removeStrings.add(s.replace('&', '\u00a7'));
|
||||
}
|
||||
revertedCollision = config.getStringList("revert-collision-rule-in-" + tab.getPlatform().getSeparatorType()+"s", Arrays.asList("reverted" + tab.getPlatform().getSeparatorType()));
|
||||
tab.debugMode = (boolean) getSecretOption("debug", false);
|
||||
rgbSupport = (boolean) getSecretOption("rgb-support", true);
|
||||
unregisterBeforeRegister = (boolean) getSecretOption("unregister-before-register", true);
|
||||
multiWorldSeparator = (String) getSecretOption("multi-world-separator", "-");
|
||||
essentialsNickPrefix = (String) getSecretOption("essentials-nickname-prefix", "");
|
||||
armorStandsAlwaysVisible = (boolean) getSecretOption("unlimited-nametag-prefix-suffix-mode.always-visible", false);
|
||||
removeGhostPlayers = (boolean) getSecretOption("remove-ghost-players", false);
|
||||
layout = (boolean) getSecretOption("layout", false);
|
||||
pipelineInjection = (boolean) getSecretOption("pipeline-injection", true);
|
||||
if (tab.getPlatform().getSeparatorType().equals("server")) {
|
||||
bukkitPermissions = config.getBoolean("use-bukkit-permissions-manager", false);
|
||||
}
|
||||
|
||||
//checking for unnecessary copypaste in config
|
||||
Set<Object> groups = config.getConfigurationSection("Groups").keySet();
|
||||
if (groups.size() < 2) return;
|
||||
Map<Object, Object> sharedProperties = new HashMap<>(config.getConfigurationSection("Groups." + groups.toArray()[0])); //cloning to not delete from original one
|
||||
for (Object groupSettings : config.getConfigurationSection("Groups").values()) {
|
||||
if (!(groupSettings instanceof Map)) continue;
|
||||
Map<String, Object> group = (Map<String, Object>) groupSettings;
|
||||
if (group == null) continue; // #261
|
||||
for (Entry<Object, Object> sharedProperty : new HashSet<>(sharedProperties.entrySet())) {
|
||||
String property = sharedProperty.getKey().toString();
|
||||
if (!group.containsKey(property) || !String.valueOf(group.get(property)).equals(sharedProperty.getValue())) {
|
||||
@@ -106,24 +148,23 @@ public class Configs {
|
||||
}
|
||||
}
|
||||
|
||||
public void loadBossbar() throws Exception {
|
||||
bossbar = new YamlConfigurationFile(Configs.class.getClassLoader().getResourceAsStream("bossbar.yml"), new File(tab.getPlatform().getDataFolder(), "bossbar.yml"));
|
||||
tab.getPlatform().convertConfig(bossbar);
|
||||
if (bossbar.hasConfigOption("enabled")) {
|
||||
tab.getErrorManager().startupWarn("You are using old bossbar config, please make a backup of the file and delete it to get new file.");
|
||||
BossBarEnabled = false;
|
||||
return;
|
||||
}
|
||||
BossBarEnabled = bossbar.getBoolean("bossbar-enabled", false);
|
||||
}
|
||||
|
||||
|
||||
public Object getSecretOption(String path, Object defaultValue) {
|
||||
/**
|
||||
* Returns value of hidden config option with specified path if it exists, defaultValue otherwise
|
||||
* @param path - path to value
|
||||
* @param defaultValue - value to return if option is not present in file
|
||||
* @return value with specified path or default value if not present
|
||||
*/
|
||||
private Object getSecretOption(String path, Object defaultValue) {
|
||||
if (config == null) return defaultValue;
|
||||
Object value = config.getObject(path);
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns player data with specified key
|
||||
* @param key - data key
|
||||
* @return list of players logged in this data key
|
||||
*/
|
||||
public List<String> getPlayerData(String key) {
|
||||
if (playerdata == null) {
|
||||
File file = new File(tab.getPlatform().getDataFolder(), "playerdata.yml");
|
||||
@@ -138,11 +179,17 @@ public class Configs {
|
||||
return playerdata.getStringList(key, new ArrayList<String>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of world group of specified world in per-server-settings that may consist of multiple
|
||||
* world separated with "-" or something else defined in config
|
||||
* @param world - name of world to find group of
|
||||
* @return name of world group
|
||||
*/
|
||||
public String getWorldGroupOf(String world) {
|
||||
Map<String, Object> worlds = config.getConfigurationSection("per-" + tab.getPlatform().getSeparatorType() + "-settings");
|
||||
if (worlds.isEmpty()) return world;
|
||||
for (String worldGroup : worlds.keySet()) {
|
||||
for (String localWorld : worldGroup.split((String)getSecretOption("multi-world-separator", "-"))) {
|
||||
for (String localWorld : worldGroup.split(multiWorldSeparator)) {
|
||||
if (localWorld.equalsIgnoreCase(world)) return worldGroup;
|
||||
}
|
||||
}
|
||||
@@ -168,13 +215,18 @@ public class Configs {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes defined line of text to file
|
||||
* @param f - file to write to
|
||||
* @param line - line to write
|
||||
*/
|
||||
public void write(File f, String line){
|
||||
try {
|
||||
BufferedWriter buf = new BufferedWriter(new FileWriter(f, true));
|
||||
buf.write(line + System.getProperty("line.separator"));
|
||||
buf.close();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
tab.getErrorManager().criticalError("Failed to write to file " + f.getName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-8
@@ -36,12 +36,20 @@ public abstract class ConfigurationFile {
|
||||
//the file
|
||||
protected File file;
|
||||
|
||||
public ConfigurationFile(InputStream source, File destination, List<String> header) throws IOException {
|
||||
/**
|
||||
* Constructs new instance and copies file from source to destination if it does not exist
|
||||
* @param source - source to copy from if file does not exist
|
||||
* @param destination - destination to load
|
||||
* @param header - comments on top of the file
|
||||
* @throws IllegalStateException - if file does not exist and source is null
|
||||
* @throws IOException - if I/O file operation fails
|
||||
*/
|
||||
public ConfigurationFile(InputStream source, File destination, List<String> header) throws IllegalStateException, IOException {
|
||||
this.header = header;
|
||||
this.file = destination;
|
||||
file.getParentFile().mkdirs();
|
||||
if (file.getParentFile() != null) file.getParentFile().mkdirs();
|
||||
if (!file.exists()) {
|
||||
if (source == null) throw new IllegalStateException("file does not exist and source is null");
|
||||
if (source == null) throw new IllegalStateException("File does not exist and source is null");
|
||||
Files.copy(source, file.toPath());
|
||||
}
|
||||
}
|
||||
@@ -67,6 +75,10 @@ public abstract class ConfigurationFile {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces values with provided map
|
||||
* @param values - values to replace map with
|
||||
*/
|
||||
public void setValues(Map<String, Object> values){
|
||||
this.values = values;
|
||||
}
|
||||
@@ -138,7 +150,7 @@ public abstract class ConfigurationFile {
|
||||
public String getString(String path, String defaultValue) {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
return value+"";
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +178,7 @@ public abstract class ConfigurationFile {
|
||||
}
|
||||
List<String> fixedList = new ArrayList<String>();
|
||||
for (Object key : (List<Object>)value) {
|
||||
fixedList.add(key+"");
|
||||
fixedList.add(key.toString());
|
||||
}
|
||||
return fixedList;
|
||||
}
|
||||
@@ -200,7 +212,7 @@ public abstract class ConfigurationFile {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
try{
|
||||
return Integer.parseInt(value+"");
|
||||
return Integer.parseInt(value.toString());
|
||||
} catch (Exception e) {
|
||||
dataMismatch(path, Integer.class, value.getClass());
|
||||
return defaultValue;
|
||||
@@ -227,7 +239,7 @@ public abstract class ConfigurationFile {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
try{
|
||||
return Boolean.parseBoolean(value+"");
|
||||
return Boolean.parseBoolean(value.toString());
|
||||
} catch (Exception e) {
|
||||
dataMismatch(path, Boolean.class, value.getClass());
|
||||
return defaultValue;
|
||||
@@ -245,7 +257,7 @@ public abstract class ConfigurationFile {
|
||||
Object value = getObject(path, defaultValue);
|
||||
if (value == null) return defaultValue;
|
||||
try{
|
||||
return Double.parseDouble(value+"");
|
||||
return Double.parseDouble(value.toString());
|
||||
} catch (Exception e) {
|
||||
dataMismatch(path, Double.class, value.getClass());
|
||||
return defaultValue;
|
||||
+4
-4
@@ -29,7 +29,7 @@ public class YamlConfigurationFile extends ConfigurationFile {
|
||||
private Yaml yaml;
|
||||
|
||||
/**
|
||||
* Constructs new instance and tried to load configuration file
|
||||
* Constructs new instance and tries to load configuration file
|
||||
* @param source - source to copy file from if it does not exist
|
||||
* @param destination - destination of the file to be copied file to if needed and loaded
|
||||
* @throws IllegalStateException - when file does not exist and source is null
|
||||
@@ -41,7 +41,7 @@ public class YamlConfigurationFile extends ConfigurationFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance and tried to load configuration file
|
||||
* Constructs new instance and tries to load configuration file
|
||||
* @param source - source to copy file from if it does not exist
|
||||
* @param destination - destination of the file to be copied file to if needed and loaded
|
||||
* @param header - comments at the beginning of the file to be pasted when file changes or null if you do not want any
|
||||
@@ -65,7 +65,7 @@ public class YamlConfigurationFile extends ConfigurationFile {
|
||||
} catch (YAMLException e) {
|
||||
input.close();
|
||||
TAB tab = TAB.getInstance();
|
||||
tab.getErrorManager().startupWarn("File " + destination + " has broken formatting.");
|
||||
tab.getErrorManager().startupWarn("File " + destination + " has broken syntax.");
|
||||
tab.brokenFile = file.getPath();
|
||||
tab.getPlatform().sendConsoleMessage("&6[TAB] Error message from yaml parser: " + e.getMessage(), true);
|
||||
List<String> suggestions = YamlAssist.getSuggestions(file);
|
||||
@@ -87,7 +87,7 @@ public class YamlConfigurationFile extends ConfigurationFile {
|
||||
writer.close();
|
||||
if (!hasHeader()) fixHeader();
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to save yaml file " + file.getPath(), e);
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to save yaml file " + file.getPath() + " with content " + values.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
package me.neznamy.tab.shared.cpu;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import me.neznamy.tab.shared.ErrorManager;
|
||||
|
||||
/**
|
||||
* A class which measures CPU usage of all tasks inserted into it and shows usage
|
||||
*/
|
||||
public class CPUManager {
|
||||
|
||||
//data reset interval in milliseconds
|
||||
private final int bufferSizeMillis = 10000;
|
||||
|
||||
//nanoseconds worked in the current 10 seconds
|
||||
private Map<Object, Map<UsageType, AtomicLong>> featureUsageCurrent = new ConcurrentHashMap<Object, Map<UsageType, AtomicLong>>();
|
||||
private Map<String, AtomicLong> placeholderUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
|
||||
private Map<String, AtomicLong> bridgePlaceholderUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
|
||||
private Map<String, AtomicLong> methodUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
|
||||
|
||||
//packets sent in the current 10 seconds
|
||||
private Map<Object, AtomicInteger> packetsCurrent = new ConcurrentHashMap<Object, AtomicInteger>();
|
||||
|
||||
//nanoseconds worked in the previous 10 seconds
|
||||
private Map<Object, Map<UsageType, AtomicLong>> featureUsagePrevious = new HashMap<Object, Map<UsageType, AtomicLong>>();
|
||||
private Map<String, AtomicLong> placeholderUsagePrevious = new HashMap<String, AtomicLong>();
|
||||
private Map<String, AtomicLong> bridgePlaceholderUsagePrevious = new HashMap<String, AtomicLong>();
|
||||
private Map<String, AtomicLong> methodUsagePrevious = new HashMap<String, AtomicLong>();
|
||||
|
||||
//packets sent in the previous 10 seconds
|
||||
private Map<Object, AtomicInteger> packetsPrevious = new ConcurrentHashMap<Object, AtomicInteger>();
|
||||
|
||||
//thread pool
|
||||
private ThreadPoolExecutor exe = (ThreadPoolExecutor) Executors.newCachedThreadPool();
|
||||
|
||||
//error manager
|
||||
private ErrorManager errorManager;
|
||||
|
||||
/**
|
||||
* Constructs new instance and starts repeating task that resets values every 10 seconds
|
||||
* @param errorManager - error manager
|
||||
*/
|
||||
public CPUManager(ErrorManager errorManager) {
|
||||
this.errorManager = errorManager;
|
||||
exe.submit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
Thread.sleep(bufferSizeMillis);
|
||||
|
||||
featureUsagePrevious = featureUsageCurrent;
|
||||
placeholderUsagePrevious = placeholderUsageCurrent;
|
||||
bridgePlaceholderUsagePrevious = bridgePlaceholderUsageCurrent;
|
||||
methodUsagePrevious = methodUsageCurrent;
|
||||
packetsPrevious = packetsCurrent;
|
||||
|
||||
featureUsageCurrent = new ConcurrentHashMap<Object, Map<UsageType, AtomicLong>>();
|
||||
placeholderUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
|
||||
bridgePlaceholderUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
|
||||
methodUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
|
||||
packetsCurrent = new ConcurrentHashMap<Object, AtomicInteger>();
|
||||
}
|
||||
} catch (InterruptedException pluginDisabled) {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns amount of active and total threads
|
||||
* @return active and total threads from this thread pool
|
||||
*/
|
||||
public String getThreadCount() {
|
||||
return exe.getActiveCount() + "/" + exe.getPoolSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all tasks, new instance is set to avoid errors when starting tasks on shutdown (such as packet readers)
|
||||
*/
|
||||
public void cancelAllTasks() {
|
||||
//preventing errors when tasks are inserted while shutting down
|
||||
ExecutorService old = exe;
|
||||
exe = (ThreadPoolExecutor) Executors.newCachedThreadPool();
|
||||
old.shutdownNow();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void runMeasuredTask(String errorDescription, Object feature, UsageType type, Runnable task) {
|
||||
exe.submit(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
long time = System.nanoTime();
|
||||
task.run();
|
||||
addTime(feature, type, System.nanoTime()-time);
|
||||
} catch (Throwable t) {
|
||||
errorManager.printError("An error occurred when " + errorDescription, t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs task in a new thread
|
||||
* @param errorDescription - description to use if this task throws an error
|
||||
* @param task - the task
|
||||
*/
|
||||
public void runTask(String errorDescription, Runnable task) {
|
||||
exe.submit(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
task.run();
|
||||
} catch (Throwable t) {
|
||||
errorManager.printError("An error occurred when " + errorDescription, t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void startRepeatingMeasuredTask(int intervalMilliseconds, String errorDescription, Object feature, UsageType type, Runnable task) {
|
||||
if (intervalMilliseconds <= 0) return;
|
||||
exe.submit(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
long lastLoop = System.currentTimeMillis()-intervalMilliseconds;
|
||||
while (true) {
|
||||
try {
|
||||
long sleep = intervalMilliseconds - (System.currentTimeMillis()-lastLoop);
|
||||
if (sleep < 0) {
|
||||
sleep = 0;
|
||||
}
|
||||
Thread.sleep(sleep);
|
||||
lastLoop = System.currentTimeMillis();
|
||||
long time = System.nanoTime();
|
||||
task.run();
|
||||
addTime(feature, type, System.nanoTime()-time);
|
||||
} catch (InterruptedException pluginDisabled) {
|
||||
break;
|
||||
} catch (Throwable t) {
|
||||
errorManager.printError("An error occurred when " + errorDescription, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void runTaskLater(int delayMilliseconds, String errorDescription, Object feature, UsageType type, Runnable task) {
|
||||
exe.submit(new Runnable() {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(delayMilliseconds);
|
||||
long time = System.nanoTime();
|
||||
task.run();
|
||||
addTime(feature, type, System.nanoTime()-time);
|
||||
} catch (InterruptedException pluginDisabled) {
|
||||
} catch (Throwable t) {
|
||||
errorManager.printError("An error occurred when " + errorDescription, t);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cpu usage map of placeholders from previous 10 seconds
|
||||
* @return cpu usage map of placeholders
|
||||
*/
|
||||
public Map<String, Float> getPlaceholderUsage(){
|
||||
return getUsage(placeholderUsagePrevious);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cpu usage map of placeholders on bukkit side from previous 10 seconds. This is only used
|
||||
* when TAB is on bungeecord with PAPI placeholder support
|
||||
* @return cpu usage map of placeholders
|
||||
*/
|
||||
public Map<String, Float> getBridgeUsage(){
|
||||
return getUsage(bridgePlaceholderUsagePrevious);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cpu usage map of methods previous 10 seconds
|
||||
* @return cpu usage map of methods
|
||||
*/
|
||||
public Map<String, Float> getMethodUsage(){
|
||||
return getUsage(methodUsagePrevious);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns map of sent packets per feature in previous 10 seconds
|
||||
* @return map of sent packets per feature
|
||||
*/
|
||||
public Map<Object, AtomicInteger> getSentPackets(){
|
||||
return sortByValue1(packetsPrevious);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts nano map to percent and sorts it from highest to lowest usage
|
||||
* @param map - map to converted
|
||||
* @return converted and sorted map
|
||||
*/
|
||||
private Map<String, Float> getUsage(Map<String, AtomicLong> map){
|
||||
Map<String, Long> nanoMap = new HashMap<String, Long>();
|
||||
String key;
|
||||
for (Entry<String, AtomicLong> nanos : map.entrySet()) {
|
||||
key = nanos.getKey();
|
||||
if (!nanoMap.containsKey(key)) nanoMap.put(key, 0L);
|
||||
nanoMap.put(key, nanoMap.get(key)+nanos.getValue().get());
|
||||
}
|
||||
Map<String, Float> percentMap = new HashMap<String, Float>();
|
||||
for (Entry<String, Long> entry : nanoMap.entrySet()) {
|
||||
percentMap.put(entry.getKey(), nanosToPercent(entry.getValue()));
|
||||
}
|
||||
return sortByValue(percentMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns map of CPU usage per feature per type in the previous 10 seconds
|
||||
* @return map of CPU usage per feature per type
|
||||
*/
|
||||
public Map<Object, Map<UsageType, Float>> getFeatureUsage(){
|
||||
Map<Object, Map<UsageType, Long>> total = new HashMap<Object, Map<UsageType, Long>>();
|
||||
for (Entry<Object, Map<UsageType, AtomicLong>> nanos : featureUsagePrevious.entrySet()) {
|
||||
Object key = nanos.getKey();
|
||||
if (!total.containsKey(key)) {
|
||||
total.put(key, new HashMap<UsageType, Long>());
|
||||
}
|
||||
Map<UsageType, Long> usage = total.get(key);
|
||||
for (Entry<UsageType, AtomicLong> entry : nanos.getValue().entrySet()) {
|
||||
if (!usage.containsKey(entry.getKey())) {
|
||||
usage.put(entry.getKey(), 0L);
|
||||
}
|
||||
usage.put(entry.getKey(), usage.get(entry.getKey()) + entry.getValue().get());
|
||||
}
|
||||
}
|
||||
Map<Object, Map<UsageType, Float>> sorted = new LinkedHashMap<Object, Map<UsageType, Float>>();
|
||||
for (Object key : sortKeys(total)) {
|
||||
Map<UsageType, Long> local = sortByValue(total.get(key));
|
||||
Map<UsageType, Float> percent = new LinkedHashMap<UsageType, Float>();
|
||||
for (Entry<UsageType, Long> entry : local.entrySet()) {
|
||||
percent.put(entry.getKey(), nanosToPercent(entry.getValue()));
|
||||
}
|
||||
sorted.put(key, percent);
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts nanoseconds to percentual usage
|
||||
* @param nanos - nanoseconds worked
|
||||
* @return usage in % (0-100)
|
||||
*/
|
||||
private float nanosToPercent(long nanos) {
|
||||
float percent = (float) nanos / bufferSizeMillis / 1000000; //relative usage (0-1)
|
||||
percent *= 100; //relative into %
|
||||
return percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts map by value from higest to lowest
|
||||
* @param <K> - map key
|
||||
* @param <V> - map value
|
||||
* @param map - map to sort
|
||||
* @return sorted map
|
||||
*/
|
||||
private <K, V extends Comparable<V>> Map<K, V> sortByValue(Map<K, V> map) {
|
||||
Comparator<K> valueComparator = new Comparator<K>() {
|
||||
public int compare(K k1, K k2) {
|
||||
int compare = map.get(k2).compareTo(map.get(k1));
|
||||
if (compare == 0) return 1;
|
||||
else return compare;
|
||||
}
|
||||
};
|
||||
Map<K, V> sortedByValues = new TreeMap<K, V>(valueComparator);
|
||||
sortedByValues.putAll(map);
|
||||
return sortedByValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts map by value from higest to lowest
|
||||
* @param <K> - map key
|
||||
* @param <V> - map value
|
||||
* @param map - map to sort
|
||||
* @return sorted map
|
||||
*/
|
||||
private <K> Map<K, AtomicInteger> sortByValue1(Map<K, AtomicInteger> map) {
|
||||
Comparator<K> valueComparator = new Comparator<K>() {
|
||||
public int compare(K k1, K k2) {
|
||||
int compare = ((Comparable<Integer>)map.get(k2).get()).compareTo(map.get(k1).get());
|
||||
if (compare == 0) return 1;
|
||||
else return compare;
|
||||
}
|
||||
};
|
||||
Map<K, AtomicInteger> sortedByValues = new TreeMap<K, AtomicInteger>(valueComparator);
|
||||
sortedByValues.putAll(map);
|
||||
return sortedByValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts keys by map nested values from highest to lowest and returns sorted list of keys
|
||||
* @param <K> - map key
|
||||
* @param <L> - nested map key
|
||||
* @param map - map to sort
|
||||
* @return list of keys sorted from highest usage to lowest
|
||||
*/
|
||||
private <K, L> List<K> sortKeys(Map<K, Map<L, Long>> map){
|
||||
Map<K, Long> simplified = new LinkedHashMap<K, Long>();
|
||||
for (Entry<K, Map<L, Long>> entry : map.entrySet()) {
|
||||
simplified.put(entry.getKey(), sumValues(entry.getValue()));
|
||||
}
|
||||
simplified = sortByValue(simplified);
|
||||
List<K> sortedKeys = new ArrayList<K>();
|
||||
for (K key : simplified.keySet()) {
|
||||
sortedKeys.add(key);
|
||||
}
|
||||
return sortedKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns summary of all values in a map
|
||||
* @param map - map to sum up values of
|
||||
* @return summary of values
|
||||
*/
|
||||
private long sumValues(Map<?, Long> map) {
|
||||
long total = 0;
|
||||
for (Long value : map.values()) {
|
||||
total += value;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds cpu time to specified feature and usage type
|
||||
* @param feature - feature to add time to
|
||||
* @param type - usage to add time to of the feature
|
||||
* @param nanoseconds - time to add
|
||||
*/
|
||||
public void addTime(Object feature, UsageType type, long nanoseconds) {
|
||||
Map<UsageType, AtomicLong> usage = featureUsageCurrent.get(feature);
|
||||
if (usage == null) {
|
||||
usage = new ConcurrentHashMap<UsageType, AtomicLong>();
|
||||
featureUsageCurrent.put(feature, usage);
|
||||
}
|
||||
try {
|
||||
if (!usage.containsKey(type)) {
|
||||
usage.put(type, new AtomicLong());
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
//java.lang.NullPointerException: null
|
||||
//at java.base/java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
|
||||
//at java.base/java.util.concurrent.ConcurrentHashMap.containsKey(ConcurrentHashMap.java:964)
|
||||
return;
|
||||
}
|
||||
AtomicLong current = usage.get(type);
|
||||
if (current == null) {
|
||||
usage.put(type, new AtomicLong(nanoseconds));
|
||||
} else {
|
||||
current.addAndGet(nanoseconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds placeholder time to defined placeholder
|
||||
* @param placeholder - placeholder to add time to
|
||||
* @param nanoseconds - time to add
|
||||
*/
|
||||
public void addPlaceholderTime(String placeholder, long nanoseconds) {
|
||||
addTime(placeholderUsageCurrent, placeholder, nanoseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds placeholder time to defined placeholder
|
||||
* @param placeholder - placeholder to add time to
|
||||
* @param nanoseconds - time to add
|
||||
*/
|
||||
public void addBridgePlaceholderTime(String placeholder, long nanoseconds) {
|
||||
addTime(bridgePlaceholderUsageCurrent, placeholder, nanoseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds method time to defined method
|
||||
* @param method - method to add time to
|
||||
* @param nanoseconds - time to add
|
||||
*/
|
||||
public void addMethodTime(String method, long nanoseconds) {
|
||||
addTime(methodUsageCurrent, method, nanoseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds used time to specified key into specified map
|
||||
* @param map - map to add usage to
|
||||
* @param key - usage key
|
||||
* @param time - nanoseconds the task took
|
||||
*/
|
||||
private void addTime(Map<String, AtomicLong> map, String key, long time) {
|
||||
AtomicLong current = map.get(key);
|
||||
if (current == null) {
|
||||
map.put(key, new AtomicLong(time));
|
||||
} else {
|
||||
current.addAndGet(time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments packets sent by 1 of specified feature
|
||||
* @param feature - feature to increment packet counter of
|
||||
*/
|
||||
public void packetSent(Object feature) {
|
||||
AtomicInteger current = packetsCurrent.get(feature);
|
||||
if (current == null) {
|
||||
packetsCurrent.put(feature, new AtomicInteger(1));
|
||||
} else {
|
||||
current.addAndGet(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-15
@@ -28,28 +28,19 @@ public enum TabFeature {
|
||||
PACKET_SERIALIZING("Packet serializing"),
|
||||
TABLIST_LAYOUT("Tablist layout"),
|
||||
PLUGIN_MESSAGE_HANDLING("Plugin message handling"),
|
||||
OTHER("Other"),
|
||||
ADDON_FEATURE_1(null),
|
||||
ADDON_FEATURE_2(null),
|
||||
ADDON_FEATURE_3(null),
|
||||
ADDON_FEATURE_4(null),
|
||||
ADDON_FEATURE_5(null),
|
||||
ADDON_FEATURE_6(null),
|
||||
ADDON_FEATURE_7(null),
|
||||
ADDON_FEATURE_8(null),
|
||||
ADDON_FEATURE_9(null),
|
||||
ADDON_FEATURE_10(null);
|
||||
PING_SPOOF("Ping spoof"),
|
||||
OTHER("Other");
|
||||
|
||||
//user-friendly name to be used in /tab cpu
|
||||
private String displayName;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given display name
|
||||
* @param displayName - name to display in /tab cpu
|
||||
*/
|
||||
private TabFeature(String displayName){
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
+8
-5
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.cpu;
|
||||
|
||||
/**
|
||||
* Enum containing all reasons features require CPU time
|
||||
* This includes events, packets, repeating tasks and other
|
||||
*/
|
||||
public enum UsageType {
|
||||
|
||||
@@ -11,25 +12,27 @@ public enum UsageType {
|
||||
WORLD_SWITCH_EVENT("PlayerChangedWorldEvent"),
|
||||
PLAYER_TOGGLE_SNEAK_EVENT("PlayerToggleSneakEvent"),
|
||||
PLAYER_MOVE_EVENT("PlayerMoveEvent"),
|
||||
PLAYER_TELEPORT_EVENT("PlayerTeleportEvent"),
|
||||
PLAYER_RESPAWN_EVENT("PlayerRespawnEvent"),
|
||||
COMMAND_PREPROCESS("PlayerCommandPreprocessEvent"),
|
||||
PLUGIN_MESSAGE_EVENT("PluginMessageEvent"),
|
||||
PLAYER_CHAT_EVENT("AsyncPlayerChatEvent"),
|
||||
PLAYER_DEATH_EVENT("PlayerDeathEvent"),
|
||||
|
||||
//packets
|
||||
ANTI_OVERRIDE("Anti override"),
|
||||
PACKET_READING("Packet reading"),
|
||||
PACKET_READING_IN("Packet reading (in)"),
|
||||
PACKET_READING_OUT("Packet reading (out)"),
|
||||
PACKET_ENTITY_MOVE("PacketPlayOutEntity"),
|
||||
PACKET_PLAYER_INFO("PacketPlayOutPlayerInfo"),
|
||||
PACKET_LOGIN("PacketLogin"),
|
||||
PACKET_DISPLAY_OBJECTIVE("PacketPlayOutScoreboardDisplayObjective"),
|
||||
PACKET_OBJECTIVE("PacketPlayOutScoreboardObjective"),
|
||||
PACKET_HEADER_FOOTER("PacketPlayOutPlayerListHeaderFooter"),
|
||||
PACKET_NAMED_ENTITY_SPAWN("PacketPlayOutNamedEntitySpawn"),
|
||||
PACKET_ENTITY_DESTROY("PacketPlayOutEntityDestroy"),
|
||||
|
||||
//nametags
|
||||
REFRESHING_COLLISION("Refreshing collision rule"),
|
||||
REFRESHING_NAMETAG_VISIBILITY("Refreshing nametag visibility"),
|
||||
REFRESHING_NAMETAG_VISIBILITY_AND_COLLISION("Refreshing nametag visibility and collision"),
|
||||
REFRESHING_TEAM_NAME("Refreshing team name"),
|
||||
|
||||
//bossbar
|
||||
@@ -38,7 +41,7 @@ public enum UsageType {
|
||||
//other
|
||||
REFRESHING("Refreshing"),
|
||||
REPEATING_TASK("Repeating task"),
|
||||
TRACKING_VEHICLES("Tracking vehicles"),
|
||||
TICKING_VEHICLES("Ticking vehicles"),
|
||||
v1_8_0_BUG_COMPENSATION("Compensating for 1.8.0 bugs");
|
||||
|
||||
//user-friendly name to be used in /tab cpu
|
||||
+58
-101
@@ -4,15 +4,11 @@ import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
import me.neznamy.tab.shared.features.types.Loadable;
|
||||
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
|
||||
import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
@@ -20,7 +16,7 @@ import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
/**
|
||||
* Additional code for Playerlist class to secure alignment
|
||||
*/
|
||||
public class AlignedSuffix implements Loadable, JoinEventListener, QuitEventListener, WorldChangeListener{
|
||||
public class AlignedSuffix implements QuitEventListener, WorldChangeListener {
|
||||
|
||||
private TAB tab;
|
||||
private int maxWidth;
|
||||
@@ -32,39 +28,28 @@ public class AlignedSuffix implements Loadable, JoinEventListener, QuitEventList
|
||||
this.tab = tab;
|
||||
this.playerlist = playerlist;
|
||||
loadWidthsFromFile();
|
||||
boolean save = false;
|
||||
Map<Integer, Integer> extraWidths = tab.getConfiguration().premiumconfig.getConfigurationSection("extra-character-widths");
|
||||
for (Integer entry : new HashSet<>(extraWidths.keySet())) {
|
||||
char c = (char)(int)entry;
|
||||
int width = (int)extraWidths.get(entry);
|
||||
if (widths.containsKey(c)) {
|
||||
extraWidths.remove((int)c);
|
||||
tab.print('2', "Deleting character width of " + (int)c + " from extra-character-widths because it already exists inside the plugin.");
|
||||
save = true;
|
||||
} else {
|
||||
widths.put(c, (byte)width);
|
||||
}
|
||||
}
|
||||
if (save) tab.getConfiguration().premiumconfig.save();
|
||||
tab.debug("Loaded " + widths.size() + " character widths.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all predefined widths from included widths.txt file
|
||||
*/
|
||||
private void loadWidthsFromFile() {
|
||||
try {
|
||||
InputStream input = getClass().getClassLoader().getResourceAsStream("widths.txt");
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(input));
|
||||
String line;
|
||||
int characterId = 1;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.isEmpty()) continue;
|
||||
String[] arr = line.split(":");
|
||||
widths.put((char)Integer.parseInt(arr[0]), (byte)Integer.parseInt(arr[1]));
|
||||
widths.put((char)characterId++, (byte)Float.parseFloat(line));
|
||||
}
|
||||
br.close();
|
||||
} catch (Exception ex) {
|
||||
tab.getErrorManager().criticalError("Failed to read character widths from file", ex);
|
||||
}
|
||||
}
|
||||
public String fixTextWidth(TabPlayer player, String prefixAndName, String suffix) {
|
||||
int playerNameWidth = getTextWidth(IChatBaseComponent.fromColoredText(prefixAndName + suffix));
|
||||
|
||||
public String formatNameAndUpdateLeader(TabPlayer player, TabPlayer viewer) {
|
||||
int playerNameWidth = getTextWidth(IChatBaseComponent.fromColoredText(player.getProperty("tabprefix").getFormat(null) + player.getProperty("customtabname").getFormat(null) + player.getProperty("tabsuffix").getFormat(null)));
|
||||
if (player == maxPlayer && playerNameWidth < maxWidth) {
|
||||
maxWidth = playerNameWidth;
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
@@ -74,12 +59,17 @@ public class AlignedSuffix implements Loadable, JoinEventListener, QuitEventList
|
||||
maxPlayer = all;
|
||||
}
|
||||
}
|
||||
updateAllNames(null, UsageType.PACKET_READING);
|
||||
updateAllNames(null);
|
||||
} else if (playerNameWidth > maxWidth) {
|
||||
maxWidth = playerNameWidth;
|
||||
maxPlayer = player;
|
||||
updateAllNames(player, UsageType.PACKET_READING);
|
||||
updateAllNames(player);
|
||||
}
|
||||
return formatName(player.getProperty("tabprefix").getFormat(viewer) + player.getProperty("customtabname").getFormat(viewer), player.getProperty("tabsuffix").getFormat(viewer));
|
||||
}
|
||||
|
||||
public String formatName(String prefixAndName, String suffix) {
|
||||
int playerNameWidth = getTextWidth(IChatBaseComponent.fromColoredText(prefixAndName + suffix));
|
||||
String newFormat = prefixAndName + "\u00a7r";
|
||||
try {
|
||||
newFormat += buildSpaces(maxWidth + 12 - playerNameWidth);
|
||||
@@ -90,116 +80,83 @@ public class AlignedSuffix implements Loadable, JoinEventListener, QuitEventList
|
||||
newFormat += tab.getPlaceholderManager().getLastColors(prefixAndName) + suffix;
|
||||
return newFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text width of characters in given component
|
||||
* @param component - component to get width of
|
||||
* @return text width of characters in given component
|
||||
*/
|
||||
private int getTextWidth(IChatBaseComponent component) {
|
||||
int width = 0;
|
||||
if (component.getText() != null) {
|
||||
for (Character c : component.getText().toCharArray()) {
|
||||
if (widths.containsKey(c)) {
|
||||
int localWidth = widths.get(c);
|
||||
if (component.isBold()) {
|
||||
width += localWidth+1;
|
||||
} else {
|
||||
width += localWidth;
|
||||
}
|
||||
if (component.isBold()) {
|
||||
width += widths.get(c)+2;
|
||||
} else {
|
||||
tab.getErrorManager().oneTimeConsoleError("Unknown character " + c + " (" + ((int)c) + ") found when aligning tabsuffix. Configure it using /tab width <character|ID>.");
|
||||
width += widths.get(c)+1;
|
||||
}
|
||||
}
|
||||
//there is 1 pixel space between characters, but not after last one
|
||||
width += component.getText().length()-1;
|
||||
}
|
||||
|
||||
if (component.getExtra() != null) {
|
||||
for (IChatBaseComponent extra : component.getExtra()) {
|
||||
int extraWidth = getTextWidth(extra);
|
||||
//ignoring empty components
|
||||
if (extraWidth > 0) {
|
||||
width += extraWidth + 1; //1 pixel space between characters
|
||||
}
|
||||
}
|
||||
for (IChatBaseComponent extra : component.getExtra()) {
|
||||
width += getTextWidth(extra);
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns width of player's tablist name format
|
||||
* @param p - player to get width for
|
||||
* @return width of player's tablist name format
|
||||
*/
|
||||
private int getPlayerNameWidth(TabPlayer p) {
|
||||
String format = p.getProperty("tabprefix").get() + p.getProperty("customtabname").get() + p.getProperty("tabsuffix").get();
|
||||
String format = p.getProperty("tabprefix").getFormat(null) + p.getProperty("customtabname").getFormat(null) + p.getProperty("tabsuffix").getFormat(null);
|
||||
return getTextWidth(IChatBaseComponent.fromColoredText(format));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a combination of normal and bold spaces to build exactly the requested amount of pixels.
|
||||
* Must be at least 12 as lower numbers cannot always be built using numbers 4 (normal space + 1 pixel) and 5 (bold space + 1 pixel)
|
||||
* Returns the result string with normal then bold spaces, such as " &l "
|
||||
* Returns the result string with normal then bold spaces, such as " &l &r"
|
||||
* @param pixelWidth - amount of pixels to be built
|
||||
* @return string consisting of spaces and &l &r (if needed)
|
||||
* @return string consisting of spaces and &l &r
|
||||
* @throws IllegalArgumentException if pixelWidth is < 12
|
||||
*/
|
||||
private String buildSpaces(int pixelWidth) {
|
||||
if (pixelWidth < 12) throw new IllegalArgumentException("Cannot build space lower than 12 pixels wide");
|
||||
int pixelsLeft = pixelWidth;
|
||||
int boldSpaces = 0;
|
||||
int normalSpaces = 0;
|
||||
while (pixelsLeft % 4 != 0) {
|
||||
pixelsLeft -= 5;
|
||||
boldSpaces++;
|
||||
}
|
||||
while (pixelsLeft > 0) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
while (pixelsLeft % 5 != 0) {
|
||||
pixelsLeft -= 4;
|
||||
normalSpaces++;
|
||||
output.append(" ");
|
||||
}
|
||||
String output = "";
|
||||
for (int i=0; i<normalSpaces; i++) {
|
||||
output += " ";
|
||||
}
|
||||
if (boldSpaces > 0) {
|
||||
output += "&l";
|
||||
for (int i=0; i<boldSpaces; i++) {
|
||||
output += " ";
|
||||
}
|
||||
output += "&r";
|
||||
}
|
||||
return tab.getPlaceholderManager().color(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
recalculateMaxWidth(null);
|
||||
}
|
||||
@Override
|
||||
public void unload() {
|
||||
//nothing to do here, Playerlist feature handles unloading
|
||||
}
|
||||
@Override
|
||||
public void onJoin(TabPlayer p) {
|
||||
int width = getPlayerNameWidth(p);
|
||||
if (width > maxWidth) {
|
||||
maxWidth = width;
|
||||
maxPlayer = p;
|
||||
updateAllNames(null, UsageType.PLAYER_JOIN_EVENT);
|
||||
output.append("\u00a7l");
|
||||
while (pixelsLeft > 0) {
|
||||
pixelsLeft -= 5;
|
||||
output.append(" ");
|
||||
}
|
||||
output.append("\u00a7r");
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer p) {
|
||||
if (maxPlayer == p && recalculateMaxWidth(p)) {
|
||||
updateAllNames(null, UsageType.PLAYER_QUIT_EVENT);
|
||||
if (recalculateMaxWidth(p)) {
|
||||
updateAllNames(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorldChange(TabPlayer p, String from, String to) {
|
||||
if (maxPlayer == p && recalculateMaxWidth(null)) {
|
||||
updateAllNames(null, UsageType.WORLD_SWITCH_EVENT);
|
||||
if (recalculateMaxWidth(null)) {
|
||||
updateAllNames(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAllNames(TabPlayer exception, UsageType usage) {
|
||||
tab.getCPUManager().runMeasuredTask("aligning tabsuffix", TabFeature.ALIGNED_TABSUFFIX, usage, new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (all == exception) continue;
|
||||
playerlist.refresh(all, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
private void updateAllNames(TabPlayer exception) {
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (all == exception) continue;
|
||||
playerlist.refresh(all, true);
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if max changed, false if not
|
||||
+2
-1
@@ -42,6 +42,7 @@ public class BelowName implements Loadable, JoinEventListener, WorldChangeListen
|
||||
rawText = tab.getConfiguration().config.getString("classic-vanilla-belowname.text", "Health");
|
||||
disabledWorlds = tab.getConfiguration().config.getStringList("disable-features-in-"+tab.getPlatform().getSeparatorType()+"s.belowname", Arrays.asList("disabled" + tab.getPlatform().getSeparatorType()));
|
||||
refreshUsedPlaceholders();
|
||||
tab.debug(String.format("Loaded BelowName feature with parameters number=%s, text=%s, disabledWorlds=%s", rawNumber, rawText, disabledWorlds));
|
||||
tab.getFeatureManager().registerFeature("belowname-text", new Refreshable() {
|
||||
|
||||
private List<String> usedPlaceholders;
|
||||
@@ -122,7 +123,7 @@ public class BelowName implements Loadable, JoinEventListener, WorldChangeListen
|
||||
}
|
||||
|
||||
private int getValue(TabPlayer p) {
|
||||
return tab.getErrorManager().parseInteger(p.getProperty(numberPropertyName).updateAndGet(), 0, "belowname");
|
||||
return tab.getErrorManager().parseInteger(p.getProperty(numberPropertyName).updateAndGet(), 0, "belowname number");
|
||||
}
|
||||
|
||||
@Override
|
||||
+5
-6
@@ -14,19 +14,18 @@ import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
*/
|
||||
public class GhostPlayerFix implements QuitEventListener {
|
||||
|
||||
private TAB tab;
|
||||
|
||||
public GhostPlayerFix(TAB tab) {
|
||||
this.tab = tab;
|
||||
public GhostPlayerFix() {
|
||||
TAB.getInstance().debug("Loaded GhostPlayerFix feature");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
tab.getCPUManager().runTaskLater(200, "removing players", getFeatureType(), UsageType.PLAYER_QUIT_EVENT, new Runnable() {
|
||||
TAB.getInstance().getCPUManager().runTaskLater(500, "removing players", getFeatureType(), UsageType.PLAYER_QUIT_EVENT, new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (TAB.getInstance().getPlayer(disconnectedPlayer.getName()) != null) return; //player reconnected meanwhile, not removing then
|
||||
for (TabPlayer all : TAB.getInstance().getPlayers()) {
|
||||
if (all == disconnectedPlayer) continue;
|
||||
all.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, new PlayerInfoData(disconnectedPlayer.getUniqueId())), getFeatureType());
|
||||
}
|
||||
+47
-12
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -14,6 +15,7 @@ import me.neznamy.tab.shared.features.types.event.JoinEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.PlayerInfoPacketListener;
|
||||
import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
@@ -25,12 +27,14 @@ import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
public class GlobalPlayerlist implements Loadable, JoinEventListener, QuitEventListener, WorldChangeListener, PlayerInfoPacketListener{
|
||||
|
||||
private TAB tab;
|
||||
public final String PREMIUMVANISH_SEE_VANISHED_PERMISSION = "pv.see";
|
||||
|
||||
private List<String> spyServers;
|
||||
private Map<String, List<String>> sharedServers;
|
||||
private boolean displayAsSpectators;
|
||||
public boolean vanishedAsSpectators;
|
||||
private boolean isolateUnlistedServers;
|
||||
|
||||
private List<TabPlayer> vanishedPlayers = new ArrayList<TabPlayer>();
|
||||
|
||||
public GlobalPlayerlist(TAB tab) {
|
||||
this.tab = tab;
|
||||
@@ -44,17 +48,45 @@ public class GlobalPlayerlist implements Loadable, JoinEventListener, QuitEventL
|
||||
vanishedAsSpectators = tab.getConfiguration().config.getBoolean("global-playerlist.display-vanished-players-as-spectators", true);
|
||||
isolateUnlistedServers = tab.getConfiguration().config.getBoolean("global-playerlist.isolate-unlisted-servers", false);
|
||||
for (TabPlayer displayed : tab.getPlayers()) {
|
||||
PacketPlayOutPlayerInfo displayedAddPacket = getAddPacket(displayed);
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
if (viewer.getWorldName().equals(displayed.getWorldName())) continue;
|
||||
if (shouldSee(viewer, displayed)) viewer.sendCustomPacket(displayedAddPacket, getFeatureType());
|
||||
if (shouldSee(viewer, displayed)) viewer.sendCustomPacket(getAddPacket(displayed, viewer), getFeatureType());
|
||||
}
|
||||
}
|
||||
tab.debug(String.format("Loaded GlobalPlayerlist feature with parameters spyServers=%s, sharedServers=%s, displayAsSpectators=%s, vanishedAsSpectators=%s, isolateUnlistedServers=%s",
|
||||
spyServers, sharedServers, displayAsSpectators, vanishedAsSpectators, isolateUnlistedServers));
|
||||
startTask();
|
||||
}
|
||||
|
||||
private void startTask() {
|
||||
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing vanished players", getFeatureType(), UsageType.REPEATING_TASK, () -> {
|
||||
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
if (vanishedPlayers.contains(p) && !p.isVanished()) {
|
||||
vanishedPlayers.remove(p);
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
if (viewer == p) continue;
|
||||
if (shouldSee(viewer, p)) {
|
||||
viewer.sendCustomPacket(getAddPacket(p, viewer), getFeatureType());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!vanishedPlayers.contains(p) && p.isVanished()) {
|
||||
vanishedPlayers.add(p);
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (all == p) continue;
|
||||
if (!shouldSee(all, p)) {
|
||||
all.sendCustomPacket(getRemovePacket(p), getFeatureType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean shouldSee(TabPlayer viewer, TabPlayer displayed) {
|
||||
if (displayed == viewer) return true;
|
||||
if (displayed.isVanished() && !viewer.hasPermission(PREMIUMVANISH_SEE_VANISHED_PERMISSION)) return false;
|
||||
if (displayed.isVanished() && !viewer.hasPermission("tab.seevanished")) return false;
|
||||
if (spyServers.contains(viewer.getWorldName())) return true;
|
||||
return getServerGroup(viewer.getWorldName()).equals(getServerGroup(displayed.getWorldName()));
|
||||
}
|
||||
@@ -78,14 +110,13 @@ public class GlobalPlayerlist implements Loadable, JoinEventListener, QuitEventL
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
PacketPlayOutPlayerInfo addConnected = getAddPacket(connectedPlayer);
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (all == connectedPlayer) continue;
|
||||
if (shouldSee(all, connectedPlayer)) {
|
||||
all.sendCustomPacket(addConnected, getFeatureType());
|
||||
all.sendCustomPacket(getAddPacket(connectedPlayer, all), getFeatureType());
|
||||
}
|
||||
if (shouldSee(connectedPlayer, all)) {
|
||||
connectedPlayer.sendCustomPacket(getAddPacket(all), getFeatureType());
|
||||
connectedPlayer.sendCustomPacket(getAddPacket(all, connectedPlayer), getFeatureType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,17 +136,16 @@ public class GlobalPlayerlist implements Loadable, JoinEventListener, QuitEventL
|
||||
|
||||
@Override
|
||||
public void onWorldChange(TabPlayer p, String from, String to) {
|
||||
PacketPlayOutPlayerInfo addChanged = getAddPacket(p);
|
||||
PacketPlayOutPlayerInfo removeChanged = getRemovePacket(p);
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (all == p) continue;
|
||||
if (shouldSee(all, p)) {
|
||||
all.sendCustomPacket(addChanged, getFeatureType());
|
||||
all.sendCustomPacket(getAddPacket(p, all), getFeatureType());
|
||||
} else {
|
||||
all.sendCustomPacket(removeChanged, getFeatureType());
|
||||
}
|
||||
if (shouldSee(p, all)) {
|
||||
p.sendCustomPacket(getAddPacket(all), getFeatureType());
|
||||
p.sendCustomPacket(getAddPacket(all, p), getFeatureType());
|
||||
} else {
|
||||
p.sendCustomPacket(getRemovePacket(all), getFeatureType());
|
||||
}
|
||||
@@ -128,9 +158,14 @@ public class GlobalPlayerlist implements Loadable, JoinEventListener, QuitEventL
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, data);
|
||||
}
|
||||
|
||||
public PacketPlayOutPlayerInfo getAddPacket(TabPlayer p) {
|
||||
public PacketPlayOutPlayerInfo getAddPacket(TabPlayer p, TabPlayer viewer) {
|
||||
IChatBaseComponent format = null;
|
||||
Playerlist playerlist = (Playerlist) tab.getFeatureManager().getFeature("playerlist");
|
||||
if (playerlist != null) {
|
||||
format = playerlist.getTabFormat(p, viewer);
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, new PlayerInfoData(p.getName(), p.getTablistUUID(), p.getSkin(),
|
||||
(int)p.getPing(), vanishedAsSpectators && p.isVanished() ? EnumGamemode.SPECTATOR : EnumGamemode.CREATIVE, null));
|
||||
(int)p.getPing(), vanishedAsSpectators && p.isVanished() ? EnumGamemode.SPECTATOR : EnumGamemode.CREATIVE, format));
|
||||
}
|
||||
|
||||
@Override
|
||||
+9
-4
@@ -1,9 +1,11 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
@@ -23,13 +25,16 @@ public class GroupRefresher implements Feature {
|
||||
this.tab = tab;
|
||||
usePrimaryGroup = tab.getConfiguration().config.getBoolean("use-primary-group", true);
|
||||
groupsByPermissions = tab.getConfiguration().config.getBoolean("assign-groups-by-permissions", false);
|
||||
primaryGroupFindingList = tab.getConfiguration().config.getStringList("primary-group-finding-list", Arrays.asList("Owner", "Admin", "Helper", "default"));
|
||||
primaryGroupFindingList = new ArrayList<String>();
|
||||
for (Object group : tab.getConfiguration().config.getStringList("primary-group-finding-list", Arrays.asList("Owner", "Admin", "Helper", "default"))){
|
||||
primaryGroupFindingList.add(group.toString());
|
||||
}
|
||||
tab.getCPUManager().startRepeatingMeasuredTask(1000, "refreshing permission groups", getFeatureType(), UsageType.REPEATING_TASK, new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
p.setGroup(detectPermissionGroup(p), true);
|
||||
((ITabPlayer) p).setGroup(detectPermissionGroup(p), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -57,9 +62,9 @@ public class GroupRefresher implements Feature {
|
||||
try {
|
||||
String[] playerGroups = tab.getPermissionPlugin().getAllGroups(p);
|
||||
if (playerGroups != null && playerGroups.length > 0) {
|
||||
for (Object groupFromList : primaryGroupFindingList) {
|
||||
for (String groupFromList : primaryGroupFindingList) {
|
||||
for (String playerGroup : playerGroups) {
|
||||
if (playerGroup.equalsIgnoreCase(groupFromList + "")) {
|
||||
if (playerGroup.equalsIgnoreCase(groupFromList)) {
|
||||
return playerGroup;
|
||||
}
|
||||
}
|
||||
+24
-16
@@ -11,14 +11,13 @@ import me.neznamy.tab.shared.features.types.Loadable;
|
||||
import me.neznamy.tab.shared.features.types.Refreshable;
|
||||
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.HeaderFooterPacketListener;
|
||||
import me.neznamy.tab.shared.packets.IChatBaseComponent;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerListHeaderFooter;
|
||||
|
||||
/**
|
||||
* Feature handler for header and footer
|
||||
*/
|
||||
public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeListener, Refreshable, HeaderFooterPacketListener {
|
||||
public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeListener, Refreshable {
|
||||
|
||||
private TAB tab;
|
||||
private List<String> usedPlaceholders;
|
||||
@@ -28,6 +27,7 @@ public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeLis
|
||||
this.tab = tab;
|
||||
disabledWorlds = tab.getConfiguration().config.getStringList("disable-features-in-"+tab.getPlatform().getSeparatorType()+"s.header-footer", Arrays.asList("disabled" + tab.getPlatform().getSeparatorType()));
|
||||
refreshUsedPlaceholders();
|
||||
tab.debug(String.format("Loaded HeaderFooter feature with parameters disabledWorlds=%s", disabledWorlds));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,7 +56,26 @@ public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeLis
|
||||
if (isDisabledWorld(disabledWorlds, p.getWorldName())) {
|
||||
if (!isDisabledWorld(disabledWorlds, from)) p.sendCustomPacket(new PacketPlayOutPlayerListHeaderFooter("", ""), getFeatureType());
|
||||
} else {
|
||||
refresh(p, true);
|
||||
boolean refresh = false;
|
||||
String headerAppend = getValue(p, "headerappend");
|
||||
if (headerAppend.length() > 0) headerAppend = "\n\u00a7r" + headerAppend;
|
||||
String header = getValue(p, "header") + headerAppend;
|
||||
if (!p.getProperty("header").getOriginalRawValue().equals(header)) {
|
||||
p.setProperty("header", header);
|
||||
refresh = true;
|
||||
}
|
||||
|
||||
String footerAppend = getValue(p, "footerappend");
|
||||
if (footerAppend.length() > 0) footerAppend = "\n\u00a7r" + footerAppend;
|
||||
String footer = getValue(p, "footer") + footerAppend;
|
||||
if (!p.getProperty("footer").getOriginalRawValue().equals(footer)) {
|
||||
p.setProperty("footer", footer);
|
||||
refresh = true;
|
||||
}
|
||||
if (refresh) {
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent.optimizedComponent(p.getProperty("header").updateAndGet()),
|
||||
IChatBaseComponent.optimizedComponent(p.getProperty("footer").updateAndGet())), getFeatureType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,9 +91,8 @@ public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeLis
|
||||
p.setProperty("footer", getValue(p, "footer") + footerAppend);
|
||||
}
|
||||
if (isDisabledWorld(disabledWorlds, p.getWorldName()) || p.getVersion().getMinorVersion() < 8) return;
|
||||
IChatBaseComponent header = IChatBaseComponent.optimizedComponent(p.getProperty("header").updateAndGet());
|
||||
if (header.getText() != null) header.setText("\u00a70\u00a71\u00a72\u00a7r" + header.getText());
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerListHeaderFooter(header, IChatBaseComponent.optimizedComponent(p.getProperty("footer").updateAndGet())), getFeatureType());
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent.optimizedComponent(p.getProperty("header").updateAndGet()),
|
||||
IChatBaseComponent.optimizedComponent(p.getProperty("footer").updateAndGet())), getFeatureType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,14 +130,4 @@ public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeLis
|
||||
public TabFeature getFeatureType() {
|
||||
return TabFeature.HEADER_FOOTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPacketSend(TabPlayer packetReciver, PacketPlayOutPlayerListHeaderFooter packet) {
|
||||
if (!isDisabledWorld(disabledWorlds, packetReciver.getWorldName()) && packet.header.getText() != null &&
|
||||
!packet.header.getText().startsWith("\u00a70\u00a71\u00a72\u00a7r")) {
|
||||
tab.getErrorManager().printError("Some plugin just tried to send header " + packet.header.toString() + " and footer " + packet.footer.toString(), null, false, tab.getErrorManager().antiOverrideLog);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,286 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.PacketAPI;
|
||||
import me.neznamy.tab.shared.Property;
|
||||
import me.neznamy.tab.shared.ProtocolVersion;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.cpu.UsageType;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import me.neznamy.tab.shared.features.types.Loadable;
|
||||
import me.neznamy.tab.shared.features.types.Refreshable;
|
||||
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
|
||||
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
|
||||
import me.neznamy.tab.shared.features.types.packet.LoginPacketListener;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardTeam;
|
||||
|
||||
public class NameTag implements Loadable, Refreshable, LoginPacketListener, QuitEventListener, WorldChangeListener, JoinEventListener {
|
||||
|
||||
protected TAB tab;
|
||||
protected Set<String> usedPlaceholders;
|
||||
protected List<String> disabledWorlds;
|
||||
private boolean collisionRule;
|
||||
private List<String> revertedCollision;
|
||||
private boolean invisibleNametags;
|
||||
protected Set<String> invisiblePlayers = new HashSet<String>();
|
||||
public Sorting sorting;
|
||||
protected Map<TabPlayer, Boolean> collision = new HashMap<TabPlayer, Boolean>();
|
||||
|
||||
public NameTag(TAB tab) {
|
||||
this.tab = tab;
|
||||
disabledWorlds = tab.getConfiguration().config.getStringList("disable-features-in-"+tab.getPlatform().getSeparatorType()+"s.nametag", Arrays.asList("disabled" + tab.getPlatform().getSeparatorType()));
|
||||
collisionRule = tab.getConfiguration().config.getBoolean("enable-collision", true);
|
||||
revertedCollision = tab.getConfiguration().config.getStringList("revert-collision-rule-in-" + tab.getPlatform().getSeparatorType()+"s", Arrays.asList("reverted" + tab.getPlatform().getSeparatorType()));
|
||||
invisibleNametags = tab.getConfiguration().config.getBoolean("invisible-nametags", false);
|
||||
sorting = new Sorting(tab, this);
|
||||
usedPlaceholders = new HashSet<>(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive("tagprefix", "tagsuffix"));
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
usedPlaceholders.addAll(tab.getPlaceholderManager().getUsedPlaceholderIdentifiersRecursive(
|
||||
p.getProperty("tagprefix").getCurrentRawValue(), p.getProperty("tagsuffix").getCurrentRawValue()));
|
||||
}
|
||||
tab.debug(String.format("Loaded NameTag feature with parameters collisionRule=%s, revertedCollision=%s, disabledWorlds=%s, invisibleNametags=%s",
|
||||
collisionRule, revertedCollision, disabledWorlds, invisibleNametags));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
((ITabPlayer) all).setTeamName(sorting.getTeamName(all));
|
||||
updateProperties(all);
|
||||
collision.put(all, true);
|
||||
if (all.hasInvisibilityPotion()) invisiblePlayers.add(all.getName());
|
||||
if (isDisabledWorld(all.getWorldName())) continue;
|
||||
registerTeam(all);
|
||||
}
|
||||
startRefreshingTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
if (!isDisabledWorld(p.getWorldName())) unregisterTeam(p);
|
||||
}
|
||||
}
|
||||
|
||||
public void startRefreshingTasks() {
|
||||
//workaround for a 1.8.x client-sided bug
|
||||
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing nametag visibility", TabFeature.NAMETAGS, UsageType.REFRESHING_NAMETAG_VISIBILITY_AND_COLLISION, new Runnable() {
|
||||
public void run() {
|
||||
//nametag visibility
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
if (!p.isLoaded() || isDisabledWorld(p.getWorldName())) continue;
|
||||
boolean invisible = p.hasInvisibilityPotion();
|
||||
if (invisible && !invisiblePlayers.contains(p.getName())) {
|
||||
invisiblePlayers.add(p.getName());
|
||||
updateTeamData(p);
|
||||
}
|
||||
if (!invisible && invisiblePlayers.contains(p.getName())) {
|
||||
invisiblePlayers.remove(p.getName());
|
||||
updateTeamData(p);
|
||||
}
|
||||
//cannot control collision rule on <1.9 servers in any way
|
||||
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) updateCollision(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isDisabledWorld(String world) {
|
||||
return isDisabledWorld(disabledWorlds, world);
|
||||
}
|
||||
|
||||
public Set<String> getInvisiblePlayers(){
|
||||
return invisiblePlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUsedPlaceholders() {
|
||||
return new ArrayList<>(usedPlaceholders);
|
||||
}
|
||||
|
||||
public void unregisterTeam(TabPlayer p) {
|
||||
if (p.hasTeamHandlingPaused()) return;
|
||||
if (p.getTeamName() == null) return;
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName()).setTeamOptions(69), TabFeature.NAMETAGS);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterTeam(TabPlayer p, TabPlayer viewer) {
|
||||
if (p.hasTeamHandlingPaused()) return;
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName()).setTeamOptions(69), TabFeature.NAMETAGS);
|
||||
}
|
||||
|
||||
public void registerTeam(TabPlayer p) {
|
||||
if (p.hasTeamHandlingPaused()) return;
|
||||
Property tagprefix = p.getProperty("tagprefix");
|
||||
Property tagsuffix = p.getProperty("tagsuffix");
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
String currentPrefix = tagprefix.getFormat(viewer);
|
||||
String currentSuffix = tagsuffix.getFormat(viewer);
|
||||
PacketAPI.registerScoreboardTeam(viewer, p.getTeamName(), currentPrefix, currentSuffix, getTeamVisibility(p, viewer), getCollision(p), Arrays.asList(p.getName()), null, TabFeature.NAMETAGS);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerTeam(TabPlayer p, TabPlayer viewer) {
|
||||
if (p.hasTeamHandlingPaused()) return;
|
||||
Property tagprefix = p.getProperty("tagprefix");
|
||||
Property tagsuffix = p.getProperty("tagsuffix");
|
||||
String replacedPrefix = tagprefix.getFormat(viewer);
|
||||
String replacedSuffix = tagsuffix.getFormat(viewer);
|
||||
PacketAPI.registerScoreboardTeam(viewer, p.getTeamName(), replacedPrefix, replacedSuffix, getTeamVisibility(p, viewer), getCollision(p), Arrays.asList(p.getName()), null, TabFeature.NAMETAGS);
|
||||
}
|
||||
|
||||
public void updateTeam(TabPlayer p) {
|
||||
if (p.getTeamName() == null) return; //player not loaded yet
|
||||
String newName = sorting.getTeamName(p);
|
||||
if (p.getTeamName().equals(newName)) {
|
||||
updateTeamData(p);
|
||||
} else {
|
||||
unregisterTeam(p);
|
||||
((ITabPlayer) p).setTeamName(newName);
|
||||
registerTeam(p);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTeamData(TabPlayer p) {
|
||||
Property tagprefix = p.getProperty("tagprefix");
|
||||
Property tagsuffix = p.getProperty("tagsuffix");
|
||||
for (TabPlayer viewer : tab.getPlayers()) {
|
||||
String currentPrefix = tagprefix.getFormat(viewer);
|
||||
String currentSuffix = tagsuffix.getFormat(viewer);
|
||||
boolean visible = getTeamVisibility(p, viewer);
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), currentPrefix, currentSuffix, visible?"always":"never", getCollision(p)?"always":"never", 69), TabFeature.NAMETAGS);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTeamData(TabPlayer p, TabPlayer viewer) {
|
||||
Property tagprefix = p.getProperty("tagprefix");
|
||||
Property tagsuffix = p.getProperty("tagsuffix");
|
||||
boolean visible = getTeamVisibility(p, viewer);
|
||||
String currentPrefix = tagprefix.getFormat(viewer);
|
||||
String currentSuffix = tagsuffix.getFormat(viewer);
|
||||
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), currentPrefix, currentSuffix, visible?"always":"never", getCollision(p)?"always":"never", 69), TabFeature.NAMETAGS);
|
||||
}
|
||||
|
||||
private void updateCollision(TabPlayer p) {
|
||||
if (TAB.getInstance().getFeatureManager().getNameTagFeature() == null) return;
|
||||
if (p.getCollisionRule() != null) {
|
||||
if (getCollision(p) != p.getCollisionRule()) {
|
||||
collision.put(p, p.getCollisionRule());
|
||||
updateTeamData(p);
|
||||
}
|
||||
} else {
|
||||
boolean collision = !p.isDisguised() && revertedCollision.contains(p.getWorldName()) ? !collisionRule : collisionRule;
|
||||
if (this.collision.get(p) == null || this.getCollision(p) != collision) {
|
||||
this.collision.put(p, collision);
|
||||
updateTeamData(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean getCollision(TabPlayer p) {
|
||||
if (p.getCollisionRule() != null) return p.getCollisionRule();
|
||||
if (!collision.containsKey(p)) {
|
||||
collision.put(p, revertedCollision.contains(p.getWorldName()) ? !collisionRule : collisionRule);
|
||||
}
|
||||
return collision.get(p);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onLoginPacket(TabPlayer packetReceiver) {
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (!all.isLoaded()) continue;
|
||||
if (!isDisabledWorld(all.getWorldName())) registerTeam(all, packetReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer refreshed, boolean force) {
|
||||
if (isDisabledWorld(refreshed.getWorldName())) return;
|
||||
boolean refresh;
|
||||
if (force) {
|
||||
updateProperties(refreshed);
|
||||
refresh = true;
|
||||
} else {
|
||||
boolean prefix = refreshed.getProperty("tagprefix").update();
|
||||
boolean suffix = refreshed.getProperty("tagsuffix").update();
|
||||
refresh = prefix || suffix;
|
||||
}
|
||||
|
||||
if (refresh) updateTeam(refreshed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
((ITabPlayer) connectedPlayer).setTeamName(sorting.getTeamName(connectedPlayer));
|
||||
updateProperties(connectedPlayer);
|
||||
collision.put(connectedPlayer, true);
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (!all.isLoaded()) continue; //avoiding NPE when 2 players join at once
|
||||
if (all == connectedPlayer) continue; //already registered 3 lines above
|
||||
if (!isDisabledWorld(all.getWorldName())) registerTeam(all, connectedPlayer);
|
||||
}
|
||||
if (isDisabledWorld(connectedPlayer.getWorldName())) return;
|
||||
registerTeam(connectedPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
if (!isDisabledWorld(disconnectedPlayer.getWorldName())) unregisterTeam(disconnectedPlayer);
|
||||
invisiblePlayers.remove(disconnectedPlayer.getName());
|
||||
collision.remove(disconnectedPlayer);
|
||||
for (TabPlayer all : tab.getPlayers()) {
|
||||
if (all == disconnectedPlayer) continue;
|
||||
all.showNametag(disconnectedPlayer.getUniqueId()); //clearing memory from API method
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorldChange(TabPlayer p, String from, String to) {
|
||||
updateProperties(p);
|
||||
if (isDisabledWorld(to) && !isDisabledWorld(from)) {
|
||||
unregisterTeam(p);
|
||||
} else if (!isDisabledWorld(to) && isDisabledWorld(from)) {
|
||||
registerTeam(p);
|
||||
} else {
|
||||
updateTeam(p);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshUsedPlaceholders() {
|
||||
usedPlaceholders = new HashSet<>(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive("tagprefix", "tagsuffix"));
|
||||
for (TabPlayer p : tab.getPlayers()) {
|
||||
usedPlaceholders.addAll(tab.getPlaceholderManager().getUsedPlaceholderIdentifiersRecursive(
|
||||
p.getProperty("tagprefix").getCurrentRawValue(), p.getProperty("tagsuffix").getCurrentRawValue()));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateProperties(TabPlayer p) {
|
||||
p.loadPropertyFromConfig("tagprefix");
|
||||
p.loadPropertyFromConfig("tagsuffix");
|
||||
}
|
||||
|
||||
public boolean getTeamVisibility(TabPlayer p, TabPlayer viewer) {
|
||||
return !p.hasHiddenNametag() && !p.hasHiddenNametag(viewer.getUniqueId()) &&
|
||||
!invisibleNametags && !invisiblePlayers.contains(p.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabFeature getFeatureType() {
|
||||
return TabFeature.NAMETAGS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.neznamy.tab.shared.features;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.cpu.TabFeature;
|
||||
import me.neznamy.tab.shared.features.types.Loadable;
|
||||
import me.neznamy.tab.shared.features.types.packet.PlayerInfoPacketListener;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
|
||||
/**
|
||||
* Sets ping of all players in the packet to configured value to prevent hacked clients from seeing exact ping value of each player
|
||||
*/
|
||||
public class PingSpoof implements PlayerInfoPacketListener, Loadable {
|
||||
|
||||
//fake ping value
|
||||
private int value;
|
||||
|
||||
/**
|
||||
* Constructs new instance and loads config options
|
||||
*/
|
||||
public PingSpoof() {
|
||||
value = TAB.getInstance().getConfiguration().config.getInt("ping-spoof.value", 0);
|
||||
TAB.getInstance().debug(String.format("Loaded PingSpoof feature with parameters value=%s", value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketSend(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
|
||||
if (info.action != EnumPlayerInfoAction.UPDATE_LATENCY && info.action != EnumPlayerInfoAction.ADD_PLAYER) return;
|
||||
for (PlayerInfoData playerInfoData : info.entries) {
|
||||
playerInfoData.latency = value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabFeature getFeatureType() {
|
||||
return TabFeature.PING_SPOOF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
updateAll(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
updateAll(true);
|
||||
}
|
||||
|
||||
private void updateAll(boolean realPing) {
|
||||
List<PlayerInfoData> list = new ArrayList<PlayerInfoData>();
|
||||
for (TabPlayer p : TAB.getInstance().getPlayers()) {
|
||||
list.add(new PlayerInfoData(p.getUniqueId(), realPing ? (int) p.getPing() : 0));
|
||||
}
|
||||
for (TabPlayer p : TAB.getInstance().getPlayers()) {
|
||||
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_LATENCY, list), getFeatureType());
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 259 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user