Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
189797aa73 | ||
|
|
1ae2ef7cad | ||
|
|
de14e14d33 | ||
|
|
19892c14c1 | ||
|
|
2e00c4c002 | ||
|
|
97aa367629 | ||
|
|
87cc9a030b | ||
|
|
8ba83d1cb1 | ||
|
|
9f0e60bd47 | ||
|
|
93b2a25b2e | ||
|
|
4242d7a839 | ||
|
|
0ddb99bd46 | ||
|
|
969672e3ad | ||
|
|
2423fbb150 | ||
|
|
a99928dbf5 | ||
|
|
12f5dd52b4 | ||
|
|
2bd105446e | ||
|
|
6d85875fde | ||
|
|
42810aa490 | ||
|
|
bd31521bd3 | ||
|
|
ec0deb8378 | ||
|
|
ae66b6e9a1 | ||
|
|
3b86becebd | ||
|
|
4a8d86825b | ||
|
|
54a3bd81b4 | ||
|
|
b6768cca99 | ||
|
|
60c414deed | ||
|
|
009283a83f | ||
|
|
4a430b97df | ||
|
|
722333da2e | ||
|
|
1215af5262 | ||
|
|
254088c37b | ||
|
|
cb2ce4acca | ||
|
|
872adca8fd | ||
|
|
f37cc0bdd8 | ||
|
|
4063578ac3 | ||
|
|
2528de7eeb | ||
|
|
37caed3123 | ||
|
|
27fe9dfb12 | ||
|
|
0169cbd4d2 | ||
|
|
ed760aec08 | ||
|
|
858cbcefeb | ||
|
|
8a87d581a9 | ||
|
|
6d642f4e51 | ||
|
|
aca63640bc | ||
|
|
1845a35315 | ||
|
|
cab5f89a95 | ||
|
|
1777d8bf01 | ||
|
|
91b0bfaa9d | ||
|
|
f1cf994d10 | ||
|
|
69957be933 | ||
|
|
b78eb5efb6 | ||
|
|
3577ad1964 | ||
|
|
4510491177 | ||
|
|
01b3a12377 | ||
|
|
64ae9863fb | ||
|
|
ecd315a407 | ||
|
|
678eed288e | ||
|
|
9510455cb1 | ||
|
|
e925ce23d0 | ||
|
|
c68cad6a1e | ||
|
|
01d6eda4fb | ||
|
|
73e7119bbc | ||
|
|
cea781382a | ||
|
|
cf25b84e86 | ||
|
|
86e1d22390 | ||
|
|
1625be111f | ||
|
|
00cda3b2aa | ||
|
|
85a00a4436 | ||
|
|
37aec1a497 | ||
|
|
ef637616ee | ||
|
|
af3b5067fb | ||
|
|
53e5b15f96 | ||
|
|
6a3afae607 | ||
|
|
2e05c7d9d1 | ||
|
|
fb698a04b7 | ||
|
|
52bdf30ddb | ||
|
|
a92f07aa40 | ||
|
|
003ff7f99d | ||
|
|
47b9806ae7 | ||
|
|
d2a7c65e08 | ||
|
|
e232336a5f | ||
|
|
8816c75212 | ||
|
|
f9d54751ba | ||
|
|
43d41ee761 | ||
|
|
4512ba8d5e | ||
|
|
8b55bca564 | ||
|
|
d8d54970d3 | ||
|
|
15ea4482f9 | ||
|
|
87f9f38eac | ||
|
|
dba559f041 | ||
|
|
c610b6f323 | ||
|
|
a5fb900e19 | ||
|
|
09d6e455f5 | ||
|
|
e68097b0f4 | ||
|
|
0744fed953 | ||
|
|
de0adf4067 | ||
|
|
76bf2b3efc | ||
|
|
c089badeb4 | ||
|
|
b3b99d366a | ||
|
|
4a36486f65 | ||
|
|
75126d00d9 | ||
|
|
17ebf95d5b | ||
|
|
c764420cfe | ||
|
|
28f05fc2af | ||
|
|
a420892887 | ||
|
|
d910dc5913 | ||
|
|
c9c1cbc349 | ||
|
|
fd4704f75e | ||
|
|
5400ca637f | ||
|
|
79d2610117 | ||
|
|
6a51f4cb9d | ||
|
|
c190776665 | ||
|
|
3765a1232e | ||
|
|
28866eba21 | ||
|
|
08adee3dc3 | ||
|
|
8a1adaf2cd | ||
|
|
fdb9accaa6 | ||
|
|
4f7656748b | ||
|
|
03e4495ac7 | ||
|
|
01af5cb9e1 | ||
|
|
8d49f32df2 | ||
|
|
8a955b3bc8 | ||
|
|
ead40cb413 | ||
|
|
5e07f78d4f | ||
|
|
c7d98795bc | ||
|
|
9f637e3d6a | ||
|
|
2d5f074c78 | ||
|
|
3be6ba484d | ||
|
|
d9d74e7a32 | ||
|
|
887306a5ed | ||
|
|
4b2542eb4f | ||
|
|
66bd3baa3e | ||
|
|
bf32004f8f | ||
|
|
bfb2df9c95 | ||
|
|
ddeb05e408 | ||
|
|
9312d61258 | ||
|
|
d44909bc42 | ||
|
|
1d77df335c | ||
|
|
a268dc25f4 | ||
|
|
8c5696faa0 | ||
|
|
94f3ccfb7d | ||
|
|
576329214d | ||
|
|
5c13c2e225 | ||
|
|
1070f85617 | ||
|
|
7c9dc87fcb | ||
|
|
363e58bf91 | ||
|
|
daca3f5b71 | ||
|
|
a7d2ec084b | ||
|
|
167f8cd0ff | ||
|
|
170307f45f | ||
|
|
2f36377780 | ||
|
|
48c565d85d | ||
|
|
86b3cc4044 | ||
|
|
6a2d5091b8 | ||
|
|
dc09e77190 | ||
|
|
4fc139c0c6 | ||
|
|
e5d800f469 | ||
|
|
4879ac28b5 | ||
|
|
bd0badab48 | ||
|
|
5575a26d3b | ||
|
|
d8cbe96949 | ||
|
|
cfb506a7ff | ||
|
|
d5b0ff7f40 | ||
|
|
31d259210c | ||
|
|
39112b8d41 | ||
|
|
4c92286ebb | ||
|
|
a317aeba85 | ||
|
|
dcaf17294e | ||
|
|
0bc734689a | ||
|
|
d0c2c00cc9 | ||
|
|
fd02328af9 | ||
|
|
3b7de31324 | ||
|
|
ebd67dd025 | ||
|
|
4557a39ae2 | ||
|
|
ce1eb299d3 | ||
|
|
20ab3f0a72 | ||
|
|
d61f90c25c | ||
|
|
90474e7592 | ||
|
|
a2e28003bc | ||
|
|
6e409c3dcd | ||
|
|
85a699c8de | ||
|
|
a788d1fb23 | ||
|
|
ab5b72375c | ||
|
|
3a023c51b5 | ||
|
|
0ad91bcafe | ||
|
|
f41ea3bcab | ||
|
|
327dc4b416 | ||
|
|
27e7779456 | ||
|
|
a7b2f5bb22 | ||
|
|
19fcb7c946 | ||
|
|
2706cd5881 | ||
|
|
4e2abd9c5b | ||
|
|
51ba2fa80a | ||
|
|
fd60cd0a21 | ||
|
|
5025372c05 | ||
|
|
4806bd075f | ||
|
|
370d17757a | ||
|
|
a648d7e75f | ||
|
|
518e1c5310 | ||
|
|
11539d8290 | ||
|
|
e4f33b58f7 | ||
|
|
073caa9515 | ||
|
|
2b06b11916 | ||
|
|
032b0f8a41 | ||
|
|
9391363f9b | ||
|
|
467fb2e9c7 | ||
|
|
257893b8de | ||
|
|
e919839de5 | ||
|
|
77d372cbee | ||
|
|
03e9facaf7 | ||
|
|
f3e459d911 | ||
|
|
36feff0e04 | ||
|
|
0320140a1b | ||
|
|
4fb1b1c9f9 | ||
|
|
37f3896a41 |
No files matched your search
@@ -57,5 +57,7 @@ body:
|
||||
required: true
|
||||
- label: I have read the wiki to make sure it's not an issue with configuration
|
||||
required: true
|
||||
- label: I ticked all of the boxes without actually reading them
|
||||
required: false
|
||||
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
|
||||
required: true
|
||||
@@ -51,5 +51,7 @@ body:
|
||||
required: true
|
||||
- label: I have included a paste of the error
|
||||
required: true
|
||||
- label: I ticked all of the boxes without actually reading them
|
||||
required: false
|
||||
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
|
||||
required: true
|
||||
@@ -11,22 +11,22 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v3.3.0 #https://github.com/actions/checkout/releases
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v3.9.0 #https://github.com/actions/setup-java/releases
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 11
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3.2.3 #https://github.com/actions/cache/releases
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3.2.3 #https://github.com/actions/cache/releases
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
run: ./gradlew clean build
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v3.1.2 #https://github.com/actions/upload-artifact/releases
|
||||
with:
|
||||
name: Jar
|
||||
path: jar/build/libs/TAB-**.jar
|
||||
@@ -6,8 +6,8 @@ More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/
|
||||
# Distribution
|
||||
As of TAB 3.0.0, released with MC 1.18 (November 30th, 2021), there is no free / premium
|
||||
version anymore. Only one version of the plugin exists now, which contains all implemented features
|
||||
and is available to everyone for free. However, official support on discord is only provided to
|
||||
those who have purchased the plugin.
|
||||
and is available to everyone for free. However, customer support is only provided to
|
||||
customers who have purchased the plugin.
|
||||
|
||||
# Compiling
|
||||
Compilation requires JDK 8 and up.
|
||||
@@ -15,17 +15,10 @@ To compile the plugin, run `./gradlew build` from the terminal.
|
||||
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
|
||||
|
||||
# Links
|
||||
The precompiled JAR can be downloaded for free from:
|
||||
* GitHub releases: https://github.com/NEZNAMY/TAB/releases
|
||||
* MC-Market: https://www.mc-market.org/resources/20631/
|
||||
|
||||
Supporting my work:
|
||||
* MC-Market: https://www.mc-market.org/resources/14009/
|
||||
* Polymart: https://polymart.org/resource/484
|
||||
|
||||
Other links:
|
||||
* Discord support for buyers: https://discord.gg/EaSvdk6
|
||||
* Wiki: https://github.com/NEZNAMY/TAB/wiki
|
||||
Download the plugin for free: [GitHub releases](https://github.com/NEZNAMY/TAB/releases),
|
||||
[BuiltByBit](https://builtbybit.com/resources/20631/), [Modrinth](https://modrinth.com/plugin/tab-was-taken)
|
||||
Support my work & receive customer support: [BuiltByBit](https://builtbybit.com/resources/14009/), [Polymart](https://polymart.org/resource/484)
|
||||
Other links: [Discord support for customers](https://discord.gg/EaSvdk6), [Wiki](https://github.com/NEZNAMY/TAB/wiki)
|
||||
|
||||
# Issues
|
||||
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is **only** available for cases included in templates.
|
||||
@@ -33,6 +26,9 @@ In other words, they **are not available for**:
|
||||
* **Support** - support is provided on discord for buyers, where roles are given to verified users
|
||||
allowing us to know who bought it and who didn't. It also serves as a much faster way of communication.
|
||||
* **Feature requests** - closed indefinitely.
|
||||
|
||||
**If you're a customer, please use discord for reporting bugs, especially if you aren't good at making bug reports.
|
||||
You will be provided with a much faster and higher quality service.**
|
||||
|
||||
# Contributing
|
||||
If this plugin has helped you in any way, and you would like to return something back
|
||||
@@ -40,8 +36,8 @@ to make the plugin even better, there is a lot of ways to contribute:
|
||||
* Open a **pull request** containing a new feature or a bug fix, which you believe many users will benefit from.
|
||||
* Make detailed high-quality **bug reports**. The difference between a bug getting fixed in 1 week vs 1 hour
|
||||
is in quality of the report (typically providing correct steps to reproduce that actually work).
|
||||
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) or in a form of pre-releases on discord
|
||||
for problems of any kind including bugs. This way you will help make the actual releases more stable / help get stable releases sooner.
|
||||
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) for problems of any kind including bugs.
|
||||
This way you will help make the actual releases more stable / help get stable releases sooner.
|
||||
* Help improve the wiki by opening a [Wiki change issue](https://github.com/NEZNAMY/TAB/issues/new?assignees=&labels=Wiki&template=wiki_change.yml),
|
||||
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples of usage of functions.
|
||||
* [Buy a copy](https://www.mc-market.org/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
|
||||
* [Buy a copy](https://builtbybit.com/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
|
||||
@@ -1,3 +1,7 @@
|
||||
plugins {
|
||||
id("net.kyori.blossom") version "1.2.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnlyApi(libs.netty)
|
||||
api(libs.jsonSimple) {
|
||||
@@ -6,4 +10,16 @@ dependencies {
|
||||
compileOnlyApi(libs.snakeyaml)
|
||||
api(libs.yamlAssist)
|
||||
compileOnlyApi(libs.gson)
|
||||
compileOnlyApi(libs.adventureMiniMessage)
|
||||
compileOnlyApi(libs.adventureApi)
|
||||
compileOnlyApi(libs.adventureLegacy)
|
||||
}
|
||||
|
||||
blossom {
|
||||
replaceToken("@plugin_version@", project.version)
|
||||
replaceTokenIn("src/main/java/me/neznamy/tab/api/TabConstants.java")
|
||||
}
|
||||
|
||||
tasks.javadoc {
|
||||
enabled = project.hasProperty("enable-javadoc")
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
/**
|
||||
* Interface representing an armor stand attached to player
|
||||
* for displaying text in game.
|
||||
*/
|
||||
public interface ArmorStand {
|
||||
|
||||
/**
|
||||
@@ -92,6 +96,13 @@ public interface ArmorStand {
|
||||
* player to spawn for
|
||||
*/
|
||||
void spawn(TabPlayer viewer);
|
||||
|
||||
|
||||
/**
|
||||
* Sends respawn packets to specified player to make position
|
||||
* change instant instead of teleport animation
|
||||
*
|
||||
* @param viewer
|
||||
* Player to send armor stand respawn packets to
|
||||
*/
|
||||
void respawn(TabPlayer viewer);
|
||||
}
|
||||
@@ -10,5 +10,11 @@ public interface ArmorStandManager {
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Refreshes text of all armor stands for all nearby players
|
||||
*
|
||||
* @param force
|
||||
* If refresh should be force despite no update seemingly being needed
|
||||
*/
|
||||
void refresh(boolean force);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Feature manager allows registration and work with features.
|
||||
*/
|
||||
@@ -41,4 +43,74 @@ public interface FeatureManager {
|
||||
* @return the feature or null if feature does not exist
|
||||
*/
|
||||
TabFeature getFeature(String name);
|
||||
|
||||
/**
|
||||
* Calls onQuit(TabPlayer) to all enabled features
|
||||
*
|
||||
* @param disconnectedPlayer
|
||||
* player who disconnected
|
||||
*/
|
||||
void onQuit(TabPlayer disconnectedPlayer);
|
||||
|
||||
/**
|
||||
* Calls onJoin(TabPlayer) to all enabled features
|
||||
*
|
||||
* @param connectedPlayer
|
||||
* player who connected
|
||||
*/
|
||||
void onJoin(TabPlayer connectedPlayer);
|
||||
|
||||
/**
|
||||
* Calls onServerChange(TabPlayer, String, String) to all enabled features
|
||||
*
|
||||
* @param playerUUID
|
||||
* player who switched server
|
||||
* @param to
|
||||
* name of the new server
|
||||
*/
|
||||
void onServerChange(UUID playerUUID, String to);
|
||||
|
||||
/**
|
||||
* Calls onWorldChange(TabPlayer, String, String) to all enabled features
|
||||
*
|
||||
* @param playerUUID
|
||||
* player who switched world
|
||||
* @param to
|
||||
* name of the new world
|
||||
*/
|
||||
void onWorldChange(UUID playerUUID, String to);
|
||||
|
||||
/**
|
||||
* Calls onCommand(TabPlayer, String) to all enabled features
|
||||
*
|
||||
* @param sender
|
||||
* command sender
|
||||
* @param command
|
||||
* command line including /
|
||||
* @return {@code true} if some feature marked the command for cancel, {@code false} if not
|
||||
*/
|
||||
boolean onCommand(TabPlayer sender, String command);
|
||||
|
||||
/**
|
||||
* Internal function that marks objective packet deserialization
|
||||
* when at least 1 feature uses it.
|
||||
* <p>
|
||||
* Internal use only.
|
||||
*/
|
||||
void markObjective();
|
||||
|
||||
/**
|
||||
* Internal function that marks display objective packet deserialization
|
||||
* when at least 1 feature uses it.
|
||||
* <p>
|
||||
* Internal use only.
|
||||
*/
|
||||
void markDisplayObjective();
|
||||
|
||||
/**
|
||||
* Returns array of all currently enabled features
|
||||
*
|
||||
* @return array of all currently enabled features
|
||||
*/
|
||||
TabFeature[] getValues();
|
||||
}
|
||||
@@ -1,16 +1,82 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
/**
|
||||
* Interface allowing to send header/footer to players.
|
||||
* <p>
|
||||
* Instance can be obtained using {@link TabAPI#getHeaderFooterManager()}.
|
||||
* This requires the Header/footer feature to be enabled in config, otherwise
|
||||
* the method will return {@code null}.
|
||||
*/
|
||||
public interface HeaderFooterManager {
|
||||
|
||||
/**
|
||||
* Sets raw value of header to specified value. This overrides plugin
|
||||
* configuration until reset is called. Has full placeholder support
|
||||
* and will send update packet whenever a placeholder used inside
|
||||
* changes its value.
|
||||
*
|
||||
* @param player
|
||||
* Player to set header for
|
||||
* @param header
|
||||
* Raw header value
|
||||
*/
|
||||
void setHeader(TabPlayer player, String header);
|
||||
|
||||
/**
|
||||
* Sets raw value of footer to specified value. This overrides plugin
|
||||
* configuration until reset is called. Has full placeholder support
|
||||
* and will send update packet whenever a placeholder used inside
|
||||
* changes its value.
|
||||
*
|
||||
* @param player
|
||||
* Player to set header for
|
||||
* @param footer
|
||||
* Raw footer value
|
||||
*/
|
||||
void setFooter(TabPlayer player, String footer);
|
||||
|
||||
/**
|
||||
* Sets raw value of header and footer to specified values. This overrides plugin
|
||||
* configuration until reset is called. Has full placeholder support
|
||||
* and will send update packet whenever a placeholder used inside
|
||||
* changes its value.
|
||||
*
|
||||
* @param player
|
||||
* Player to set header for
|
||||
* @param header
|
||||
* Raw header value
|
||||
* @param footer
|
||||
* Raw footer value
|
||||
*/
|
||||
void setHeaderAndFooter(TabPlayer player, String header, String footer);
|
||||
|
||||
/**
|
||||
* Resets header value previously set using {@link #setHeader(TabPlayer, String)}
|
||||
* or {@link #setHeaderAndFooter(TabPlayer, String, String)} and restores internal
|
||||
* logic using configuration files.
|
||||
*
|
||||
* @param player
|
||||
* Player to reset header for
|
||||
*/
|
||||
void resetHeader(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Resets footer value previously set using {@link #setFooter(TabPlayer, String)}
|
||||
* or {@link #setHeaderAndFooter(TabPlayer, String, String)} and restores internal
|
||||
* logic using configuration files.
|
||||
*
|
||||
* @param player
|
||||
* Player to reset header for
|
||||
*/
|
||||
void resetFooter(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Resets values previously set using {@link #setHeader(TabPlayer, String)},
|
||||
* {@link #setFooter(TabPlayer, String)} or {@link #setHeaderAndFooter(TabPlayer, String, String)}
|
||||
* and restores internal logic using configuration files.
|
||||
*
|
||||
* @param player
|
||||
* Player to reset header and footer for
|
||||
*/
|
||||
void resetHeaderAndFooter(TabPlayer player);
|
||||
}
|
||||
@@ -8,6 +8,10 @@ public enum ProtocolVersion {
|
||||
UNKNOWN_SERVER_VERSION ("Unknown"),
|
||||
UNKNOWN_CLIENT_VERSION ("Unknown"),
|
||||
PROXY ("Proxy"),
|
||||
V1_19_3 (761),
|
||||
V1_19_2 (760),
|
||||
V1_19_1 (760),
|
||||
V1_19 (759),
|
||||
V1_18_2 (758),
|
||||
V1_18_1 (757),
|
||||
V1_18 (757),
|
||||
|
||||
@@ -18,6 +18,13 @@ public abstract class TabAPI {
|
||||
/** Instance of the API */
|
||||
private static TabAPI instance;
|
||||
|
||||
/**
|
||||
* Constructs new instance and sets it as new API instance
|
||||
*/
|
||||
public TabAPI() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns API instance. If instance was not set by the plugin, throws
|
||||
* {@code IllegalStateException}. This is usually caused by shading the API
|
||||
@@ -34,16 +41,6 @@ public abstract class TabAPI {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance setter for internal use by the plugin only.
|
||||
*
|
||||
* @param instance
|
||||
* API instance
|
||||
*/
|
||||
public static void setInstance(TabAPI instance) {
|
||||
TabAPI.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns player object from given UUID
|
||||
*
|
||||
@@ -204,4 +201,13 @@ public abstract class TabAPI {
|
||||
* Name of file with syntax error
|
||||
*/
|
||||
public abstract void setBrokenFile(String file);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the plugin is currently disabled due to an error,
|
||||
* either a yaml syntax error or an internal startup error. Returns {@code false}
|
||||
* if the plugin is properly running.
|
||||
*
|
||||
* @return {@code true} if disabled, {@code false} if running correctly
|
||||
*/
|
||||
public abstract boolean isPluginDisabled();
|
||||
}
|
||||
+14
-1
@@ -1,4 +1,4 @@
|
||||
package me.neznamy.tab.shared;
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
/**
|
||||
* A class containing various constants used everywhere in the plugin
|
||||
@@ -83,6 +83,8 @@ public class TabConstants {
|
||||
public static final String NAMETAGS_TEAM_UNREGISTER = "NameTags (Team unregister)";
|
||||
public static final String NAMETAGS_TEAM_UPDATE = "NameTags (Team update)";
|
||||
|
||||
public static final String SORTING_CHANGING_TEAM_NAME = "Sorting (Changing team name)";
|
||||
|
||||
public static final String SCOREBOARD_TITLE = "Scoreboard (Title)";
|
||||
public static final String SCOREBOARD_LINES = "Scoreboard (Lines)";
|
||||
|
||||
@@ -116,7 +118,11 @@ public class TabConstants {
|
||||
public static final String COMMAND_MYSQL_DOWNLOAD = "tab.mysql.download";
|
||||
public static final String COMMAND_MYSQL_UPLOAD = "tab.mysql.upload";
|
||||
public static final String COMMAND_NAMETAG_TOGGLE = "tab.nametag.toggle";
|
||||
|
||||
public static final String COMMAND_NAMETAG_TOGGLE_OTHER = "tab.nametag.toggle.other";
|
||||
public static final String COMMAND_NAMETAG_PREVIEW = "tab.nametag.preview";
|
||||
|
||||
public static final String COMMAND_NAMETAG_PREVIEW_OTHER = "tab.nametag.preview.other";
|
||||
public static final String COMMAND_PARSE = "tab.parse";
|
||||
public static final String COMMAND_SCOREBOARD_ANNOUNCE = "tab.announce.scoreboard";
|
||||
public static final String COMMAND_SCOREBOARD_TOGGLE = "tab.scoreboard.toggle";
|
||||
@@ -329,6 +335,9 @@ public class TabConstants {
|
||||
public static final String VAULT_SUFFIX = "%vault-suffix%";
|
||||
public static final String HEALTH = "%health%";
|
||||
|
||||
public static final int MINIMUM_REFRESH_INTERVAL = 50;
|
||||
|
||||
|
||||
public static String condition(String name) {
|
||||
return "%condition:" + name + "%";
|
||||
}
|
||||
@@ -336,6 +345,10 @@ public class TabConstants {
|
||||
public static String animation(String name) {
|
||||
return "%animation:" + name + "%";
|
||||
}
|
||||
|
||||
public static String globalPlayerListGroup(String group) {
|
||||
return "%playerlist-group_" + group + "%";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,12 +64,16 @@ public abstract class TabFeature {
|
||||
methodOverrides.add("onWorldChange");
|
||||
if (getClass().getMethod("onServerChange", TabPlayer.class, String.class, String.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onServerChange");
|
||||
if (getClass().getMethod("onDisplayObjective", TabPlayer.class, PacketPlayOutScoreboardDisplayObjective.class).getDeclaringClass() != TabFeature.class)
|
||||
if (getClass().getMethod("onDisplayObjective", TabPlayer.class, PacketPlayOutScoreboardDisplayObjective.class).getDeclaringClass() != TabFeature.class) {
|
||||
methodOverrides.add("onDisplayObjective");
|
||||
TabAPI.getInstance().getFeatureManager().markDisplayObjective();
|
||||
}
|
||||
if (getClass().getMethod("onLoginPacket", TabPlayer.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onLoginPacket");
|
||||
if (getClass().getMethod("onObjective", TabPlayer.class, PacketPlayOutScoreboardObjective.class).getDeclaringClass() != TabFeature.class)
|
||||
if (getClass().getMethod("onObjective", TabPlayer.class, PacketPlayOutScoreboardObjective.class).getDeclaringClass() != TabFeature.class) {
|
||||
methodOverrides.add("onObjective");
|
||||
TabAPI.getInstance().getFeatureManager().markObjective();
|
||||
}
|
||||
if (getClass().getMethod("onPlayerInfo", TabPlayer.class, PacketPlayOutPlayerInfo.class).getDeclaringClass() != TabFeature.class)
|
||||
methodOverrides.add("onPlayerInfo");
|
||||
if (getClass().getMethod("onPacketReceive", TabPlayer.class, Object.class).getDeclaringClass() != TabFeature.class)
|
||||
|
||||
@@ -212,13 +212,6 @@ public interface TabPlayer {
|
||||
*/
|
||||
String getTeamName();
|
||||
|
||||
/**
|
||||
* Returns user-friendly explanation of team name
|
||||
*
|
||||
* @return explanation behind team name
|
||||
*/
|
||||
String getTeamNameNote();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if player is disguised using LibsDisguises, {@code false} if not
|
||||
*
|
||||
@@ -310,4 +303,18 @@ public interface TabPlayer {
|
||||
* @return player's game profile name as seen by other players
|
||||
*/
|
||||
String getNickname();
|
||||
|
||||
/**
|
||||
* Returns player's chat message signing key. Returned object is a direct
|
||||
* object from the server, which is different per platform.
|
||||
*
|
||||
* @return Player's direct chat message signing key
|
||||
*/
|
||||
Object getProfilePublicKey();
|
||||
|
||||
/**
|
||||
* Return player's chat session ID
|
||||
* @return player's chat session ID
|
||||
*/
|
||||
UUID getChatSessionId();
|
||||
}
|
||||
@@ -199,4 +199,12 @@ public interface BossBar {
|
||||
* @return {@code true} if contains, {@code false} if not
|
||||
*/
|
||||
boolean containsPlayer(TabPlayer player);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this bossbar in configured as announcement-only bar,
|
||||
* {@code false} if not.
|
||||
*
|
||||
* @return {@code true} if bar is announcement only, {@code false} if not
|
||||
*/
|
||||
boolean isAnnouncementBar();
|
||||
}
|
||||
@@ -2,21 +2,23 @@ package me.neznamy.tab.api.chat;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
public class ChatComponentEntity extends IChatBaseComponent {
|
||||
|
||||
private final String type;
|
||||
private final UUID id;
|
||||
private final String name;
|
||||
|
||||
private final String CONTENTS;
|
||||
private final String VALUE_1_13;
|
||||
private final String VALUE_1_12;
|
||||
private final String VALUE_1_8;
|
||||
|
||||
public ChatComponentEntity(String type, UUID id, String name) {
|
||||
Preconditions.checkNotNull(type, "type");
|
||||
Preconditions.checkNotNull(id, "id");
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.CONTENTS = String.format("{\"type\":\"%s\",\"id\":\"%s\",\"name\":{\"text\":\"%s\"}}", type, id, name);
|
||||
this.VALUE_1_13 = String.format("{type:\"%s\",id:\"%s\",name:\"{\\\"text\\\":\\\"%s\\\"}\"}", type, id, name);
|
||||
this.VALUE_1_12 = String.format("{type:\"%s\",id:\"%s\",name:\"%s\"}", type, id, name);
|
||||
this.VALUE_1_8 = String.format("{type:%s,id:%s,name:%s}", type, id, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -26,16 +28,10 @@ public class ChatComponentEntity extends IChatBaseComponent {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{\"type\":\"%s\",\"id\":\"%s\",\"name\":{\"text\":\"%s\"}}", type, id, name);
|
||||
}
|
||||
|
||||
public JsonObject toJson() {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("type", type);
|
||||
json.addProperty("id", id.toString());
|
||||
JsonObject json2 = new JsonObject();
|
||||
json2.addProperty("text", name);
|
||||
json.add("name", json2);
|
||||
return json;
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) return CONTENTS;
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 13) return VALUE_1_13;
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() == 12) return VALUE_1_12;
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 8) return VALUE_1_8;
|
||||
throw new IllegalStateException("show_entity hover action is not supported on <1.8");
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,18 @@ public class ChatHoverable {
|
||||
*/
|
||||
public enum EnumHoverAction {
|
||||
|
||||
SHOW_TEXT,
|
||||
SHOW_ITEM,
|
||||
SHOW_ENTITY
|
||||
SHOW_TEXT("contents"),
|
||||
SHOW_ITEM("value"),
|
||||
SHOW_ENTITY("contents");
|
||||
|
||||
private final String preferredKey;
|
||||
|
||||
EnumHoverAction(String preferredKey) {
|
||||
this.preferredKey = preferredKey;
|
||||
}
|
||||
|
||||
public String getPreferredKey() {
|
||||
return preferredKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,17 +249,7 @@ public class ChatModifier {
|
||||
public void onHoverShowEntity(String type, UUID id, String name) {
|
||||
Preconditions.checkNotNull(type, "type");
|
||||
Preconditions.checkNotNull(id, "id");
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new ChatComponentEntity(type, id, name));
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 13) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new IChatBaseComponent(String.format("{type:\"%s\",id:\"%s\",name:\"{\\\"text\\\":\\\"%s\\\"}\"}", type, id, name)));
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() == 12) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new IChatBaseComponent(String.format("{type:\"%s\",id:\"%s\",name:\"%s\"}", type, id, name)));
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 8) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new IChatBaseComponent(String.format("{type:%s,id:%s,name:%s}", type, id, name)));
|
||||
} else {
|
||||
throw new IllegalStateException("show_entity hover action is not supported on <1.8");
|
||||
}
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new ChatComponentEntity(type, id, name));
|
||||
}
|
||||
|
||||
public void onHover(EnumHoverAction action, IChatBaseComponent value) {
|
||||
@@ -295,7 +285,7 @@ public class ChatModifier {
|
||||
JSONObject hover = new JSONObject();
|
||||
hover.put("action", hoverEvent.getAction().toString().toLowerCase());
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) {
|
||||
hover.put("contents", hoverEvent.getValue());
|
||||
hover.put(hoverEvent.getAction().getPreferredKey(), hoverEvent.getValue());
|
||||
} else {
|
||||
hover.put("value", TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 ?
|
||||
hoverEvent.getValue() : hoverEvent.getValue().toRawText());
|
||||
|
||||
@@ -424,7 +424,7 @@ public class IChatBaseComponent {
|
||||
if (text == null) return null;
|
||||
if (componentCache.containsKey(text)) return componentCache.get(text);
|
||||
IChatBaseComponent component;
|
||||
if (text.contains("#") || text.contains("&x") || text.contains(EnumChatFormat.COLOR_CHAR + "x")){
|
||||
if (text.contains("#") || text.contains("&x") || text.contains(EnumChatFormat.COLOR_CHAR + "x") || text.contains("<")){
|
||||
//contains RGB colors
|
||||
component = IChatBaseComponent.fromColoredText(text);
|
||||
} else {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package me.neznamy.tab.api.chat.rgb;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.TextColor;
|
||||
import me.neznamy.tab.api.chat.rgb.format.BukkitFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.format.CMIFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.format.HtmlFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.format.KyoriFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.format.RGBFormatter;
|
||||
import me.neznamy.tab.api.chat.rgb.format.UnnamedFormat1;
|
||||
import me.neznamy.tab.api.chat.rgb.format.*;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.CMIGradient;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.CommonGradient;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.GradientPattern;
|
||||
@@ -41,13 +38,18 @@ public class RGBUtils {
|
||||
* Constructs new instance and loads all RGB patterns and gradients
|
||||
*/
|
||||
public RGBUtils() {
|
||||
formats = new RGBFormatter[] {
|
||||
new BukkitFormat(),
|
||||
new CMIFormat(),
|
||||
new UnnamedFormat1(),
|
||||
new HtmlFormat(),
|
||||
new KyoriFormat()
|
||||
};
|
||||
List<RGBFormatter> list = new ArrayList<>();
|
||||
try {
|
||||
Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
|
||||
list.add(new MiniMessageFormat());
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
list.add(new BukkitFormat());
|
||||
list.add(new CMIFormat());
|
||||
list.add(new UnnamedFormat1());
|
||||
list.add(new HtmlFormat());
|
||||
list.add(new KyoriFormat());
|
||||
formats = list.toArray(new RGBFormatter[0]);
|
||||
|
||||
gradients = new GradientPattern[] {
|
||||
//{#RRGGBB>}text{#RRGGBB<}
|
||||
new CMIGradient(),
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Formatter for #<RRGGBB>
|
||||
* Formatter for #<RRGGBB>
|
||||
*/
|
||||
public class HtmlFormat implements RGBFormatter {
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Formatter for <#RRGGBB>
|
||||
* Formatter for <#RRGGBB>
|
||||
*/
|
||||
public class KyoriFormat implements RGBFormatter {
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package me.neznamy.tab.api.chat.rgb.format;
|
||||
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
/**
|
||||
* Call to MiniMessage API to reformat text to &x&R&R&G&G&B&B
|
||||
*/
|
||||
public class MiniMessageFormat implements RGBFormatter {
|
||||
|
||||
@Override
|
||||
public String reformat(String text) {
|
||||
if (!text.contains("<")) return text;
|
||||
try {
|
||||
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(text));
|
||||
} catch (Throwable ignored) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.TextColor;
|
||||
|
||||
/**
|
||||
* Gradient applier for <gradient:#RRGGBB:#RRGGBB>Text</gradient>
|
||||
* and <gradient:#RRGGBB|L:#RRGGBB>Text</gradient>
|
||||
* Gradient applier for <gradient:#RRGGBB:#RRGGBB>Text</gradient>
|
||||
* and <gradient:#RRGGBB|L:#RRGGBB>Text</gradient>
|
||||
*/
|
||||
public class KyoriGradient implements GradientPattern {
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package me.neznamy.tab.api.config;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -16,9 +13,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Abstract class for configuration file
|
||||
*/
|
||||
@@ -53,18 +47,9 @@ public abstract class ConfigurationFile {
|
||||
Preconditions.checkNotNull(destination, "destination");
|
||||
this.file = destination;
|
||||
if (file.getParentFile() != null && !file.getParentFile().exists()) Files.createDirectories(file.getParentFile().toPath());
|
||||
if (!file.exists() && source == null) throw new IllegalStateException("File does not exist and source is null");
|
||||
if (file.createNewFile()) {
|
||||
// Files.copy(source, file.toPath());
|
||||
//avoiding MalformedInputException thrown on fabric due to file not having UTF8 encoding by default
|
||||
//also automatically using the encoding so users don't have to worry about it anymore
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8));
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))){
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
writer.write(line + "\n");
|
||||
}
|
||||
}
|
||||
if (!file.exists()) {
|
||||
if (source == null) throw new IllegalStateException("File does not exist and source is null");
|
||||
Files.copy(source, file.toPath());
|
||||
}
|
||||
detectHeader();
|
||||
}
|
||||
@@ -224,7 +209,7 @@ public abstract class ConfigurationFile {
|
||||
}
|
||||
List<String> fixedList = new ArrayList<>();
|
||||
for (Object key : (List<Object>)value) {
|
||||
fixedList.add(key.toString());
|
||||
fixedList.add(String.valueOf(key));
|
||||
}
|
||||
return fixedList;
|
||||
}
|
||||
|
||||
@@ -9,4 +9,6 @@ public interface Placeholder {
|
||||
void enableTriggerMode(Runnable onActivation, Runnable onDisable);
|
||||
|
||||
void unload();
|
||||
|
||||
boolean isUsed();
|
||||
}
|
||||
@@ -48,4 +48,6 @@ public interface PlaceholderManager {
|
||||
String findReplacement(String placeholder, String output);
|
||||
|
||||
List<String> getUsedPlaceholders();
|
||||
|
||||
Placeholder getPlaceholder(String identifier);
|
||||
}
|
||||
@@ -202,7 +202,7 @@ public class PacketBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code clientVersion} is >= 1.13, creates a component from given text and returns
|
||||
* If {@code clientVersion} is >= 1.13, creates a component from given text and returns
|
||||
* it as a serialized component, which BungeeCord uses.
|
||||
* <p>
|
||||
* If {@code clientVersion} is < 1.12, the text is cut to {@code length} characters if
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -12,8 +13,8 @@ import me.neznamy.tab.api.util.Preconditions;
|
||||
*/
|
||||
public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
/** Packet action */
|
||||
private final EnumPlayerInfoAction action;
|
||||
/** Packet actions */
|
||||
private final EnumSet<EnumPlayerInfoAction> actions;
|
||||
|
||||
/** List of affected entries */
|
||||
private final List<PlayerInfoData> entries;
|
||||
@@ -27,10 +28,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, PlayerInfoData... entries) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
this.action = action;
|
||||
this.entries = Arrays.asList(entries);
|
||||
this(action, Arrays.asList(entries));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,22 +42,61 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
this.action = action;
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
actions = EnumSet.of(EnumPlayerInfoAction.ADD_PLAYER,
|
||||
EnumPlayerInfoAction.INITIALIZE_CHAT,
|
||||
EnumPlayerInfoAction.UPDATE_GAME_MODE,
|
||||
EnumPlayerInfoAction.UPDATE_LISTED,
|
||||
EnumPlayerInfoAction.UPDATE_LATENCY,
|
||||
EnumPlayerInfoAction.UPDATE_DISPLAY_NAME);
|
||||
} else {
|
||||
this.actions = EnumSet.of(action);
|
||||
}
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param actions
|
||||
* Packet actions
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, PlayerInfoData... entries) {
|
||||
Preconditions.checkNotNull(actions, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
this.actions = actions;
|
||||
this.entries = Arrays.asList(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param actions
|
||||
* Packet actions
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, List<PlayerInfoData> entries) {
|
||||
Preconditions.checkNotNull(actions, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
this.actions = actions;
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutPlayerInfo{action=%s,entries=%s}", action, entries);
|
||||
return String.format("PacketPlayOutPlayerInfo{actions=%s,entries=%s}", actions, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
* Returns {@link #actions}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public EnumPlayerInfoAction getAction() {
|
||||
return action;
|
||||
public EnumSet<EnumPlayerInfoAction> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,6 +116,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
/** Latency */
|
||||
private int latency;
|
||||
|
||||
/** I have no idea what this is */
|
||||
private boolean listed;
|
||||
|
||||
/** GameMode */
|
||||
private EnumGamemode gameMode = EnumGamemode.SURVIVAL; //ProtocolLib causes NPE even when action does not use GameMode
|
||||
|
||||
@@ -97,9 +137,15 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
/** Player's skin, null for empty skin */
|
||||
private Skin skin;
|
||||
|
||||
/** Chat session ID */
|
||||
private UUID chatSessionId;
|
||||
|
||||
/** Player's chat signing key */
|
||||
private Object profilePublicKey;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.ADD_PLAYER action
|
||||
* {@link EnumPlayerInfoAction#ADD_PLAYER} action
|
||||
*
|
||||
* @param name
|
||||
* Player's name
|
||||
@@ -113,20 +159,25 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* Player's GameMode
|
||||
* @param displayName
|
||||
* Player's display name
|
||||
* @param profilePublicKey
|
||||
* Player's chat signing key
|
||||
*/
|
||||
public PlayerInfoData(String name, UUID uniqueId, Skin skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName) {
|
||||
public PlayerInfoData(String name, UUID uniqueId, Skin skin, boolean listed, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, UUID chatSessionId, Object profilePublicKey) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
this.name = name;
|
||||
this.uniqueId = uniqueId;
|
||||
this.skin = skin;
|
||||
this.listed = listed;
|
||||
this.latency = latency;
|
||||
this.gameMode = gameMode;
|
||||
this.displayName = displayName;
|
||||
this.chatSessionId = chatSessionId;
|
||||
this.profilePublicKey = profilePublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.UPDATE_GAME_MODE action
|
||||
* {@link EnumPlayerInfoAction#UPDATE_GAME_MODE} action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
@@ -141,7 +192,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.UPDATE_LATENCY action
|
||||
* {@link EnumPlayerInfoAction#UPDATE_LATENCY} action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
@@ -156,7 +207,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.UPDATE_DISPLAY_NAME action
|
||||
* {@link EnumPlayerInfoAction#UPDATE_DISPLAY_NAME} action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
@@ -171,7 +222,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.REMOVE_PLAYER action
|
||||
* {@link EnumPlayerInfoAction#REMOVE_PLAYER} action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
@@ -182,8 +233,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PlayerInfoData{latency=%s,gameMode=%s,displayName=%s,name=%s,uniqueId=%s,skin=%s}",
|
||||
latency, gameMode, displayName, name, uniqueId, skin);
|
||||
return String.format("PlayerInfoData{latency=%s,gameMode=%s,displayName=%s,name=%s,uniqueId=%s,skin=%s,profilePublicKey=%s}",
|
||||
latency, gameMode, displayName, name, uniqueId, skin, profilePublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,6 +256,25 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
this.latency = latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #listed}
|
||||
*
|
||||
* @return listed
|
||||
*/
|
||||
public boolean isListed() {
|
||||
return listed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #listed} to specified value
|
||||
*
|
||||
* @param listed
|
||||
* Listed flag
|
||||
*/
|
||||
public void setListed(boolean listed) {
|
||||
this.listed = listed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #displayName}
|
||||
*
|
||||
@@ -299,16 +369,55 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
public void setSkin(Skin skin) {
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #chatSessionId}
|
||||
*
|
||||
* @return chatSessionId
|
||||
*/
|
||||
public UUID getChatSessionId() {
|
||||
return chatSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #chatSessionId} to specified value
|
||||
*
|
||||
* @param chatSessionId
|
||||
* chatSessionId to use
|
||||
*/
|
||||
public void setChatSessionId(UUID chatSessionId) {
|
||||
this.chatSessionId = chatSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #profilePublicKey}
|
||||
*
|
||||
* @return profilePublicKey
|
||||
*/
|
||||
public Object getProfilePublicKey() {
|
||||
return profilePublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #profilePublicKey} to specified value
|
||||
*
|
||||
* @param profilePublicKey
|
||||
* profilePublicKey to use
|
||||
*/
|
||||
public void setProfilePublicKey(Object profilePublicKey) {
|
||||
this.profilePublicKey = profilePublicKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* En enum representing packet action
|
||||
* Calling ordinal() will return action's network ID.
|
||||
*/
|
||||
public enum EnumPlayerInfoAction {
|
||||
|
||||
ADD_PLAYER,
|
||||
INITIALIZE_CHAT,
|
||||
UPDATE_GAME_MODE,
|
||||
UPDATE_LISTED,
|
||||
UPDATE_LATENCY,
|
||||
UPDATE_DISPLAY_NAME,
|
||||
REMOVE_PLAYER
|
||||
|
||||
@@ -20,10 +20,10 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/** Team prefix, limited to 16 characters, on <1.13 */
|
||||
/** Team prefix, limited to 16 characters, on <1.13 */
|
||||
private String playerPrefix;
|
||||
|
||||
/** Team suffix, limited to 16 characters, on <1.13 */
|
||||
/** Team suffix, limited to 16 characters, on <1.13 */
|
||||
private String playerSuffix;
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
|
||||
/**
|
||||
* Affected entities. For players, it is their name, for other entities their UUID
|
||||
* Player name length is limited to 16 characters for <1.8, 40 characters since 1.8.
|
||||
* Player name length is limited to 16 characters for <1.8, 40 characters since 1.8.
|
||||
* Entity UUID option was added in 1.8.
|
||||
*/
|
||||
private Collection<String> players = Collections.emptyList();
|
||||
|
||||
@@ -74,7 +74,7 @@ public interface Scoreboard {
|
||||
* @param index
|
||||
* Index of line to remove, starting at 0
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of range (index < 0 || index >= {@link #getLines()}.size())
|
||||
* if the index is out of range (index < 0 || index >= {@link #getLines()}.size())
|
||||
* @see #getLines()
|
||||
* @see #addLine(String)
|
||||
*/
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package me.neznamy.tab.api.task;
|
||||
|
||||
public interface RepeatingTask {
|
||||
|
||||
int getInterval();
|
||||
|
||||
void setInterval(int paramInt);
|
||||
|
||||
void cancel();
|
||||
}
|
||||
@@ -4,6 +4,10 @@ import java.util.concurrent.Future;
|
||||
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
|
||||
/**
|
||||
* Manager for scheduling and executing tasks in TAB's
|
||||
* main thread to avoid concurrency issues.
|
||||
*/
|
||||
public interface ThreadManager {
|
||||
|
||||
/**
|
||||
@@ -49,10 +53,20 @@ public interface ThreadManager {
|
||||
* usage type to add cpu usage to
|
||||
* @param task
|
||||
* the task
|
||||
* @return Future representing the submitted task
|
||||
*/
|
||||
RepeatingTask startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task);
|
||||
Future<?> startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task);
|
||||
|
||||
RepeatingTask startRepeatingTask(int intervalMilliseconds, Runnable task);
|
||||
/**
|
||||
* Starts a new task with defined repeat interval
|
||||
*
|
||||
* @param intervalMilliseconds
|
||||
* task interval
|
||||
* @param task
|
||||
* the task
|
||||
* @return Future representing the submitted task
|
||||
*/
|
||||
Future<?> startRepeatingTask(int intervalMilliseconds, Runnable task);
|
||||
|
||||
/**
|
||||
* Runs task with a delay and measures how long it took to process
|
||||
@@ -65,7 +79,18 @@ public interface ThreadManager {
|
||||
* usage type to add cpu usage to
|
||||
* @param task
|
||||
* the task
|
||||
* @return future allowing to cancel the task
|
||||
* @return Future representing the submitted task
|
||||
*/
|
||||
Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task);
|
||||
|
||||
/**
|
||||
* Runs task with a delay
|
||||
*
|
||||
* @param delayMilliseconds
|
||||
* how long to run the task after
|
||||
* @param task
|
||||
* the task
|
||||
* @return Future representing the submitted task
|
||||
*/
|
||||
Future<?> runTaskLater(int delayMilliseconds, Runnable task);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package me.neznamy.tab.api.util;
|
||||
|
||||
/**
|
||||
* An interface for BiFunction which can throw an exception.
|
||||
* Used in {@link me.neznamy.tab.api.protocol.PacketBuilder} to
|
||||
* Used in {@link me.neznamy.tab.api.protocol.PacketBuilder} to
|
||||
* call build method for packet classes, which may throw an
|
||||
* exception due to reflection.
|
||||
* @param <A>
|
||||
@@ -14,5 +14,16 @@ package me.neznamy.tab.api.util;
|
||||
*/
|
||||
public interface BiFunctionWithException<A, B, C> {
|
||||
|
||||
/**
|
||||
* Runs the function and returns the result
|
||||
*
|
||||
* @param a
|
||||
* First argument
|
||||
* @param b
|
||||
* Second argument
|
||||
* @return Output from function
|
||||
* @throws ReflectiveOperationException
|
||||
* If reflective operation fails
|
||||
*/
|
||||
C apply(A a, B b) throws ReflectiveOperationException;
|
||||
}
|
||||
@@ -2,24 +2,76 @@ package me.neznamy.tab.api.util;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
/**
|
||||
* Class with utility methods used in the plugin
|
||||
*/
|
||||
public class Preconditions {
|
||||
|
||||
private Preconditions(){}
|
||||
/**
|
||||
* Private constructor preventing the class from being instantiated
|
||||
*/
|
||||
private Preconditions() {}
|
||||
|
||||
public static void checkNotNull(Object obj, String name){
|
||||
/**
|
||||
* Checks object and throws {@code IllegalArgumentException} if it is {@code null}.
|
||||
*
|
||||
* @param obj
|
||||
* Object to check
|
||||
* @param name
|
||||
* Name of the variable to use in exception message
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code obj} is {@code null}
|
||||
*/
|
||||
public static void checkNotNull(Object obj, String name) {
|
||||
if (obj == null) throw new IllegalArgumentException(name + " cannot be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks length of String and throws {@code IllegalArgumentException} if it is
|
||||
* longer than limit.
|
||||
*
|
||||
* @param string
|
||||
* String to check
|
||||
* @param maxLength
|
||||
* Maximum length of the string
|
||||
* @param name
|
||||
* Name of the variable to use in exception message
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code string} is {@code null} or longer than {@code maxLength} characters
|
||||
*/
|
||||
public static void checkMaxLength(String string, int maxLength, String name) {
|
||||
checkNotNull(string, name);
|
||||
if (string.length() > maxLength) throw new IllegalArgumentException(name + " is longer than " + maxLength + " characters (" + string.length() + ")");
|
||||
}
|
||||
|
||||
public static void checkRange(Number number, Number min, Number max, String variable){
|
||||
/**
|
||||
* Checks number range and throws {@code IllegalArgumentException} if number
|
||||
* is out of range.
|
||||
*
|
||||
* @param number
|
||||
* Number to check
|
||||
* @param min
|
||||
* Minimum allowed value
|
||||
* @param max
|
||||
* Maximum allowed value
|
||||
* @param variable
|
||||
* Variable name to use in exception message
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code number} is out of range ({@code number < min || number > max})
|
||||
*/
|
||||
public static void checkRange(Number number, Number min, Number max, String variable) {
|
||||
if (number.doubleValue() < min.doubleValue() || number.doubleValue() > max.doubleValue())
|
||||
throw new IllegalArgumentException(variable + " index out of range (" + min + " - " + max + ")");
|
||||
throw new IllegalArgumentException(variable + " index out of range (" + min + " - " + max + "), was " + number);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if player is loaded and throws {@code IllegalStateException} if not.
|
||||
*
|
||||
* @param player
|
||||
* Player to check
|
||||
* @throws IllegalStateException
|
||||
* If player is not loaded
|
||||
*/
|
||||
public static void checkLoaded(TabPlayer player) {
|
||||
if (!player.isLoaded()) throw new IllegalStateException("Player is not loaded yet. Try again later.");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
/**
|
||||
* Supplier that can throw an exception
|
||||
*
|
||||
* @param <T>
|
||||
* Return class type
|
||||
*/
|
||||
public interface SupplierWithException<T> {
|
||||
|
||||
T get() throws Exception;
|
||||
}
|
||||
@@ -2,11 +2,16 @@ plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
dependencies {
|
||||
implementation(libs.plugin.indra)
|
||||
implementation(libs.plugin.shadow)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.0")
|
||||
compileOnly(files(libs::class.java.protectionDomain.codeSource.location))
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
rootProject.name = "build-logic"
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
}
|
||||
// You're a great IDE IntelliJ, but sometimes you really annoy me. >:(
|
||||
// repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
|
||||
versionCatalogs {
|
||||
register("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,16 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("net.kyori.indra")
|
||||
}
|
||||
|
||||
indra {
|
||||
javaVersions {
|
||||
target(8)
|
||||
}
|
||||
github("NEZNAMY", "TAB") {
|
||||
ci(true)
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -13,18 +22,9 @@ tasks {
|
||||
))
|
||||
}
|
||||
javadoc {
|
||||
// This saves a decent bit of processing power on slow machines
|
||||
enabled = false
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
(options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet")
|
||||
}
|
||||
compileJava {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
options.release.set(8)
|
||||
options.compilerArgs.addAll(listOf("-nowarn", "-Xlint:-unchecked", "-Xlint:-deprecation"))
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,12 @@
|
||||
import gradle.kotlin.dsl.accessors._4cbb0020bd75d48acc42cabded5fdbb7.publishing
|
||||
import org.gradle.api.artifacts.repositories.PasswordCredentials
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.repositories
|
||||
|
||||
plugins {
|
||||
id("tab.base-conventions")
|
||||
`maven-publish`
|
||||
id("net.kyori.indra.publishing")
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "krypton"
|
||||
url = uri("https://repo.kryptonmc.org/releases")
|
||||
credentials(PasswordCredentials::class)
|
||||
}
|
||||
}
|
||||
publications.create<MavenPublication>("mavenJava") {
|
||||
groupId = rootProject.group as String
|
||||
artifactId = "tab-${project.name}"
|
||||
version = rootProject.version as String
|
||||
from(components["java"])
|
||||
indra {
|
||||
publishReleasesTo("krypton", "https://repo.kryptonmc.org/releases")
|
||||
publishSnapshotsTo("krypton", "https://repo.kryptonmc.org/snapshots")
|
||||
configurePublications {
|
||||
artifactId = "tab-$artifactId"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ val platforms = setOf(
|
||||
projects.bukkit,
|
||||
projects.bungeecord,
|
||||
// projects.krypton,
|
||||
// projects.velocity
|
||||
projects.velocity
|
||||
).map { it.dependencyProject }
|
||||
|
||||
val special = setOf(
|
||||
|
||||
@@ -6,7 +6,6 @@ dependencies {
|
||||
compileOnly(libs.vault)
|
||||
compileOnly(libs.via)
|
||||
compileOnly(libs.authlib)
|
||||
compileOnly(libs.libsDisguises)
|
||||
compileOnly(libs.essentials) {
|
||||
exclude("org.spigotmc", "spigot-api")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
@@ -8,8 +9,6 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* The core for bukkit forwarding events into all enabled features
|
||||
*/
|
||||
@@ -36,8 +35,8 @@ public class BukkitEventListener implements Listener {
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onQuit(PlayerQuitEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask(() -> TAB.getInstance().getFeatureManager().onQuit(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
if (TabAPI.getInstance().isPluginDisabled()) return;
|
||||
TabAPI.getInstance().getThreadManager().runTask(() -> TabAPI.getInstance().getFeatureManager().onQuit(TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,8 +47,9 @@ public class BukkitEventListener implements Listener {
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask(() -> TAB.getInstance().getFeatureManager().onJoin(new BukkitTabPlayer(e.getPlayer(), platform.getProtocolVersion(e.getPlayer()))));
|
||||
if (TabAPI.getInstance().isPluginDisabled()) return;
|
||||
TabAPI.getInstance().getThreadManager().runTask(() ->
|
||||
TabAPI.getInstance().getFeatureManager().onJoin(new BukkitTabPlayer(e.getPlayer(), platform.getProtocolVersion(e.getPlayer()))));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,8 +60,9 @@ public class BukkitEventListener implements Listener {
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onWorldChange(PlayerChangedWorldEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask(() -> TAB.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName()));
|
||||
if (TabAPI.getInstance().isPluginDisabled()) return;
|
||||
TabAPI.getInstance().getThreadManager().runTask(() ->
|
||||
TabAPI.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +73,8 @@ public class BukkitEventListener implements Listener {
|
||||
*/
|
||||
@EventHandler
|
||||
public void onCommand(PlayerCommandPreprocessEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
if (TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
if (TabAPI.getInstance().isPluginDisabled()) return;
|
||||
if (TabAPI.getInstance().getFeatureManager().onCommand(
|
||||
TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,29 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import me.neznamy.tab.api.chat.WrappedChatComponent;
|
||||
import me.neznamy.tab.api.protocol.*;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.chat.ChatComponentEntity;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.chat.WrappedChatComponent;
|
||||
import me.neznamy.tab.api.protocol.*;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class BukkitPacketBuilder extends PacketBuilder {
|
||||
@@ -51,9 +44,10 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
* Constructs new instance
|
||||
*/
|
||||
public BukkitPacketBuilder() {
|
||||
if (nms.getMinorVersion() >= 13) {
|
||||
if (nms.getMinorVersion() >= 19) {
|
||||
entityIds.put(EntityType.ARMOR_STAND, 2);
|
||||
} else if (nms.getMinorVersion() >= 13) {
|
||||
entityIds.put(EntityType.ARMOR_STAND, 1);
|
||||
entityIds.put(EntityType.WITHER, 83);
|
||||
} else {
|
||||
entityIds.put(EntityType.WITHER, 64);
|
||||
if (nms.getMinorVersion() >= 8){
|
||||
@@ -79,41 +73,76 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
@Override
|
||||
public Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
Object component = toNMSComponent(packet.getMessage(), clientVersion);
|
||||
if (nms.getMinorVersion() >= 16) {
|
||||
return nms.newPacketPlayOutChat.newInstance(component, nms.ChatMessageType_values[packet.getType().ordinal()], UUID.randomUUID());
|
||||
}
|
||||
if (nms.getMinorVersion() >= 12) {
|
||||
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf((Class<Enum>) nms.ChatMessageType, packet.getType().toString()));
|
||||
}
|
||||
if (nms.getMinorVersion() >= 8) {
|
||||
if (nms.getMinorVersion() >= 19)
|
||||
try {
|
||||
return nms.newPacketPlayOutChat.newInstance(component, packet.getType() == PacketPlayOutChat.ChatMessageType.GAME_INFO);
|
||||
} catch (Exception e) {
|
||||
//1.19.0
|
||||
return nms.newPacketPlayOutChat.newInstance(component, packet.getType().ordinal());
|
||||
}
|
||||
if (nms.getMinorVersion() >= 16)
|
||||
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.getType().toString()), UUID.randomUUID());
|
||||
if (nms.getMinorVersion() >= 12)
|
||||
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.getType().toString()));
|
||||
if (nms.getMinorVersion() >= 8)
|
||||
return nms.newPacketPlayOutChat.newInstance(component, (byte) packet.getType().ordinal());
|
||||
}
|
||||
if (nms.getMinorVersion() == 7) {
|
||||
if (nms.getMinorVersion() == 7)
|
||||
return nms.newPacketPlayOutChat.newInstance(component);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
if (nms.getMinorVersion() < 8) return null;
|
||||
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(nms.EnumPlayerInfoAction_values[packet.getAction().ordinal()], Array.newInstance(nms.EntityPlayer, 0));
|
||||
List<Object> items = new ArrayList<>();
|
||||
for (PlayerInfoData data : packet.getEntries()) {
|
||||
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
|
||||
if (data.getSkin() != null) profile.getProperties().put("textures", new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
|
||||
List<Object> parameters = new ArrayList<>();
|
||||
if (nms.newPlayerInfoData.getParameterCount() == 5) {
|
||||
parameters.add(nmsPacket);
|
||||
if (nms.ClientboundPlayerInfoRemovePacket != null) {
|
||||
//1.19.3+
|
||||
if (packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
|
||||
return nms.newClientboundPlayerInfoRemovePacket.newInstance(
|
||||
packet.getEntries().stream().map(PlayerInfoData::getUniqueId).collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
parameters.add(profile);
|
||||
parameters.add(data.getLatency());
|
||||
parameters.add(data.getGameMode() == null ? null : nms.EnumGamemode_values[nms.EnumGamemode_values.length-EnumGamemode.VALUES.length+data.getGameMode().ordinal()]); //not_set was removed in 1.17
|
||||
parameters.add(data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion));
|
||||
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
|
||||
Enum[] array = packet.getActions().stream().map(action -> Enum.valueOf(nms.EnumPlayerInfoAction, action.toString())).toArray(Enum[]::new);
|
||||
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(EnumSet.of(array[0], array), Collections.emptyList());
|
||||
List<Object> items = new ArrayList<>();
|
||||
for (PlayerInfoData data : packet.getEntries()) {
|
||||
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
|
||||
if (data.getSkin() != null) profile.getProperties().put("textures",
|
||||
new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
|
||||
Object obj = nms.newPlayerInfoData.newInstance(
|
||||
data.getUniqueId(),
|
||||
profile,
|
||||
data.isListed(),
|
||||
data.getLatency(),
|
||||
data.getGameMode() == null ? null : Enum.valueOf(nms.EnumGamemode, data.getGameMode().toString()),
|
||||
data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion),
|
||||
data.getProfilePublicKey() == null ? null : nms.newRemoteChatSession$Data.newInstance(data.getChatSessionId(), data.getProfilePublicKey()));
|
||||
items.add(obj);
|
||||
}
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
|
||||
return nmsPacket;
|
||||
} else {
|
||||
//1.19.2-
|
||||
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
|
||||
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
|
||||
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(Enum.valueOf(nms.EnumPlayerInfoAction, action.toString()), Array.newInstance(nms.EntityPlayer, 0));
|
||||
List<Object> items = new ArrayList<>();
|
||||
for (PlayerInfoData data : packet.getEntries()) {
|
||||
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
|
||||
if (data.getSkin() != null) profile.getProperties().put("textures", new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
|
||||
List<Object> parameters = new ArrayList<>();
|
||||
if (nms.newPlayerInfoData.getParameterTypes()[0] == nms.PacketPlayOutPlayerInfo) {
|
||||
parameters.add(nmsPacket);
|
||||
}
|
||||
parameters.add(profile);
|
||||
parameters.add(data.getLatency());
|
||||
parameters.add(data.getGameMode() == null ? null : Enum.valueOf(nms.EnumGamemode, data.getGameMode().toString()));
|
||||
parameters.add(data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion));
|
||||
if (nms.getMinorVersion() >= 19) parameters.add(data.getProfilePublicKey());
|
||||
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
|
||||
}
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
|
||||
return nmsPacket;
|
||||
}
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,7 +168,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
if (nms.getMinorVersion() >= 13) {
|
||||
return nms.newPacketPlayOutScoreboardObjective.newInstance(nms.newScoreboardObjective.newInstance(null, packet.getObjectiveName(), null,
|
||||
toNMSComponent(IChatBaseComponent.optimizedComponent(displayName), clientVersion),
|
||||
packet.getRenderType() == null ? null : nms.EnumScoreboardHealthDisplay_values[packet.getRenderType().ordinal()]),
|
||||
packet.getRenderType() == null ? null : Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString())),
|
||||
packet.getAction());
|
||||
}
|
||||
|
||||
@@ -147,7 +176,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME, packet.getObjectiveName());
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_DISPLAYNAME, displayName);
|
||||
if (nms.getMinorVersion() >= 8 && packet.getRenderType() != null) {
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_RENDERTYPE, Enum.valueOf((Class<Enum>) nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString()));
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_RENDERTYPE, Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString()));
|
||||
}
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_METHOD, packet.getAction());
|
||||
return nmsPacket;
|
||||
@@ -156,7 +185,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
@Override
|
||||
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
if (nms.getMinorVersion() >= 13) {
|
||||
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(nms.EnumScoreboardAction_values[packet.getAction().ordinal()], packet.getObjectiveName(), packet.getPlayer(), packet.getScore());
|
||||
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(Enum.valueOf(nms.EnumScoreboardAction, packet.getAction().toString()), packet.getObjectiveName(), packet.getPlayer(), packet.getScore());
|
||||
}
|
||||
if (packet.getAction() == PacketPlayOutScoreboardScore.Action.REMOVE) {
|
||||
return nms.newPacketPlayOutScoreboardScore_String.newInstance(packet.getPlayer());
|
||||
@@ -196,9 +225,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
case 2:
|
||||
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, false);
|
||||
case 3:
|
||||
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().toArray(new String[0])[0], nms.PacketPlayOutScoreboardTeam_PlayerAction_values[0]);
|
||||
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().iterator().next(), Enum.valueOf(nms.PacketPlayOutScoreboardTeam_PlayerAction, "ADD"));
|
||||
case 4:
|
||||
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().toArray(new String[0])[0], nms.PacketPlayOutScoreboardTeam_PlayerAction_values[1]);
|
||||
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().iterator().next(), Enum.valueOf(nms.PacketPlayOutScoreboardTeam_PlayerAction, "REMOVE"));
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid action: " + packet.getAction());
|
||||
}
|
||||
@@ -234,7 +263,12 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object build(PacketPlayOutEntityMetadata packet) throws ReflectiveOperationException {
|
||||
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
|
||||
if (nms.newPacketPlayOutEntityMetadata.getParameterCount() == 2) {
|
||||
//1.19.3+
|
||||
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), nms.DataWatcher_b.invoke(packet.getDataWatcher().toNMS()));
|
||||
} else {
|
||||
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,7 +288,6 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance();
|
||||
}
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYID, packet.getEntityId());
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, entityIds.get(packet.getEntityType()));
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_YAW, (byte)(packet.getLocation().getYaw() * 256.0f / 360.0f));
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_PITCH, (byte)(packet.getLocation().getPitch() * 256.0f / 360.0f));
|
||||
if (nms.getMinorVersion() <= 14) {
|
||||
@@ -270,6 +303,12 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_Y, floor(packet.getLocation().getY()*32));
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_Z, floor(packet.getLocation().getZ()*32));
|
||||
}
|
||||
int id = entityIds.get(packet.getEntityType());
|
||||
if (nms.getMinorVersion() >= 19) {
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, nms.EntityTypes_ARMOR_STAND); // :(
|
||||
} else {
|
||||
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, id);
|
||||
}
|
||||
return nmsPacket;
|
||||
}
|
||||
|
||||
@@ -324,9 +363,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(prefix), clientVersion));
|
||||
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(suffix), clientVersion));
|
||||
EnumChatFormat format = packet.getColor() != null ? packet.getColor() : EnumChatFormat.lastColorsOf(prefix);
|
||||
nms.ScoreboardTeam_setColor.invoke(team, nms.EnumChatFormat_values[format.ordinal()]);
|
||||
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNameTagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
|
||||
nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
|
||||
nms.ScoreboardTeam_setColor.invoke(team, Enum.valueOf(nms.EnumChatFormat, format.toString()));
|
||||
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(nms.EnumNameTagVisibility, String.valueOf(packet.getNameTagVisibility()).equals("always") ? "ALWAYS" : "NEVER"));
|
||||
nms.ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(nms.EnumTeamPush, String.valueOf(packet.getCollisionRule()).equals("always") ? "ALWAYS" : "NEVER"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,30 +385,71 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
private void createTeamLegacy(PacketPlayOutScoreboardTeam packet, Object team, String prefix, String suffix) throws ReflectiveOperationException {
|
||||
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, prefix);
|
||||
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, suffix);
|
||||
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNameTagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
|
||||
if (nms.getMinorVersion() >= 9) nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
|
||||
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(nms.EnumNameTagVisibility, String.valueOf(packet.getNameTagVisibility()).equals("always") ? "ALWAYS" : "NEVER"));
|
||||
if (nms.getMinorVersion() >= 9) nms.ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(nms.EnumTeamPush, String.valueOf(packet.getCollisionRule()).equals("always") ? "ALWAYS" : "NEVER"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketPlayOutPlayerInfo readPlayerInfo(Object nmsPacket, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
if (nms.getMinorVersion() < 8) return null;
|
||||
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
|
||||
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
|
||||
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
|
||||
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
|
||||
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
|
||||
IChatBaseComponent listName = nmsComponent == null ? null : new WrappedChatComponent(nmsComponent);
|
||||
Skin skin = null;
|
||||
if (!profile.getProperties().get("textures").isEmpty()) {
|
||||
Property pr = (Property) profile.getProperties().get("textures").iterator().next();
|
||||
skin = new Skin(pr.getValue(), pr.getSignature());
|
||||
if (nms.ClientboundPlayerInfoRemovePacket != null) {
|
||||
//1.19.3+
|
||||
if (nms.ClientboundPlayerInfoRemovePacket.isInstance(nmsPacket)) {
|
||||
List<UUID> entries = (List<UUID>) nms.ClientboundPlayerInfoRemovePacket_getEntries.invoke(nmsPacket);
|
||||
return new PacketPlayOutPlayerInfo(
|
||||
EnumPlayerInfoAction.REMOVE_PLAYER,
|
||||
entries.stream().map(PlayerInfoData::new).collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), skin,
|
||||
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName));
|
||||
EnumSet<?> set = (EnumSet<?>) nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket);
|
||||
EnumPlayerInfoAction[] array = set.stream().map(action -> EnumPlayerInfoAction.valueOf(action.toString())).toArray(EnumPlayerInfoAction[]::new);
|
||||
EnumSet<EnumPlayerInfoAction> actions = EnumSet.of(array[0], array);
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
|
||||
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
|
||||
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
|
||||
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
|
||||
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
|
||||
Skin skin = null;
|
||||
if (!profile.getProperties().get("textures").isEmpty()) {
|
||||
Property pr = profile.getProperties().get("textures").iterator().next();
|
||||
skin = new Skin(pr.getValue(), pr.getSignature());
|
||||
}
|
||||
Object remoteChatSession = nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData);
|
||||
listData.add(
|
||||
new PlayerInfoData(
|
||||
profile.getName(),
|
||||
profile.getId(),
|
||||
skin,
|
||||
(boolean) nms.PlayerInfoData_isListed.invoke(nmsData),
|
||||
(int) nms.PlayerInfoData_getLatency.invoke(nmsData),
|
||||
gameMode,
|
||||
nmsComponent == null ? null : new WrappedChatComponent(nmsComponent),
|
||||
remoteChatSession == null ? null : (UUID) nms.RemoteChatSession$Data_getSessionId.invoke(remoteChatSession),
|
||||
remoteChatSession == null ? null : nms.RemoteChatSession$Data_getProfilePublicKey.invoke(remoteChatSession)));
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(actions, listData);
|
||||
} else {
|
||||
//1.19.2-
|
||||
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
|
||||
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
|
||||
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
|
||||
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
|
||||
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
|
||||
IChatBaseComponent listName = nmsComponent == null ? null : new WrappedChatComponent(nmsComponent);
|
||||
Skin skin = null;
|
||||
if (!profile.getProperties().get("textures").isEmpty()) {
|
||||
Property pr = profile.getProperties().get("textures").iterator().next();
|
||||
skin = new Skin(pr.getValue(), pr.getSignature());
|
||||
}
|
||||
Object profilePublicKey = nms.getMinorVersion() >= 19 ? nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData) : null;
|
||||
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), skin, true,
|
||||
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName, null, profilePublicKey));
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(action, listData);
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(action, listData);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -389,7 +469,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds entity BossBar packet
|
||||
* Builds entity packet representing requested BossBar packet using Wither on 1.8- clients.
|
||||
*
|
||||
* @param packet
|
||||
* packet to build
|
||||
@@ -417,6 +497,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
}
|
||||
if (packet.getAction() == Action.ADD) {
|
||||
w.helper().setEntityFlags((byte) 32);
|
||||
w.helper().setWitherInvulnerableTime(880); // Magic number
|
||||
return build(new PacketPlayOutSpawnEntityLiving(entityId, new UUID(0, 0), EntityType.WITHER, new Location(null, 0,0,0), w));
|
||||
} else {
|
||||
return build(new PacketPlayOutEntityMetadata(entityId, w));
|
||||
@@ -436,13 +517,15 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts TAB's IChatBaseComponent into minecraft's component.
|
||||
* Converts TAB's IChatBaseComponent into minecraft's component using String deserialization.
|
||||
* If the requested component is found in cache, it is returned. If not, it is created, added into cache and returned.
|
||||
* If {@code component} is {@code null}, returns {@code null}
|
||||
*
|
||||
* @param component
|
||||
* component to convert
|
||||
* @param clientVersion
|
||||
* client version used to decide RGB conversion
|
||||
* @return converted component
|
||||
* @return converted component or {@code null} if {@code component} is {@code null}
|
||||
* @throws ReflectiveOperationException
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
@@ -452,62 +535,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
Map<IChatBaseComponent, Object> cache = clientVersion.getMinorVersion() >= 16 ? componentCacheModern : componentCacheLegacy;
|
||||
if (cache.containsKey(component)) return cache.get(component);
|
||||
if (cache.size() > 10000) cache.clear();
|
||||
Object chat = nms.newChatComponentText.newInstance(component.getText());
|
||||
Object modifier;
|
||||
Object clickEvent = component.getModifier().getClickEvent() == null ? null : nms.newChatClickable.newInstance(Enum.valueOf((Class<Enum>) nms.EnumClickAction,
|
||||
component.getModifier().getClickEvent().getAction().toString().toUpperCase()), component.getModifier().getClickEvent().getValue());
|
||||
if (nms.getMinorVersion() >= 16) {
|
||||
Object color = null;
|
||||
if (component.getModifier().getColor() != null) {
|
||||
if (clientVersion.getMinorVersion() >= 16) {
|
||||
color = nms.ChatHexColor_ofInt.invoke(null, (component.getModifier().getColor().getRed() << 16) + (component.getModifier().getColor().getGreen() << 8) + component.getModifier().getColor().getBlue());
|
||||
} else {
|
||||
color = nms.ChatHexColor_ofString.invoke(null, component.getModifier().getColor().getLegacyColor().toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
Object hoverEvent = null;
|
||||
if (component.getModifier().getHoverEvent() != null) {
|
||||
Object nmsAction = nms.EnumHoverAction_a.invoke(null, component.getModifier().getHoverEvent().getAction().toString().toLowerCase());
|
||||
switch (component.getModifier().getHoverEvent().getAction()) {
|
||||
case SHOW_TEXT:
|
||||
hoverEvent = nms.newChatHoverable.newInstance(nmsAction, toNMSComponent(component.getModifier().getHoverEvent().getValue(), clientVersion));
|
||||
break;
|
||||
case SHOW_ENTITY:
|
||||
hoverEvent = nms.EnumHoverAction_fromJson.invoke(nmsAction, ((ChatComponentEntity) component.getModifier().getHoverEvent().getValue()).toJson());
|
||||
break;
|
||||
case SHOW_ITEM:
|
||||
hoverEvent = nms.EnumHoverAction_fromLegacyComponent.invoke(nmsAction, toNMSComponent(component.getModifier().getHoverEvent().getValue(), clientVersion));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
modifier = nms.newChatModifier.newInstance(
|
||||
color,
|
||||
component.getModifier().getBold(),
|
||||
component.getModifier().getItalic(),
|
||||
component.getModifier().getUnderlined(),
|
||||
component.getModifier().getStrikethrough(),
|
||||
component.getModifier().getObfuscated(),
|
||||
clickEvent, hoverEvent, null, null);
|
||||
} else {
|
||||
modifier = nms.newChatModifier.newInstance();
|
||||
if (component.getModifier().getColor() != null) nms.setField(modifier, nms.ChatModifier_color, Enum.valueOf((Class<Enum>) nms.EnumChatFormat, component.getModifier().getColor().getLegacyColor().toString()));
|
||||
nms.setField(modifier, nms.ChatModifier_bold, component.getModifier().getBold());
|
||||
nms.setField(modifier, nms.ChatModifier_italic, component.getModifier().getItalic());
|
||||
nms.setField(modifier, nms.ChatModifier_underlined, component.getModifier().getUnderlined());
|
||||
nms.setField(modifier, nms.ChatModifier_strikethrough, component.getModifier().getStrikethrough());
|
||||
nms.setField(modifier, nms.ChatModifier_obfuscated, component.getModifier().getObfuscated());
|
||||
if (clickEvent != null) nms.setField(modifier, nms.ChatModifier_clickEvent, clickEvent);
|
||||
if (component.getModifier().getHoverEvent() != null) {
|
||||
nms.setField(modifier, nms.ChatModifier_hoverEvent, nms.newChatHoverable.newInstance(nms.EnumHoverAction_a.invoke(null,
|
||||
component.getModifier().getHoverEvent().getAction().toString().toLowerCase()), toNMSComponent(component.getModifier().getHoverEvent().getValue(), clientVersion)));
|
||||
}
|
||||
}
|
||||
nms.setField(chat, nms.ChatBaseComponent_modifier, modifier);
|
||||
for (IChatBaseComponent extra : component.getExtra()) {
|
||||
nms.ChatComponentText_addSibling.invoke(chat, toNMSComponent(extra, clientVersion));
|
||||
}
|
||||
Object chat = nms.ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion));
|
||||
cache.put(component, chat);
|
||||
return chat;
|
||||
}
|
||||
@@ -523,7 +551,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
*/
|
||||
private Object newScoreboardObjective(String objectiveName) throws ReflectiveOperationException {
|
||||
if (nms.getMinorVersion() >= 13) {
|
||||
return nms.newScoreboardObjective.newInstance(null, objectiveName, null, nms.newChatComponentText.newInstance(""), null);
|
||||
return nms.newScoreboardObjective.newInstance(null, objectiveName, null, toNMSComponent(new IChatBaseComponent(""), TabAPI.getInstance().getServerVersion()), null);
|
||||
}
|
||||
return nms.newScoreboardObjective.newInstance(null, objectiveName, nms.IScoreboardCriteria_self.get(null));
|
||||
}
|
||||
|
||||
@@ -9,10 +9,11 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -66,7 +67,8 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
@Override
|
||||
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
|
||||
try {
|
||||
if (nms.PacketPlayOutPlayerInfo.isInstance(packet)) {
|
||||
if (nms.PacketPlayOutPlayerInfo.isInstance(packet) ||
|
||||
(nms.ClientboundPlayerInfoRemovePacket != null && nms.ClientboundPlayerInfoRemovePacket.isInstance(packet))) {
|
||||
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
|
||||
return;
|
||||
}
|
||||
@@ -117,9 +119,10 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
newList.add(entry);
|
||||
continue;
|
||||
}
|
||||
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
|
||||
if (!((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
|
||||
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(p.getTeamName())) {
|
||||
logTeamOverride(teamName, p.getName(), p.getTeamName());
|
||||
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(sorting.getShortTeamName(p))) {
|
||||
logTeamOverride(teamName, p.getName(), sorting.getShortTeamName(p));
|
||||
} else {
|
||||
newList.add(entry);
|
||||
}
|
||||
|
||||
+62
-15
@@ -5,9 +5,16 @@ import java.lang.reflect.Method;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import net.ess3.api.events.NickChangeEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
@@ -16,6 +23,7 @@ import com.earth2me.essentials.Essentials;
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager;
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Bukkit registry to register bukkit-only and universal placeholders
|
||||
@@ -25,21 +33,38 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
|
||||
/** Number formatter for 2 decimal places */
|
||||
private final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
|
||||
|
||||
private Object chat; // Object because Fabric
|
||||
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
|
||||
/** JavaPlugin reference for registering events */
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
/** Vault Chat hook */
|
||||
private Chat chat;
|
||||
|
||||
/** NMS server to get TPS from on spigot */
|
||||
private Object server;
|
||||
|
||||
/** TPS field*/
|
||||
private Field recentTps;
|
||||
|
||||
/** Detection for presence of Paper's TPS getter */
|
||||
private Method paperTps;
|
||||
|
||||
/** Detection for presence of Paper's MSPT getter */
|
||||
private Method paperMspt;
|
||||
|
||||
/** Detection for presence of Purpur's AFK getter */
|
||||
private Method purpurIsAfk;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
* Constructs new instance with given parameter and loads hooks
|
||||
*
|
||||
* @param plugin
|
||||
* reference to the plugin's main class
|
||||
*/
|
||||
public BukkitPlaceholderRegistry() {
|
||||
public BukkitPlaceholderRegistry(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
numberFormat.setMaximumFractionDigits(2);
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
|
||||
RegisteredServiceProvider<?> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
|
||||
RegisteredServiceProvider<Chat> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
|
||||
if (rspChat != null) chat = rspChat.getProvider();
|
||||
}
|
||||
try {
|
||||
@@ -56,7 +81,6 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void registerPlaceholders(PlaceholderManager manager) {
|
||||
super.registerPlaceholders(manager);
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((Player) p.getPlayer()).getDisplayName());
|
||||
if (paperTps != null) {
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> formatTPS(Bukkit.getTPS()[0]));
|
||||
@@ -74,26 +98,49 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
|
||||
if (paperMspt != null) {
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.MSPT, 1000, () -> numberFormat.format(Bukkit.getAverageTickTime()));
|
||||
}
|
||||
Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.AFK, 500, p -> {
|
||||
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
|
||||
return purpurIsAfk != null && ((Player)p.getPlayer()).isAfk();
|
||||
});
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, 1000, p -> {
|
||||
String nickname = null;
|
||||
if (essentials != null)
|
||||
nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
|
||||
return nickname == null || nickname.length() == 0 ? p.getName() : nickname;
|
||||
});
|
||||
if (essentials != null) {
|
||||
PlayerPlaceholder nick = manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, p -> {
|
||||
String nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
|
||||
return nickname == null ? p.getName() : nickname;
|
||||
});
|
||||
Listener nickListener = new Listener() {
|
||||
@EventHandler
|
||||
public void onNickChange(NickChangeEvent e) {
|
||||
String name = e.getValue() == null ? e.getController().getName() : e.getValue();
|
||||
TabPlayer player = TAB.getInstance().getPlayer(e.getController().getUUID());
|
||||
if (player == null) return;
|
||||
nick.updateValue(player, name);
|
||||
}
|
||||
};
|
||||
nick.enableTriggerMode(() -> Bukkit.getPluginManager().registerEvents(nickListener, plugin),
|
||||
() -> HandlerList.unregisterAll(nickListener));
|
||||
} else {
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, TabPlayer::getName);
|
||||
}
|
||||
|
||||
if (chat != null) {
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, 1000, p -> ((Chat) chat).getPlayerPrefix((Player) p.getPlayer()));
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, 1000, p -> ((Chat) chat).getPlayerSuffix((Player) p.getPlayer()));
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, 1000, p -> chat.getPlayerPrefix((Player) p.getPlayer()));
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, 1000, p -> chat.getPlayerSuffix((Player) p.getPlayer()));
|
||||
} else {
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, -1, () -> "");
|
||||
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, -1, () -> "");
|
||||
}
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.HEALTH, 100, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
|
||||
super.registerPlaceholders(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats TPS using number formatter with 2 decimal places.
|
||||
*
|
||||
* @param tps
|
||||
* TPS to format
|
||||
* @return Formatted TPS as a String
|
||||
*/
|
||||
private String formatTPS(double tps) {
|
||||
return numberFormat.format(Math.min(20, tps));
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import com.earth2me.essentials.Essentials;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.platforms.bukkit.event.TabLoadEvent;
|
||||
import me.neznamy.tab.platforms.bukkit.event.TabPlayerLoadEvent;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
|
||||
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
|
||||
import me.neznamy.tab.platforms.bukkit.features.PetFix;
|
||||
@@ -15,10 +14,10 @@ import me.neznamy.tab.platforms.bukkit.features.unlimitedtags.BukkitNameTagX;
|
||||
import me.neznamy.tab.platforms.bukkit.permission.Vault;
|
||||
import me.neznamy.tab.shared.Platform;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import me.neznamy.tab.shared.permission.LuckPerms;
|
||||
import me.neznamy.tab.shared.permission.None;
|
||||
import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
@@ -43,7 +42,6 @@ public class BukkitPlatform extends Platform {
|
||||
/** Variables checking presence of other plugins to hook into */
|
||||
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API);
|
||||
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
|
||||
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
|
||||
private Plugin viaVersion;
|
||||
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
|
||||
|
||||
@@ -86,8 +84,9 @@ public class BukkitPlatform extends Platform {
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.getConfiguration().isPipelineInjection())
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
|
||||
new BukkitPlaceholderRegistry().registerPlaceholders(tab.getPlaceholderManager());
|
||||
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
|
||||
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.SORTING, new Sorting());
|
||||
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && tab.getServerVersion().getMinorVersion() >= 8) {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, new BukkitNameTagX(plugin));
|
||||
} else {
|
||||
@@ -182,16 +181,6 @@ public class BukkitPlatform extends Platform {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callLoadEvent() {
|
||||
Bukkit.getPluginManager().callEvent(new TabLoadEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callLoadEvent(TabPlayer player) {
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> Bukkit.getPluginManager().callEvent(new TabPlayerLoadEvent(player)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns status of LibsDisguises plugin presence
|
||||
*
|
||||
@@ -213,20 +202,6 @@ public class BukkitPlatform extends Platform {
|
||||
libsDisguises = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Essentials' main class if the plugin is installed, {@code null} if not
|
||||
*
|
||||
* @return Essentials instance or {@code null} if plugin is not installed
|
||||
*/
|
||||
public Essentials getEssentials() {
|
||||
return (Essentials) essentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigName() {
|
||||
return "bukkitconfig.yml";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets protocol version of requested player and returns it.
|
||||
*
|
||||
@@ -298,6 +273,7 @@ public class BukkitPlatform extends Platform {
|
||||
/**
|
||||
* Sends console message using ConsoleCommandSender, due to
|
||||
* Paper not translating colors correctly in Logger messages
|
||||
* and to allow RGB (at least on Paper, doesn't work on spigot)
|
||||
*
|
||||
* @param message
|
||||
* Message to send
|
||||
@@ -306,6 +282,9 @@ public class BukkitPlatform extends Platform {
|
||||
*/
|
||||
@Override
|
||||
public void sendConsoleMessage(String message, boolean translateColors) {
|
||||
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ? EnumChatFormat.color(message) : message));
|
||||
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ?
|
||||
EnumChatFormat.color(RGBUtils.getInstance().convertToBukkitFormat(message,
|
||||
TAB.getInstance().getServerVersion().getMinorVersion() >= 16))
|
||||
: message));
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,11 @@ 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.metadata.MetadataValue;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
import com.earth2me.essentials.Essentials;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
|
||||
@@ -23,10 +23,9 @@ import com.viaversion.viaversion.api.legacy.bossbar.BossFlag;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.libraryaddict.disguise.DisguiseAPI;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
@@ -56,7 +55,7 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
* Player's protocol network id
|
||||
*/
|
||||
public BukkitTabPlayer(Player p, int protocolVersion){
|
||||
super(p, p.getUniqueId(), p.getName(), "N/A", p.getWorld().getName(), protocolVersion);
|
||||
super(p, p.getUniqueId(), p.getName(), TAB.getInstance().getConfiguration().getServerName(), p.getWorld().getName(), protocolVersion, true);
|
||||
try {
|
||||
handle = NMSStorage.getInstance().getHandle.invoke(player);
|
||||
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
|
||||
@@ -243,8 +242,8 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
public boolean isDisguised() {
|
||||
try {
|
||||
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).isLibsDisguisesEnabled()) return false;
|
||||
return DisguiseAPI.isDisguised(getPlayer());
|
||||
} catch (LinkageError e) {
|
||||
return (boolean) Class.forName("me.libraryaddict.disguise.DisguiseAPI").getMethod("isDisguised", Entity.class).invoke(null, getPlayer());
|
||||
} catch (LinkageError | ReflectiveOperationException e) {
|
||||
//java.lang.NoClassDefFoundError: Could not initialize class me.libraryaddict.disguise.DisguiseAPI
|
||||
TAB.getInstance().getErrorManager().printError("Failed to check disguise status using LibsDisguises", e);
|
||||
((BukkitPlatform)TAB.getInstance().getPlatform()).setLibsDisguisesEnabled(false);
|
||||
@@ -252,7 +251,6 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Skin getSkin() {
|
||||
try {
|
||||
@@ -278,10 +276,7 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
|
||||
@Override
|
||||
public boolean isVanished() {
|
||||
Essentials essentials = ((BukkitPlatform)TAB.getInstance().getPlatform()).getEssentials();
|
||||
if (essentials != null && essentials.getUser(getUniqueId()).isVanished()) return true;
|
||||
List<MetadataValue> metadata = getPlayer().getMetadata("vanished");
|
||||
return !metadata.isEmpty() && metadata.get(0).asBoolean();
|
||||
return getPlayer().getMetadata("vanished").stream().anyMatch(MetadataValue::asBoolean);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@@ -290,6 +285,40 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
return getPlayer().getGameMode().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProfilePublicKey() {
|
||||
if (NMSStorage.getInstance().getMinorVersion() < 19) return null;
|
||||
try {
|
||||
if (NMSStorage.getInstance().RemoteChatSession != null) {
|
||||
//1.19.3+
|
||||
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
|
||||
if (session == null) return null;
|
||||
Object key = NMSStorage.getInstance().RemoteChatSession_getProfilePublicKey.invoke(session);
|
||||
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
|
||||
} else {
|
||||
Object key = NMSStorage.getInstance().EntityHuman_ProfilePublicKey.get(handle);
|
||||
if (key == null) return null;
|
||||
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get profile key of " + getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getChatSessionId() {
|
||||
if (NMSStorage.getInstance().RemoteChatSession != null) return null;
|
||||
try {
|
||||
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
|
||||
if (session == null) return null;
|
||||
return (UUID) NMSStorage.getInstance().RemoteChatSession_getSessionId.invoke(session);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get chat session of " + getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel() {
|
||||
try {
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.util.SupplierWithException;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.*;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Main class for Bukkit platform
|
||||
*/
|
||||
@@ -54,7 +55,7 @@ public class Main extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
//null check due to L34 return making L37 not run
|
||||
//null check due to compatibility check making instance not get set on unsupported versions
|
||||
if (TAB.getInstance() != null) TAB.getInstance().unload();
|
||||
}
|
||||
|
||||
@@ -70,28 +71,43 @@ public class Main extends JavaPlugin {
|
||||
"v1_5_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3",
|
||||
"v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2", "v1_8_R3",
|
||||
"v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1", "v1_13_R1", "v1_13_R2",
|
||||
"v1_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1", "v1_18_R1", "v1_18_R2");
|
||||
"v1_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1", "v1_18_R1",
|
||||
"v1_18_R2", "v1_19_R1", "v1_19_R2");
|
||||
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
NMSStorage.setInstance(new NMSStorage());
|
||||
int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
|
||||
NMSStorage.setInstance(getNMSLoader());
|
||||
if (supportedVersions.contains(serverPackage)) {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
|
||||
return true;
|
||||
} else {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on 1.5.x - 1.18.2. Disabling just to stay safe."));
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on 1.5.2 - 1.19.3. Disabling just to stay safe."));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
} catch (IllegalStateException ex) {
|
||||
if (supportedVersions.contains(serverPackage)) {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report the error below (include your server version & fork too)"));
|
||||
getLogger().log(Level.SEVERE, "", ex);
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report this issue (include your server version & fork too)"));
|
||||
} else {
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.x - 1.18.2. Disabling."));
|
||||
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.2 - 1.19.3. Disabling."));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private NMSStorage getNMSLoader() {
|
||||
List<SupplierWithException<NMSStorage>> loaders = new ArrayList<>();
|
||||
loaders.add(BukkitLegacyNMSStorage::new);
|
||||
loaders.add(BukkitModernNMSStorage::new);
|
||||
loaders.add(MojangModernNMSStorage::new);
|
||||
loaders.add(ThermosNMSStorage::new);
|
||||
for (SupplierWithException<NMSStorage> loader : loaders) {
|
||||
try {
|
||||
return loader.get();
|
||||
} catch (Exception ignored){}
|
||||
}
|
||||
throw new IllegalStateException("Unsupported server version");
|
||||
}
|
||||
|
||||
/**
|
||||
* Command handler for /tab command
|
||||
*/
|
||||
@@ -99,7 +115,7 @@ public class Main extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
if (TAB.getInstance().isDisabled()) {
|
||||
if (TabAPI.getInstance().isPluginDisabled()) {
|
||||
for (String message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
|
||||
sender.sendMessage(EnumChatFormat.color(message));
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.event;
|
||||
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Bukkit event that is called each time TAB fully loads. This includes server startup, reload,
|
||||
* /tab reload and /plugman reload tab
|
||||
*
|
||||
* @deprecated for removal, use {@link me.neznamy.tab.api.event.plugin.TabLoadEvent}
|
||||
*/
|
||||
@Deprecated
|
||||
public class TabLoadEvent extends Event {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers(){
|
||||
return getHandlerList();
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.event;
|
||||
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
/**
|
||||
* Bukkit event that is called when player is successfully loaded after joining. This also includes plugin reloading.
|
||||
*
|
||||
* @deprecated for removal, use {@link me.neznamy.tab.api.event.player.PlayerLoadEvent}
|
||||
*/
|
||||
@Deprecated
|
||||
public class TabPlayerLoadEvent extends Event {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
private final TabPlayer player;
|
||||
|
||||
public TabPlayerLoadEvent(TabPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers(){
|
||||
return getHandlerList();
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public TabPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
+16
-7
@@ -2,9 +2,11 @@ package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -18,7 +20,7 @@ import java.util.WeakHashMap;
|
||||
*/
|
||||
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion {
|
||||
|
||||
/** Map holding all placeholder values for all players */
|
||||
/** Map holding all values for all players for easy and high-performance access */
|
||||
private final Map<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
|
||||
|
||||
@Override
|
||||
@@ -44,15 +46,22 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
|
||||
@Override
|
||||
public String onPlaceholderRequest(Player player, @NotNull String identifier){
|
||||
if (identifier.startsWith("replace_")) {
|
||||
String placeholder = "%" + identifier.substring(8) + "%";
|
||||
String output = PlaceholderAPI.setPlaceholders(player, placeholder);
|
||||
return TAB.getInstance().getPlaceholderManager().findReplacement(placeholder, output);
|
||||
String text = "%" + identifier.substring(8) + "%";
|
||||
String textBefore;
|
||||
do {
|
||||
textBefore = text;
|
||||
for (String placeholder : TAB.getInstance().getPlaceholderManager().detectPlaceholders(text)) {
|
||||
text = text.replace(placeholder, TabAPI.getInstance().getPlaceholderManager().findReplacement(placeholder,
|
||||
PlaceholderAPI.setPlaceholders(player, placeholder)));
|
||||
}
|
||||
} while (!textBefore.equals(text));
|
||||
return text;
|
||||
}
|
||||
if (identifier.startsWith("placeholder_")) {
|
||||
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", TAB.getInstance().getPlaceholderManager());
|
||||
TabAPI.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", (TabFeature) TabAPI.getInstance().getPlaceholderManager());
|
||||
}
|
||||
if (player == null) return "<Player cannot be null>";
|
||||
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
|
||||
TabPlayer p = TabAPI.getInstance().getPlayer(player.getUniqueId());
|
||||
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
|
||||
return values.get(p).get(identifier);
|
||||
}
|
||||
|
||||
+24
-11
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -15,7 +16,7 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
@@ -24,13 +25,10 @@ import me.neznamy.tab.shared.TAB;
|
||||
@SuppressWarnings("deprecation")
|
||||
public class PerWorldPlayerList extends TabFeature implements Listener {
|
||||
|
||||
//plugin instance
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
//config options
|
||||
private final boolean allowBypass = TAB.getInstance().getConfiguration().getConfig().getBoolean("per-world-playerlist.allow-bypass-permission", false);
|
||||
private final List<String> ignoredWorlds = TAB.getInstance().getConfiguration().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
|
||||
private final Map<String, List<String>> sharedWorlds = TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
|
||||
/** Config options */
|
||||
private final boolean allowBypass = TabAPI.getInstance().getConfig().getBoolean("per-world-playerlist.allow-bypass-permission", false);
|
||||
private final List<String> ignoredWorlds = TabAPI.getInstance().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
|
||||
private final Map<String, List<String>> sharedWorlds = TabAPI.getInstance().getConfig().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
@@ -40,14 +38,13 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
|
||||
*/
|
||||
public PerWorldPlayerList(JavaPlugin plugin) {
|
||||
super("Per world PlayerList", null);
|
||||
this.plugin = plugin;
|
||||
TAB.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
TabAPI.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getOnlinePlayers().forEach(this::checkPlayer));
|
||||
Bukkit.getOnlinePlayers().forEach(this::checkPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,6 +71,14 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
|
||||
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs visibility check on the player. Shows players this player should see and does not,
|
||||
* hides players the player should not see, shows the player to those who should see player and hides
|
||||
* from those, who should not see.
|
||||
*
|
||||
* @param p
|
||||
* Player to update
|
||||
*/
|
||||
private void checkPlayer(Player p) {
|
||||
for (Player all : Bukkit.getOnlinePlayers()){
|
||||
if (all == p) continue;
|
||||
@@ -84,6 +89,14 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if viewer should see target player, {@code false} if not.
|
||||
* @param viewer
|
||||
* Player viewing the TabList
|
||||
* @param target
|
||||
* Target displayed in the TabList
|
||||
* @return {@code true} if viewer should see target, {@code false} if not.
|
||||
*/
|
||||
private boolean shouldSee(Player viewer, Player target) {
|
||||
if (target == viewer) return true;
|
||||
if ((allowBypass && viewer.hasPermission(TabConstants.Permission.PER_WORLD_PLAYERLIST_BYPASS)) || ignoredWorlds.contains(viewer.getWorld().getName())) return true;
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* A feature to disable minecraft 1.9+ feature making tamed animals
|
||||
@@ -19,13 +22,13 @@ import me.neznamy.tab.shared.TAB;
|
||||
*/
|
||||
public class PetFix extends TabFeature {
|
||||
|
||||
//nms storage
|
||||
/** NMS Storage reference for quick access */
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
|
||||
//DataWatcher position of pet owner field
|
||||
/** DataWatcher position of pet owner field */
|
||||
private final int petOwnerPosition = getPetOwnerPosition();
|
||||
|
||||
//logger of last interacts to prevent feature not working on 1.16
|
||||
/** Logger of last interacts to prevent feature not working on 1.16 */
|
||||
private final WeakHashMap<TabPlayer, Long> lastInteractFix = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
@@ -45,7 +48,7 @@ public class PetFix extends TabFeature {
|
||||
*/
|
||||
public PetFix() {
|
||||
super("Pet name fix", null);
|
||||
TAB.getInstance().debug("Loaded PetFix feature");
|
||||
TabAPI.getInstance().debug("Loaded PetFix feature");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,7 +58,7 @@ public class PetFix extends TabFeature {
|
||||
*/
|
||||
private int getPetOwnerPosition() {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
//1.17.x, 1.18.x
|
||||
//1.17.x, 1.18.x, 1.19.x
|
||||
return 18;
|
||||
} else if (nms.getMinorVersion() >= 15) {
|
||||
//1.15.x, 1.16.x
|
||||
@@ -91,6 +94,13 @@ public class PetFix extends TabFeature {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided entity use action is INTERACT or not.
|
||||
*
|
||||
* @param action
|
||||
* Action to check
|
||||
* @return {@code true} if action is INTERACT, {@code false} if not.
|
||||
*/
|
||||
private boolean isInteract(Object action) {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
return nms.PacketPlayInUseEntity$d.isInstance(action);
|
||||
@@ -115,8 +125,16 @@ public class PetFix extends TabFeature {
|
||||
try {
|
||||
for (Object item : items) {
|
||||
if (item == null) continue;
|
||||
if (nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item)) == petOwnerPosition) {
|
||||
Object value = nms.DataWatcherItem_VALUE.get(item);
|
||||
int slot;
|
||||
Object value;
|
||||
if (nms.is1_19_3Plus()) {
|
||||
slot = nms.DataWatcher$DataValue_POSITION.getInt(item);
|
||||
value = nms.DataWatcher$DataValue_VALUE.get(item);
|
||||
} else {
|
||||
slot = nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item));
|
||||
value = nms.DataWatcherItem_VALUE.get(item);
|
||||
}
|
||||
if (slot == petOwnerPosition) {
|
||||
if (value instanceof Optional || value instanceof com.google.common.base.Optional) {
|
||||
removedEntry = item;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.bossbar.BossBar;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
|
||||
@@ -21,11 +21,11 @@ import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
*/
|
||||
public class WitherBossBar extends BossBarManagerImpl implements Listener {
|
||||
|
||||
//distance of wither in blocks
|
||||
/** Distance of the Wither in blocks */
|
||||
private static final int WITHER_DISTANCE = 60;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the class
|
||||
* Constructs a new instance of the class and registers events and task
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
@@ -44,6 +44,9 @@ public class WitherBossBar extends BossBarManagerImpl implements Listener {
|
||||
teleport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates Wither's location for all online players
|
||||
*/
|
||||
private void teleport() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
if (p.getVersion().getMinorVersion() > 8) continue; //sending VV packets to those
|
||||
|
||||
+81
-55
@@ -1,16 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
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 me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.Property;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
@@ -19,52 +9,72 @@ import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Pose;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A class representing an armor stand attached to a player (if the feature is enabled)
|
||||
* A class representing an armor stand attached to a player
|
||||
*/
|
||||
public class BukkitArmorStand implements ArmorStand {
|
||||
|
||||
//entity id counter to pick unique entity IDs
|
||||
/** Entity id counter to pick unique entity ID for each armor stand */
|
||||
private static int idCounter = 2000000000;
|
||||
|
||||
//NameTag feature
|
||||
private final BukkitNameTagX manager = (BukkitNameTagX) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
|
||||
/** NameTag feature */
|
||||
private final BukkitNameTagX manager = (BukkitNameTagX) TabAPI.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
|
||||
|
||||
/** Armor stand manager which this armor stand belongs to */
|
||||
private final BukkitArmorStandManager asm;
|
||||
|
||||
//owner of the armor stand
|
||||
/** Owner of the armor stand */
|
||||
private final TabPlayer owner;
|
||||
|
||||
//instance of Bukkit player
|
||||
/** Owner as a Bukkit player */
|
||||
private final Player player;
|
||||
|
||||
//offset in blocks, 0 for original height
|
||||
/** Offset in blocks, 0 for original height */
|
||||
private double yOffset;
|
||||
|
||||
//entity ID of this armor stand
|
||||
private final int entityId = idCounter++;
|
||||
|
||||
//unique ID of this armor stand
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
|
||||
//sneaking flag of armor stands
|
||||
private boolean sneaking;
|
||||
|
||||
//armor stand visibility
|
||||
private boolean visible;
|
||||
|
||||
//property dedicated to this armor stand
|
||||
private final Property property;
|
||||
|
||||
//if offset is static or dynamic based on other armor stands
|
||||
/** If offset is static, or dynamic based on other armor stands */
|
||||
private final boolean staticOffset;
|
||||
|
||||
//entity destroy packet
|
||||
/** Entity ID of this armor stand */
|
||||
private final int entityId = idCounter++;
|
||||
|
||||
/** Unique ID of this armor stand */
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
|
||||
/** Sneaking flag of armor stands */
|
||||
private boolean sneaking;
|
||||
|
||||
/** Armor stand visibility */
|
||||
private boolean visible;
|
||||
|
||||
/** Refresh property dedicated to this armor stand */
|
||||
private final Property property;
|
||||
|
||||
/** Entity destroy packet */
|
||||
private final PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(entityId);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param asm
|
||||
* Armor stand manager which this armor stand belongs to
|
||||
* @param owner
|
||||
* Owner of the armor stand
|
||||
* @param propertyName
|
||||
* Name of refresh property to use
|
||||
* @param yOffset
|
||||
* Offset in blocks
|
||||
* @param staticOffset
|
||||
* {@code true} if offset is static, {@code false} if not
|
||||
*/
|
||||
public BukkitArmorStand(BukkitArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
|
||||
this.asm = asm;
|
||||
this.owner = owner;
|
||||
@@ -73,6 +83,7 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
this.yOffset = yOffset;
|
||||
this.property = owner.getProperty(propertyName);
|
||||
visible = getVisibility();
|
||||
sneaking = player.isSneaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,7 +204,7 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
/**
|
||||
* Returns general visibility rule for everyone with limited info
|
||||
*
|
||||
* @return true if armor stand should be visible, false if not
|
||||
* @return {@code true} if armor stand should be visible, {@code false} if not
|
||||
*/
|
||||
public boolean getVisibility() {
|
||||
if (manager.isArmorStandsAlwaysVisible()) return true;
|
||||
@@ -202,22 +213,32 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns general location where armor stand should be at time of calling
|
||||
* Returns location where armor stand should be at time of calling.
|
||||
* This takes into account everything that affects height, including
|
||||
* viewer's game version.
|
||||
*
|
||||
* @return Location where armor stand should be for everyone
|
||||
* @param viewer
|
||||
* Player looking at the armor stand
|
||||
* @return Location where armor stand should be for specified viewer
|
||||
*/
|
||||
public Location getLocation() {
|
||||
public Location getLocation(TabPlayer viewer) {
|
||||
double x = player.getLocation().getX();
|
||||
double y = getY() + yOffset + 2;
|
||||
double y = getY() + yOffset;
|
||||
double z = player.getLocation().getZ();
|
||||
if (player.isSleeping()) {
|
||||
y -= 1.76;
|
||||
} else {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
y -= (sneaking ? 0.45 : 0.18);
|
||||
if (!player.isSleeping()) {
|
||||
if (sneaking) {
|
||||
if (viewer.getVersion().getMinorVersion() >= 15) {
|
||||
y += 1.37;
|
||||
} else if (viewer.getVersion().getMinorVersion() >= 9) {
|
||||
y += 1.52;
|
||||
} else {
|
||||
y += 1.7;
|
||||
}
|
||||
} else {
|
||||
y -= (sneaking ? 0.30 : 0.18);
|
||||
y += viewer.getVersion().getMinorVersion() >= 9 ? 1.8 : 1.84; // Normal
|
||||
}
|
||||
} else {
|
||||
y += viewer.getVersion().getMinorVersion() >= 9 ? 0.2 : 0.26; // Sleeping
|
||||
}
|
||||
return new Location(null,x,y,z);
|
||||
}
|
||||
@@ -245,15 +266,19 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
}
|
||||
}
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (TAB.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
if (isSwimming() || (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
return player.getLocation().getY()-1.22;
|
||||
}
|
||||
return player.getLocation().getY();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if owner is swimming, {@code false} if not
|
||||
* @return {@code true} if owner is swimming, {@code false} if not
|
||||
*/
|
||||
private boolean isSwimming() {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
|
||||
return TAB.getInstance().getServerVersion().getMinorVersion() == 13 && player.isSwimming();
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
|
||||
return TabAPI.getInstance().getServerVersion().getMinorVersion() == 13 && player.isSwimming();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,11 +312,12 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if display name is in fact empty, for example only containing color codes
|
||||
* Returns {@code true} if display name is in fact empty,
|
||||
* for example only containing color codes, {@code false} if not.
|
||||
*
|
||||
* @param displayName
|
||||
* string to check
|
||||
* @return true if it's empty, false if not
|
||||
* @return {@code true} if it's empty, {@code false} if not
|
||||
*/
|
||||
private boolean isNameVisiblyEmpty(String displayName) {
|
||||
if (displayName.length() == 0) return true;
|
||||
@@ -311,7 +337,7 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
public TabPacket[] getSpawnPackets(TabPlayer viewer) {
|
||||
visible = getVisibility();
|
||||
DataWatcher dataWatcher = createDataWatcher(property.getFormat(viewer), viewer);
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getArmorStandLocationFor(viewer), null),
|
||||
new PacketPlayOutEntityMetadata(entityId, dataWatcher)
|
||||
@@ -331,7 +357,7 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
* @return location of armor stand
|
||||
*/
|
||||
public Location getArmorStandLocationFor(TabPlayer viewer) {
|
||||
return viewer.getVersion().getMinorVersion() == 8 && !manager.isMarkerFor18x() ? getLocation().clone().add(0,-2,0) : getLocation();
|
||||
return viewer.getVersion().getMinorVersion() == 8 && !manager.isMarkerFor18x() ? getLocation(viewer).add(0,-2,0) : getLocation(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+21
-14
@@ -3,7 +3,7 @@ package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.ArmorStandManager;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -16,27 +16,34 @@ import java.util.Map;
|
||||
*/
|
||||
public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
|
||||
private final NameTagX nameTagX;
|
||||
//map of registered armor stands
|
||||
/** Map of registered armor stands by name */
|
||||
private final Map<String, ArmorStand> armorStands = new LinkedHashMap<>();
|
||||
|
||||
//players in entity tracking range
|
||||
private final List<TabPlayer> nearbyPlayers = new ArrayList<>();
|
||||
|
||||
//array to iterate over to avoid concurrent modification and slightly boost performance & memory
|
||||
/** Armor stands in an array for speed while iterating */
|
||||
private ArmorStand[] armorStandArray = new ArmorStand[0];
|
||||
|
||||
/** Players in entity tracking range of owner */
|
||||
private final List<TabPlayer> nearbyPlayers = new ArrayList<>();
|
||||
|
||||
/** Nearby players in an array for speed while iterating */
|
||||
private TabPlayer[] nearbyPlayerArray = new TabPlayer[0];
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads armor stands.
|
||||
*
|
||||
* @param nameTagX
|
||||
* Main feature
|
||||
* @param owner
|
||||
* Owner of this armor stand manager
|
||||
*/
|
||||
public BukkitArmorStandManager(NameTagX nameTagX, TabPlayer owner) {
|
||||
this.nameTagX = nameTagX;
|
||||
owner.setProperty(nameTagX, TabConstants.Property.NAMETAG, owner.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue()
|
||||
+ owner.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue()
|
||||
+ owner.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
|
||||
double height = 0;
|
||||
for (String line : nameTagX.getDynamicLines()) {
|
||||
addArmorStand(line, new BukkitArmorStand(this, owner, line, height, false));
|
||||
height += nameTagX.getSpaceBetweenLines();
|
||||
height += 0.26;
|
||||
}
|
||||
for (Map.Entry<String, Object> line : nameTagX.getStaticLines().entrySet()) {
|
||||
addArmorStand(line.getKey(), new BukkitArmorStand(this, owner, line.getKey(), Double.parseDouble(line.getValue().toString()), true));
|
||||
@@ -63,7 +70,7 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
|
||||
/**
|
||||
* Returns array of nearby players
|
||||
* @return array of nearby players
|
||||
* @return array of nearby players
|
||||
*/
|
||||
public TabPlayer[] getNearbyPlayers(){
|
||||
return nearbyPlayerArray;
|
||||
@@ -81,11 +88,11 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if manager contains armor stand with specified entity id, false if not
|
||||
* Returns {@code true} if manager contains armor stand with specified entity id, {@code false} if not
|
||||
*
|
||||
* @param entityId
|
||||
* entity id
|
||||
* @return true if armor stand with specified entity id exists, false if not
|
||||
* @return {@code true} if armor stand with specified entity id exists, {@code false} if not
|
||||
*/
|
||||
public boolean hasArmorStandWithID(int entityId) {
|
||||
for (ArmorStand a : armorStandArray) {
|
||||
@@ -134,11 +141,11 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
* Fixes heights of all armor stands due to dynamic lines
|
||||
*/
|
||||
public void fixArmorStandHeights() {
|
||||
double currentY = -nameTagX.getSpaceBetweenLines();
|
||||
double currentY = -0.26;
|
||||
for (ArmorStand as : armorStandArray) {
|
||||
if (as.hasStaticOffset()) continue;
|
||||
if (as.getProperty().get().length() != 0) {
|
||||
currentY += nameTagX.getSpaceBetweenLines();
|
||||
currentY += 0.26;
|
||||
as.setOffset(currentY);
|
||||
}
|
||||
}
|
||||
|
||||
+36
-16
@@ -1,8 +1,8 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
@@ -11,17 +11,19 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* The core class for unlimited NameTag mode
|
||||
* The core class for unlimited NameTag mode on Bukkit
|
||||
*/
|
||||
public class BukkitNameTagX extends NameTagX {
|
||||
|
||||
//bukkit event listener
|
||||
/** Bukkit event listener */
|
||||
private final EventListener eventListener = new EventListener(this);
|
||||
|
||||
/** Vehicle manager reference */
|
||||
private final VehicleRefresher vehicleManager = new VehicleRefresher(this);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
* Constructs new instance with given parameter, loads config options, registers events
|
||||
* and registers sub-features.
|
||||
*
|
||||
* @param plugin
|
||||
* plugin instance
|
||||
@@ -29,26 +31,29 @@ public class BukkitNameTagX extends NameTagX {
|
||||
public BukkitNameTagX(JavaPlugin plugin) {
|
||||
super(BukkitArmorStandManager::new);
|
||||
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, new PacketListener(this));
|
||||
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, new PacketListener(this));
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(all)) continue;
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, all);
|
||||
}
|
||||
}
|
||||
startVisibilityRefreshTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts task checking for player visibility to hide armor stands of invisible players.
|
||||
*/
|
||||
private void startVisibilityRefreshTask() {
|
||||
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(500, this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
|
||||
TabAPI.getInstance().getThreadManager().startRepeatingMeasuredTask(500, this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
|
||||
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer p : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(p)) continue;
|
||||
getArmorStandManager(p).updateVisibility(false);
|
||||
}
|
||||
@@ -65,7 +70,7 @@ public class BukkitNameTagX extends NameTagX {
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
super.onJoin(connectedPlayer);
|
||||
if (isPlayerDisabled(connectedPlayer)) return;
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, connectedPlayer);
|
||||
spawnArmorStands(connectedPlayer, viewer);
|
||||
}
|
||||
@@ -76,7 +81,18 @@ public class BukkitNameTagX extends NameTagX {
|
||||
return vehicleManager != null && vehicleManager.isOnBoat(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns armor stands of target player to viewer if all requirements are met.
|
||||
* These include players being in the same world, distance being less than 48 blocks
|
||||
* and target player being visible to viewer.
|
||||
*
|
||||
* @param viewer
|
||||
* Player viewing armor stands
|
||||
* @param target
|
||||
* Target player with armor stands
|
||||
*/
|
||||
private void spawnArmorStands(TabPlayer viewer, TabPlayer target) {
|
||||
if (viewer.getVersion().getMinorVersion() < 8) return;
|
||||
if (target == viewer || isPlayerDisabled(target)) return;
|
||||
if (((Player) viewer.getPlayer()).getWorld() != ((Player) target.getPlayer()).getWorld()) return;
|
||||
if (getDistance(viewer, target) <= 48 && ((Player)viewer.getPlayer()).canSee((Player)target.getPlayer()) && !target.isVanished())
|
||||
@@ -95,7 +111,7 @@ public class BukkitNameTagX extends NameTagX {
|
||||
@Override
|
||||
public void resumeArmorStands(TabPlayer player) {
|
||||
if (isPlayerDisabled(player)) return;
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, player);
|
||||
}
|
||||
}
|
||||
@@ -107,7 +123,7 @@ public class BukkitNameTagX extends NameTagX {
|
||||
|
||||
@Override
|
||||
public void updateNameTagVisibilityView(TabPlayer player) {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).updateVisibility(true);
|
||||
}
|
||||
}
|
||||
@@ -115,7 +131,7 @@ public class BukkitNameTagX extends NameTagX {
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
super.onQuit(disconnectedPlayer);
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).unregisterPlayer(disconnectedPlayer);
|
||||
}
|
||||
armorStandManagerMap.get(disconnectedPlayer).destroy();
|
||||
@@ -135,7 +151,7 @@ public class BukkitNameTagX extends NameTagX {
|
||||
getArmorStandManager(p).spawn(p);
|
||||
}
|
||||
//for some reason this is needed for some users
|
||||
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getWorld().equals(from)) {
|
||||
getArmorStandManager(p).destroy(viewer);
|
||||
}
|
||||
@@ -162,6 +178,10 @@ public class BukkitNameTagX extends NameTagX {
|
||||
return (BukkitArmorStandManager) armorStandManagerMap.get(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #vehicleManager}
|
||||
* @return {@link #vehicleManager}
|
||||
*/
|
||||
public VehicleRefresher getVehicleManager() {
|
||||
return vehicleManager;
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,25 +1,25 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import 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.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* The event listener part for securing proper functionality of armor stands
|
||||
* Class listening to Bukkit events which the feature requires and are
|
||||
* sufficient, without packets being requires.
|
||||
*/
|
||||
public class EventListener implements Listener {
|
||||
|
||||
//the NameTag feature handler
|
||||
/** Reference to the main feature */
|
||||
private final BukkitNameTagX feature;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param feature
|
||||
* NameTag feature handler
|
||||
@@ -36,9 +36,9 @@ public class EventListener implements Listener {
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onSneak(PlayerToggleSneakEvent e) {
|
||||
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
TabPlayer p = TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (p == null || feature.isPlayerDisabled(p)) return;
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(feature, TabConstants.CpuUsageCategory.PLAYER_SNEAK, () -> feature.getArmorStandManager(p).sneak(e.isSneaking()));
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(feature, TabConstants.CpuUsageCategory.PLAYER_SNEAK, () -> feature.getArmorStandManager(p).sneak(e.isSneaking()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,8 +49,8 @@ public class EventListener implements Listener {
|
||||
*/
|
||||
@EventHandler
|
||||
public void onRespawn(PlayerRespawnEvent e) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(feature, TabConstants.CpuUsageCategory.PLAYER_RESPAWN, () -> {
|
||||
TabPlayer respawned = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(feature, TabConstants.CpuUsageCategory.PLAYER_RESPAWN, () -> {
|
||||
TabPlayer respawned = TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId());
|
||||
if (feature.isPlayerDisabled(respawned)) return;
|
||||
feature.getArmorStandManager(respawned).teleport();
|
||||
});
|
||||
|
||||
+65
-24
@@ -1,35 +1,37 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* The packet listening part for securing proper functionality of armor stands
|
||||
* The packet listening part for securing proper functionality of armor stands.
|
||||
* Bukkit events are too unreliable and delayed/ahead which causes de-sync
|
||||
* if trying to listen to move event.
|
||||
* For entering/leaving tracking range there are no events and
|
||||
* periodic / move-triggered distance checks would cause high CPU usage.
|
||||
*/
|
||||
public class PacketListener extends TabFeature {
|
||||
|
||||
//main feature
|
||||
/** Reference to the main feature */
|
||||
private final BukkitNameTagX nameTagX;
|
||||
|
||||
//player data by entityId, used for better performance
|
||||
/** A player map by entity id, used for better performance */
|
||||
private final Map<Integer, TabPlayer> entityIdMap = new ConcurrentHashMap<>();
|
||||
|
||||
//nms storage
|
||||
/** Reference to NMS storage for quick access */
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param nameTagX
|
||||
* main feature
|
||||
@@ -41,7 +43,7 @@ public class PacketListener extends TabFeature {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
entityIdMap.put(((Player) all.getPlayer()).getEntityId(), all);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +63,7 @@ public class PacketListener extends TabFeature {
|
||||
if (sender.getVersion().getMinorVersion() == 8 && nms.PacketPlayInUseEntity.isInstance(packet)) {
|
||||
int entityId = nms.PacketPlayInUseEntity_ENTITY.getInt(packet);
|
||||
TabPlayer attacked = null;
|
||||
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (all.isLoaded() && nameTagX.getArmorStandManager(all).hasArmorStandWithID(entityId)) {
|
||||
attacked = all;
|
||||
break;
|
||||
@@ -101,7 +103,10 @@ public class PacketListener extends TabFeature {
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes entity move packet
|
||||
* Processes entity move packet. If entity ID belongs to a player,
|
||||
* armor stands of that player are teleported to player who received the packet.
|
||||
* If it belongs to a vehicle carrying a player, that player's armor stands are
|
||||
* teleported as well.
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
@@ -114,45 +119,81 @@ public class PacketListener extends TabFeature {
|
||||
if (pl != null) {
|
||||
//player moved
|
||||
if (nameTagX.isPlayerDisabled(pl) || !pl.isLoaded()) return;
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE,
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE,
|
||||
() -> nameTagX.getArmorStandManager(pl).teleport(receiver));
|
||||
} else if ((vehicleList = nameTagX.getVehicleManager().getVehicles().get(entityId)) != null){
|
||||
//a vehicle carrying something moved
|
||||
for (Entity entity : vehicleList) {
|
||||
TabPlayer passenger = entityIdMap.get(entity.getEntityId());
|
||||
if (passenger != null && nameTagX.getArmorStandManager(passenger) != null) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE_PASSENGER,
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE_PASSENGER,
|
||||
() -> nameTagX.getArmorStandManager(passenger).teleport(receiver));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes named entity spawn packet and spawns armor stands if
|
||||
* entity ID belongs to an online player.
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
* @param entityId
|
||||
* spawned entity
|
||||
*/
|
||||
private void onEntitySpawn(TabPlayer receiver, int entityId) {
|
||||
TabPlayer spawnedPlayer = entityIdMap.get(entityId);
|
||||
if (spawnedPlayer != null && spawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(spawnedPlayer)) {
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_SPAWN,
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_SPAWN,
|
||||
() -> nameTagX.getArmorStandManager(spawnedPlayer).spawn(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes entity destroy packet and destroys armor stands if
|
||||
* entity ID belongs to an online player.
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
* @param entities
|
||||
* de-spawned entities
|
||||
*/
|
||||
private void onEntityDestroy(TabPlayer receiver, List<Integer> entities) {
|
||||
for (int entity : entities) {
|
||||
onEntityDestroy(receiver, entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes entity destroy packet and destroys armor stands if
|
||||
* entity ID belongs to an online player.
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
* @param entities
|
||||
* de-spawned entities
|
||||
*/
|
||||
private void onEntityDestroy(TabPlayer receiver, int... entities) {
|
||||
for (int entity : entities) {
|
||||
onEntityDestroy(receiver, entity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes entity destroy packet and destroys armor stands if
|
||||
* entity ID belongs to an online player.
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
* @param entity
|
||||
* de-spawned entity
|
||||
*/
|
||||
private void onEntityDestroy(TabPlayer receiver, int entity) {
|
||||
TabPlayer deSpawnedPlayer = entityIdMap.get(entity);
|
||||
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(deSpawnedPlayer)) {
|
||||
BukkitArmorStandManager asm = nameTagX.getArmorStandManager(deSpawnedPlayer);
|
||||
TAB.getInstance().getCPUManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY,
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY,
|
||||
() -> asm.destroy(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
+49
-23
@@ -1,57 +1,71 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
/**
|
||||
* Sub-feature for unlimited name tag mode to secure
|
||||
* proper functionality when using vehicles.
|
||||
* A config option allows to disable the feature on boats,
|
||||
* where it would cause a massive de-sync due to boat movement
|
||||
* animation.
|
||||
* Additionally, when entering a vehicle, no move packet is sent
|
||||
* and therefore manual teleporting of armor stands is required.
|
||||
*/
|
||||
public class VehicleRefresher extends TabFeature {
|
||||
|
||||
//map of players currently in a vehicle
|
||||
/** Map of players currently in a vehicle */
|
||||
private final WeakHashMap<TabPlayer, Entity> playersInVehicle = new WeakHashMap<>();
|
||||
|
||||
//map of vehicles carrying players
|
||||
/** Map of vehicles carrying players */
|
||||
private final Map<Integer, List<Entity>> vehicles = new ConcurrentHashMap<>();
|
||||
|
||||
//set of players currently on boats
|
||||
/** set of players currently on boats */
|
||||
private final Set<TabPlayer> playersOnBoats = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
|
||||
|
||||
/** Reference to the main feature */
|
||||
private final BukkitNameTagX feature;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter and starts tasks.
|
||||
*
|
||||
* @param feature
|
||||
* Main feature
|
||||
*/
|
||||
public VehicleRefresher(BukkitNameTagX feature) {
|
||||
super(feature.getFeatureName(), "Refreshing vehicles");
|
||||
this.feature = feature;
|
||||
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(50,
|
||||
TabAPI.getInstance().getThreadManager().startRepeatingMeasuredTask(50,
|
||||
this, TabConstants.CpuUsageCategory.PROCESSING_PLAYER_MOVEMENT, () -> {
|
||||
for (TabPlayer inVehicle : playersInVehicle.keySet()) {
|
||||
if (!inVehicle.isOnline() || feature.getArmorStandManager(inVehicle) == null) continue; // not removed from WeakHashMap yet
|
||||
feature.getArmorStandManager(inVehicle).teleport();
|
||||
}
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer p : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (feature.isPreviewingNametag(p)) {
|
||||
feature.getArmorStandManager(p).teleport(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VEHICLE));
|
||||
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
|
||||
TabAPI.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
for (TabPlayer p : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
Entity vehicle = ((Player)p.getPlayer()).getVehicle();
|
||||
if (vehicle != null) {
|
||||
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
|
||||
playersInVehicle.put(p, vehicle);
|
||||
if (feature.isDisableOnBoats() && vehicle.getType() == EntityType.BOAT) {
|
||||
if (feature.isDisableOnBoats() && vehicle.getType().toString().contains("BOAT")) {
|
||||
playersOnBoats.add(p);
|
||||
}
|
||||
}
|
||||
@@ -91,17 +105,29 @@ public class VehicleRefresher extends TabFeature {
|
||||
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
|
||||
feature.getArmorStandManager(p).respawn(); //making teleport instant instead of showing teleport animation
|
||||
playersInVehicle.put(p, vehicle);
|
||||
if (feature.isDisableOnBoats() && vehicle.getType() == EntityType.BOAT) {
|
||||
if (feature.isDisableOnBoats() && vehicle.getType().toString().contains("BOAT")) {
|
||||
playersOnBoats.add(p);
|
||||
feature.updateTeamData(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the player is in a boat, {@code false} if not
|
||||
*
|
||||
* @param p
|
||||
* Player to check
|
||||
* @return {@code true} if in a boat, {@code false} if not
|
||||
*/
|
||||
public boolean isOnBoat(TabPlayer p) {
|
||||
return playersOnBoats.contains(p);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns vehicle map
|
||||
*
|
||||
* @return vehicle map
|
||||
*/
|
||||
public Map<Integer, List<Entity>> getVehicles() {
|
||||
return vehicles;
|
||||
}
|
||||
@@ -115,13 +141,13 @@ public class VehicleRefresher extends TabFeature {
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public List<Entity> getPassengers(Entity vehicle){
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 11) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 11) {
|
||||
return vehicle.getPassengers();
|
||||
} else {
|
||||
if (vehicle.getPassenger() != null) {
|
||||
return Collections.singletonList(vehicle.getPassenger());
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,702 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
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 com.google.gson.JsonElement;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.platforms.bukkit.Main;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherRegistry;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public final class NMSStorage {
|
||||
|
||||
//instance of this class
|
||||
private static NMSStorage instance;
|
||||
|
||||
//server package, such as "v1_16_R3"
|
||||
private final String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
|
||||
//server minor version such as "16"
|
||||
private final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
|
||||
|
||||
//another field due to fabric which uses 1.16 package on 1.17 server
|
||||
private final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]);
|
||||
|
||||
//base
|
||||
private final Class<?> Packet = getNMSClass("net.minecraft.network.protocol.Packet", "Packet");
|
||||
public final Class<?> EnumChatFormat = getNMSClass("net.minecraft.EnumChatFormat", "EnumChatFormat");
|
||||
public final Class<?> EntityPlayer = getNMSClass("net.minecraft.server.level.EntityPlayer", "EntityPlayer");
|
||||
private final Class<?> Entity = getNMSClass("net.minecraft.world.entity.Entity", "Entity");
|
||||
private final Class<?> EntityLiving = getNMSClass("net.minecraft.world.entity.EntityLiving", "EntityLiving");
|
||||
private final Class<?> PlayerConnection = getNMSClass("net.minecraft.server.network.PlayerConnection", "PlayerConnection");
|
||||
public final Field PING = getField(EntityPlayer, "ping", "latency", "field_71138_i", "field_13967", "e");
|
||||
public final Field PLAYER_CONNECTION = getFields(EntityPlayer, PlayerConnection).get(0);
|
||||
public Field NETWORK_MANAGER;
|
||||
public Field CHANNEL;
|
||||
public final Method getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
|
||||
public final Method sendPacket = getMethods(PlayerConnection, void.class, Packet).get(0);
|
||||
public Method getProfile;
|
||||
public final Enum[] EnumChatFormat_values = getEnumValues(EnumChatFormat);
|
||||
|
||||
//chat
|
||||
public Class<?> EnumClickAction;
|
||||
private Class<?> IChatBaseComponent;
|
||||
public Constructor<?> newChatComponentText;
|
||||
public Constructor<?> newChatClickable;
|
||||
public Constructor<?> newChatModifier;
|
||||
public Constructor<?> newChatHoverable;
|
||||
public Field ChatBaseComponent_modifier;
|
||||
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 ChatModifier_color;
|
||||
public Method ChatComponentText_addSibling;
|
||||
public Method EnumHoverAction_a;
|
||||
public Method ChatHexColor_ofInt;
|
||||
public Method ChatHexColor_ofString;
|
||||
public Method EnumHoverAction_fromJson;
|
||||
public Method EnumHoverAction_fromLegacyComponent;
|
||||
|
||||
//PacketPlayOutChat
|
||||
public Class<?> ChatMessageType;
|
||||
public Constructor<?> newPacketPlayOutChat;
|
||||
public Enum[] ChatMessageType_values;
|
||||
|
||||
//DataWatcher
|
||||
private final Class<?> DataWatcher = getNMSClass("net.minecraft.network.syncher.DataWatcher", "DataWatcher");
|
||||
private final Class<?> DataWatcherItem = getNMSClass("net.minecraft.network.syncher.DataWatcher$Item", "DataWatcher$Item", "DataWatcher$WatchableObject", "WatchableObject");
|
||||
public Class<?> DataWatcherRegistry;
|
||||
public final Constructor<?> newDataWatcher = DataWatcher.getConstructors()[0];
|
||||
public Constructor<?> newDataWatcherObject;
|
||||
public Field DataWatcherItem_TYPE;
|
||||
public final Field DataWatcherItem_VALUE = getFields(DataWatcherItem, Object.class).get(0);
|
||||
public Field DataWatcherObject_SLOT;
|
||||
public Field DataWatcherObject_SERIALIZER;
|
||||
public Method DataWatcher_REGISTER;
|
||||
private DataWatcherRegistry registry;
|
||||
|
||||
//PacketPlayOutSpawnEntityLiving
|
||||
public final Class<?> PacketPlayOutSpawnEntityLiving = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving", "PacketPlayOutSpawnEntityLiving", "Packet24MobSpawn");
|
||||
public Constructor<?> newPacketPlayOutSpawnEntityLiving;
|
||||
public final Field PacketPlayOutSpawnEntityLiving_ENTITYID = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(0);
|
||||
public final Field PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(1);
|
||||
public final Field PacketPlayOutSpawnEntityLiving_YAW = getFields(PacketPlayOutSpawnEntityLiving, byte.class).get(0);
|
||||
public final Field PacketPlayOutSpawnEntityLiving_PITCH = getFields(PacketPlayOutSpawnEntityLiving, byte.class).get(0);
|
||||
public Field PacketPlayOutSpawnEntityLiving_UUID;
|
||||
public Field PacketPlayOutSpawnEntityLiving_X;
|
||||
public Field PacketPlayOutSpawnEntityLiving_Y;
|
||||
public Field PacketPlayOutSpawnEntityLiving_Z;
|
||||
public Field PacketPlayOutSpawnEntityLiving_DATAWATCHER;
|
||||
|
||||
//PacketPlayOutEntityTeleport
|
||||
public final Class<?> PacketPlayOutEntityTeleport = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport", "PacketPlayOutEntityTeleport", "Packet34EntityTeleport");
|
||||
public Constructor<?> newPacketPlayOutEntityTeleport;
|
||||
public final Field PacketPlayOutEntityTeleport_ENTITYID = getFields(PacketPlayOutEntityTeleport, int.class).get(0);
|
||||
public Field PacketPlayOutEntityTeleport_X;
|
||||
public Field PacketPlayOutEntityTeleport_Y;
|
||||
public Field PacketPlayOutEntityTeleport_Z;
|
||||
public final Field PacketPlayOutEntityTeleport_YAW = getFields(PacketPlayOutEntityTeleport, byte.class).get(0);
|
||||
public final Field PacketPlayOutEntityTeleport_PITCH = getFields(PacketPlayOutEntityTeleport, byte.class).get(1);
|
||||
|
||||
//PacketPlayOutPlayerListHeaderFooter
|
||||
public Constructor<?> newPacketPlayOutPlayerListHeaderFooter;
|
||||
public Field PacketPlayOutPlayerListHeaderFooter_HEADER;
|
||||
public Field PacketPlayOutPlayerListHeaderFooter_FOOTER;
|
||||
|
||||
//other entity packets
|
||||
public final Class<?> PacketPlayInUseEntity = getNMSClass("net.minecraft.network.protocol.game.PacketPlayInUseEntity", "PacketPlayInUseEntity", "Packet7UseEntity");
|
||||
public Class<?> PacketPlayInUseEntity$d;
|
||||
public Field PacketPlayInUseEntity_ENTITY;
|
||||
public Field PacketPlayInUseEntity_ACTION;
|
||||
|
||||
public final Class<?> PacketPlayOutEntity = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntity", "PacketPlayOutEntity", "Packet30Entity");
|
||||
public final Field PacketPlayOutEntity_ENTITYID = getFields(PacketPlayOutEntity, int.class).get(0);
|
||||
|
||||
public final Class<?> PacketPlayOutEntityDestroy = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy", "PacketPlayOutEntityDestroy", "Packet29DestroyEntity");
|
||||
public Constructor<?> newPacketPlayOutEntityDestroy;
|
||||
public final Field PacketPlayOutEntityDestroy_ENTITIES = setAccessible(PacketPlayOutEntityDestroy.getDeclaredFields()[0]);
|
||||
|
||||
public final Class<?> PacketPlayOutEntityLook = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntityLook", "Packet32EntityLook");
|
||||
|
||||
public final Class<?> PacketPlayOutEntityMetadata = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata", "PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
|
||||
public final Constructor<?> newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class);
|
||||
public final Field PacketPlayOutEntityMetadata_LIST = getFields(PacketPlayOutEntityMetadata, List.class).get(0);
|
||||
|
||||
public final Class<?> PacketPlayOutNamedEntitySpawn = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn", "PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
|
||||
public final Field PacketPlayOutNamedEntitySpawn_ENTITYID = getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
|
||||
|
||||
//PacketPlayOutPlayerInfo
|
||||
public Class<?> PacketPlayOutPlayerInfo;
|
||||
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 Enum[] EnumPlayerInfoAction_values;
|
||||
public Enum[] EnumGamemode_values;
|
||||
|
||||
//scoreboard objectives
|
||||
public final Class<?> PacketPlayOutScoreboardDisplayObjective = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective", "PacketPlayOutScoreboardDisplayObjective", "Packet208SetScoreboardDisplayObjective");
|
||||
public final Class<?> PacketPlayOutScoreboardObjective = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective", "PacketPlayOutScoreboardObjective", "Packet206SetScoreboardObjective");
|
||||
private final Class<?> Scoreboard = getNMSClass("net.minecraft.world.scores.Scoreboard", "Scoreboard");
|
||||
private final Class<?> PacketPlayOutScoreboardScore = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore", "PacketPlayOutScoreboardScore", "Packet207SetScoreboardScore");
|
||||
private final Class<?> ScoreboardObjective = getNMSClass("net.minecraft.world.scores.ScoreboardObjective", "ScoreboardObjective");
|
||||
private final Class<?> ScoreboardScore = getNMSClass("net.minecraft.world.scores.ScoreboardScore", "ScoreboardScore");
|
||||
private final Class<?> IScoreboardCriteria = getNMSClass("net.minecraft.world.scores.criteria.IScoreboardCriteria", "IScoreboardCriteria");
|
||||
public Class<?> EnumScoreboardHealthDisplay;
|
||||
public final Constructor<?> newScoreboardObjective = ScoreboardObjective.getConstructors()[0];
|
||||
private final Constructor<?> newScoreboard = Scoreboard.getConstructor();
|
||||
public final Constructor<?> newScoreboardScore = ScoreboardScore.getConstructor(Scoreboard, ScoreboardObjective, String.class);
|
||||
public final Constructor<?> newPacketPlayOutScoreboardDisplayObjective = PacketPlayOutScoreboardDisplayObjective.getConstructor(int.class, ScoreboardObjective);
|
||||
public Constructor<?> newPacketPlayOutScoreboardObjective;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore_1_13;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore_String;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore;
|
||||
public final Field PacketPlayOutScoreboardDisplayObjective_POSITION = getFields(PacketPlayOutScoreboardDisplayObjective, int.class).get(0);
|
||||
public final Field PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME = getFields(PacketPlayOutScoreboardDisplayObjective, String.class).get(0);
|
||||
public final Field PacketPlayOutScoreboardObjective_OBJECTIVENAME = getFields(PacketPlayOutScoreboardObjective, String.class).get(0);
|
||||
public Field PacketPlayOutScoreboardObjective_METHOD;
|
||||
public final Field IScoreboardCriteria_self = getFields(IScoreboardCriteria, IScoreboardCriteria).get(0);
|
||||
public Field PacketPlayOutScoreboardObjective_RENDERTYPE;
|
||||
public Field PacketPlayOutScoreboardObjective_DISPLAYNAME;
|
||||
public final Method ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[]{"setScore", "func_96647_c", "method_1128", "b"}, int.class);
|
||||
public Enum[] EnumScoreboardHealthDisplay_values;
|
||||
public Enum[] EnumScoreboardAction_values;
|
||||
|
||||
//PacketPlayOutScoreboardTeam
|
||||
public Class<?> PacketPlayOutScoreboardTeam;
|
||||
public Constructor<?> newScoreboardTeam;
|
||||
public Constructor<?> newPacketPlayOutScoreboardTeam;
|
||||
public Field PacketPlayOutScoreboardTeam_NAME;
|
||||
public Field PacketPlayOutScoreboardTeam_ACTION;
|
||||
public Field PacketPlayOutScoreboardTeam_PLAYERS;
|
||||
public Method ScoreboardTeam_getPlayerNameSet;
|
||||
public Method ScoreboardTeam_setNameTagVisibility;
|
||||
public Method ScoreboardTeam_setCollisionRule;
|
||||
public Method ScoreboardTeam_setPrefix;
|
||||
public Method ScoreboardTeam_setSuffix;
|
||||
public Method ScoreboardTeam_setColor;
|
||||
public Method ScoreboardTeam_setAllowFriendlyFire;
|
||||
public Method ScoreboardTeam_setCanSeeFriendlyInvisibles;
|
||||
public Method PacketPlayOutScoreboardTeam_of;
|
||||
public Method PacketPlayOutScoreboardTeam_ofBoolean;
|
||||
public Method PacketPlayOutScoreboardTeam_ofString;
|
||||
public Enum[] EnumNameTagVisibility_values;
|
||||
public Enum[] EnumTeamPush_values;
|
||||
public Enum[] PacketPlayOutScoreboardTeam_PlayerAction_values;
|
||||
|
||||
//other
|
||||
public final Object emptyScoreboard = newScoreboard.newInstance();
|
||||
public Object dummyEntity;
|
||||
|
||||
/**
|
||||
* Creates new instance, initializes required NMS classes and fields
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If any class, field or method fails to load
|
||||
*/
|
||||
public NMSStorage() throws ReflectiveOperationException {
|
||||
ProtocolVersion.UNKNOWN_SERVER_VERSION.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
|
||||
Class<?> NetworkManager = getNMSClass("net.minecraft.network.NetworkManager", "NetworkManager");
|
||||
if (minorVersion >= 7) {
|
||||
NETWORK_MANAGER = getFields(PlayerConnection, NetworkManager).get(0);
|
||||
}
|
||||
if (minorVersion >= 8) {
|
||||
CHANNEL = getFields(NetworkManager, Channel.class).get(0);
|
||||
getProfile = getMethods(getNMSClass("net.minecraft.world.entity.player.EntityHuman", "EntityHuman"), GameProfile.class).get(0);
|
||||
Constructor<?> newEntityArmorStand = getNMSClass("net.minecraft.world.entity.decoration.EntityArmorStand", "EntityArmorStand")
|
||||
.getConstructor(getNMSClass("net.minecraft.world.level.World", "World"), double.class, double.class, double.class);
|
||||
Method World_getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".CraftWorld").getMethod("getHandle");
|
||||
dummyEntity = newEntityArmorStand.newInstance(World_getHandle.invoke(Bukkit.getWorlds().get(0)), 0, 0, 0);
|
||||
}
|
||||
initializeChatComponents();
|
||||
initializeChatPacket();
|
||||
initializeDataWatcher();
|
||||
initializeEntityPackets();
|
||||
initializeHeaderFooterPacket();
|
||||
initializePlayerInfoPacket();
|
||||
initializeScoreboardPackets();
|
||||
try {
|
||||
initializeTeamPackets();
|
||||
} catch (ClassNotFoundException e) {
|
||||
Bukkit.getConsoleSender().sendMessage(me.neznamy.tab.api.chat.EnumChatFormat.color("&c[TAB] Failed to load PacketPlayOutScoreboardTeam"));
|
||||
//fabric with missing team packet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
private void initializeChatComponents() throws ReflectiveOperationException {
|
||||
if (minorVersion < 7) return;
|
||||
Class<?> ChatBaseComponent = getNMSClass("net.minecraft.network.chat.ChatBaseComponent", "ChatBaseComponent");
|
||||
Class<?> ChatClickable = getNMSClass("net.minecraft.network.chat.ChatClickable", "ChatClickable");
|
||||
Class<?> ChatComponentText = getNMSClass("net.minecraft.network.chat.ChatComponentText", "ChatComponentText");
|
||||
Class<?> ChatHoverable = getNMSClass("net.minecraft.network.chat.ChatHoverable", "ChatHoverable");
|
||||
Class<?> ChatModifier = getNMSClass("net.minecraft.network.chat.ChatModifier", "ChatModifier");
|
||||
EnumClickAction = getNMSClass("net.minecraft.network.chat.ChatClickable$EnumClickAction", "ChatClickable$EnumClickAction", "EnumClickAction");
|
||||
Class<?> EnumHoverAction = getNMSClass("net.minecraft.network.chat.ChatHoverable$EnumHoverAction", "ChatHoverable$EnumHoverAction", "EnumHoverAction");
|
||||
IChatBaseComponent = getNMSClass("net.minecraft.network.chat.IChatBaseComponent", "IChatBaseComponent");
|
||||
newChatComponentText = ChatComponentText.getConstructor(String.class);
|
||||
newChatClickable = ChatClickable.getConstructor(EnumClickAction, String.class);
|
||||
ChatBaseComponent_modifier = getFields(ChatBaseComponent, ChatModifier).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);
|
||||
ChatComponentText_addSibling = getMethod(ChatComponentText, new String[]{"addSibling", "a", "func_150257_a", "method_10852"}, IChatBaseComponent);
|
||||
EnumHoverAction_a = getMethod(EnumHoverAction, new String[]{"a", "func_150684_a", "method_27670"}, String.class);
|
||||
if (minorVersion >= 16) {
|
||||
Class<?> ChatHexColor = getNMSClass("net.minecraft.network.chat.ChatHexColor", "ChatHexColor");
|
||||
Class<?> MinecraftKey = getNMSClass("net.minecraft.resources.MinecraftKey", "MinecraftKey");
|
||||
newChatModifier = setAccessible(ChatModifier.getDeclaredConstructor(ChatHexColor, Boolean.class, Boolean.class, Boolean.class, Boolean.class, Boolean.class, ChatClickable, ChatHoverable, String.class, MinecraftKey));
|
||||
newChatHoverable = ChatHoverable.getConstructor(EnumHoverAction, Object.class);
|
||||
ChatModifier_color = getFields(ChatModifier, ChatHexColor).get(0);
|
||||
ChatHexColor_ofInt = getMethods(ChatHexColor, ChatHexColor, int.class).get(0);
|
||||
ChatHexColor_ofString = getMethods(ChatHexColor, ChatHexColor, String.class).get(0);
|
||||
EnumHoverAction_fromJson = getMethods(EnumHoverAction, ChatHoverable, JsonElement.class).get(0);
|
||||
EnumHoverAction_fromLegacyComponent = getMethods(EnumHoverAction, ChatHoverable, IChatBaseComponent).get(0);
|
||||
} else {
|
||||
newChatModifier = ChatModifier.getConstructor();
|
||||
newChatHoverable = ChatHoverable.getConstructor(EnumHoverAction, IChatBaseComponent);
|
||||
ChatModifier_color = getFields(ChatModifier, EnumChatFormat).get(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeChatPacket() throws ReflectiveOperationException {
|
||||
Class<?> PacketPlayOutChat = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutChat", "PacketPlayOutChat", "Packet3Chat");
|
||||
if (minorVersion >= 12) {
|
||||
ChatMessageType = getNMSClass("net.minecraft.network.chat.ChatMessageType", "ChatMessageType");
|
||||
ChatMessageType_values = getEnumValues(ChatMessageType);
|
||||
}
|
||||
if (minorVersion >= 16) {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType, UUID.class);
|
||||
} else if (minorVersion >= 12) {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType);
|
||||
} else if (minorVersion >= 8) {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, byte.class);
|
||||
} else if (minorVersion >= 7){
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeDataWatcher() throws ReflectiveOperationException {
|
||||
if (minorVersion >= 9) {
|
||||
Class<?> DataWatcherObject = getNMSClass("net.minecraft.network.syncher.DataWatcherObject", "DataWatcherObject");
|
||||
DataWatcherRegistry = getNMSClass("net.minecraft.network.syncher.DataWatcherRegistry", "DataWatcherRegistry");
|
||||
Class<?> DataWatcherSerializer = getNMSClass("net.minecraft.network.syncher.DataWatcherSerializer", "DataWatcherSerializer");
|
||||
newDataWatcherObject = DataWatcherObject.getConstructor(int.class, DataWatcherSerializer);
|
||||
DataWatcherItem_TYPE = getFields(DataWatcherItem, DataWatcherObject).get(0);
|
||||
DataWatcherObject_SLOT = getFields(DataWatcherObject, int.class).get(0);
|
||||
DataWatcherObject_SERIALIZER = getFields(DataWatcherObject, DataWatcherSerializer).get(0);
|
||||
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"register", "method_12784", "a"}, DataWatcherObject, Object.class);
|
||||
} else {
|
||||
DataWatcherItem_TYPE = getFields(DataWatcherItem, int.class).get(1);
|
||||
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"a", "func_75682_a"}, int.class, Object.class);
|
||||
}
|
||||
registry = new DataWatcherRegistry(this);
|
||||
}
|
||||
|
||||
private void initializeEntityPackets() throws ReflectiveOperationException {
|
||||
if (minorVersion >= 17 || serverVersion.getMinorVersion() >= 17) {
|
||||
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
|
||||
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor(Entity);
|
||||
} else {
|
||||
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor();
|
||||
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor();
|
||||
}
|
||||
if (minorVersion >= 17) {
|
||||
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$d");
|
||||
}
|
||||
try {
|
||||
newPacketPlayOutEntityDestroy = PacketPlayOutEntityDestroy.getConstructor(int[].class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
//1.17.0
|
||||
newPacketPlayOutEntityDestroy = PacketPlayOutEntityDestroy.getConstructor(int.class);
|
||||
}
|
||||
if (minorVersion >= 7) {
|
||||
Class<?> EnumEntityUseAction = getNMSClass("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction", "PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction", "net.minecraft.class_2824$class_5906");
|
||||
PacketPlayInUseEntity_ENTITY = getFields(PacketPlayInUseEntity, int.class).get(0);
|
||||
PacketPlayInUseEntity_ACTION = getFields(PacketPlayInUseEntity, EnumEntityUseAction).get(0);
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
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);
|
||||
PacketPlayOutEntityTeleport_X = getFields(PacketPlayOutEntityTeleport, double.class).get(0);
|
||||
PacketPlayOutEntityTeleport_Y = getFields(PacketPlayOutEntityTeleport, double.class).get(1);
|
||||
PacketPlayOutEntityTeleport_Z = getFields(PacketPlayOutEntityTeleport, double.class).get(2);
|
||||
} else {
|
||||
PacketPlayOutSpawnEntityLiving_X = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(2);
|
||||
PacketPlayOutSpawnEntityLiving_Y = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(3);
|
||||
PacketPlayOutSpawnEntityLiving_Z = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(4);
|
||||
PacketPlayOutEntityTeleport_X = getFields(PacketPlayOutEntityTeleport, int.class).get(1);
|
||||
PacketPlayOutEntityTeleport_Y = getFields(PacketPlayOutEntityTeleport, int.class).get(2);
|
||||
PacketPlayOutEntityTeleport_Z = getFields(PacketPlayOutEntityTeleport, int.class).get(3);
|
||||
}
|
||||
if (minorVersion <= 14) {
|
||||
PacketPlayOutSpawnEntityLiving_DATAWATCHER = getFields(PacketPlayOutSpawnEntityLiving, DataWatcher).get(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeHeaderFooterPacket() throws ReflectiveOperationException {
|
||||
if (minorVersion < 8) return;
|
||||
Class<?> PacketPlayOutPlayerListHeaderFooter = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter", "PacketPlayOutPlayerListHeaderFooter");
|
||||
PacketPlayOutPlayerListHeaderFooter_HEADER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(0);
|
||||
PacketPlayOutPlayerListHeaderFooter_FOOTER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(1);
|
||||
if (minorVersion >= 17 || serverVersion.getMinorVersion() >= 17) {
|
||||
newPacketPlayOutPlayerListHeaderFooter = PacketPlayOutPlayerListHeaderFooter.getConstructor(IChatBaseComponent, IChatBaseComponent);
|
||||
} else {
|
||||
newPacketPlayOutPlayerListHeaderFooter = PacketPlayOutPlayerListHeaderFooter.getConstructor();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializePlayerInfoPacket() throws ReflectiveOperationException {
|
||||
if (minorVersion < 8) return;
|
||||
PacketPlayOutPlayerInfo = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo", "PacketPlayOutPlayerInfo");
|
||||
Class<?> EnumPlayerInfoAction = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction", "net.minecraft.class_2703$class_5893");
|
||||
Class<?> PlayerInfoData = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData", "PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
|
||||
Class<?> EnumGamemode = getNMSClass("net.minecraft.world.level.EnumGamemode", "EnumGamemode", "WorldSettings$EnumGamemode");
|
||||
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
|
||||
newPlayerInfoData = PlayerInfoData.getConstructors()[0];
|
||||
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
|
||||
PacketPlayOutPlayerInfo_PLAYERS = getFields(PacketPlayOutPlayerInfo, List.class).get(0);
|
||||
PlayerInfoData_getProfile = getMethods(PlayerInfoData, GameProfile.class).get(0);
|
||||
PlayerInfoData_getLatency = getMethods(PlayerInfoData, int.class).get(0);
|
||||
PlayerInfoData_getGamemode = getMethods(PlayerInfoData, EnumGamemode).get(0);
|
||||
PlayerInfoData_getDisplayName = getMethods(PlayerInfoData, IChatBaseComponent).get(0);
|
||||
EnumPlayerInfoAction_values = getEnumValues(EnumPlayerInfoAction);
|
||||
EnumGamemode_values = getEnumValues(EnumGamemode);
|
||||
}
|
||||
|
||||
private void initializeScoreboardPackets() throws ReflectiveOperationException {
|
||||
List<Field> list = getFields(PacketPlayOutScoreboardObjective, int.class);
|
||||
PacketPlayOutScoreboardObjective_METHOD = list.get(list.size()-1);
|
||||
Class<?> EnumScoreboardAction = null;
|
||||
if (minorVersion >= 8) {
|
||||
EnumScoreboardHealthDisplay = getNMSClass("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay", "IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
|
||||
EnumScoreboardHealthDisplay_values = getEnumValues(EnumScoreboardHealthDisplay);
|
||||
EnumScoreboardAction = getNMSClass("net.minecraft.server.ScoreboardServer$Action", "ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
|
||||
EnumScoreboardAction_values = getEnumValues(EnumScoreboardAction);
|
||||
PacketPlayOutScoreboardObjective_RENDERTYPE = getFields(PacketPlayOutScoreboardObjective, EnumScoreboardHealthDisplay).get(0);
|
||||
}
|
||||
if (minorVersion >= 13) {
|
||||
newPacketPlayOutScoreboardObjective = PacketPlayOutScoreboardObjective.getConstructor(ScoreboardObjective, int.class);
|
||||
newPacketPlayOutScoreboardScore_1_13 = PacketPlayOutScoreboardScore.getConstructor(EnumScoreboardAction, String.class, String.class, int.class);
|
||||
PacketPlayOutScoreboardObjective_DISPLAYNAME = getFields(PacketPlayOutScoreboardObjective, IChatBaseComponent).get(0);
|
||||
} else {
|
||||
newPacketPlayOutScoreboardObjective = PacketPlayOutScoreboardObjective.getConstructor();
|
||||
newPacketPlayOutScoreboardScore_String = PacketPlayOutScoreboardScore.getConstructor(String.class);
|
||||
PacketPlayOutScoreboardObjective_DISPLAYNAME = getFields(PacketPlayOutScoreboardObjective, String.class).get(1);
|
||||
if (minorVersion >= 8) {
|
||||
newPacketPlayOutScoreboardScore = PacketPlayOutScoreboardScore.getConstructor(ScoreboardScore);
|
||||
} else {
|
||||
newPacketPlayOutScoreboardScore = PacketPlayOutScoreboardScore.getConstructor(ScoreboardScore, int.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeTeamPackets() throws ReflectiveOperationException {
|
||||
PacketPlayOutScoreboardTeam = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam", "PacketPlayOutScoreboardTeam", "Packet209SetScoreboardTeam");
|
||||
Class<?> ScoreboardTeam = getNMSClass("net.minecraft.world.scores.ScoreboardTeam", "ScoreboardTeam");
|
||||
newScoreboardTeam = ScoreboardTeam.getConstructor(Scoreboard, String.class);
|
||||
PacketPlayOutScoreboardTeam_NAME = getFields(PacketPlayOutScoreboardTeam, String.class).get(0);
|
||||
PacketPlayOutScoreboardTeam_ACTION = getInstanceFields(PacketPlayOutScoreboardTeam, int.class).get(0);
|
||||
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
|
||||
ScoreboardTeam_getPlayerNameSet = getMethods(ScoreboardTeam, Collection.class).get(0);
|
||||
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a", "func_96660_a"}, boolean.class);
|
||||
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[]{"setCanSeeFriendlyInvisibles", "b", "func_98300_b"}, boolean.class);
|
||||
if (minorVersion >= 8) {
|
||||
Class<?> EnumNameTagVisibility = getNMSClass("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility", "ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
|
||||
EnumNameTagVisibility_values = getEnumValues(EnumNameTagVisibility);
|
||||
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[]{"setNameTagVisibility", "a", "method_1149"}, EnumNameTagVisibility);
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
Class<?> EnumTeamPush = getNMSClass("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush", "ScoreboardTeamBase$EnumTeamPush");
|
||||
EnumTeamPush_values = getEnumValues(EnumTeamPush);
|
||||
ScoreboardTeam_setCollisionRule = getMethods(ScoreboardTeam, void.class, EnumTeamPush).get(0);
|
||||
}
|
||||
if (minorVersion >= 13) {
|
||||
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[]{"setPrefix", "method_1138", "b"}, IChatBaseComponent);
|
||||
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[]{"setSuffix", "method_1139", "c"}, IChatBaseComponent);
|
||||
ScoreboardTeam_setColor = getMethods(ScoreboardTeam, void.class, EnumChatFormat).get(0);
|
||||
} 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 >= 17 || serverVersion.getMinorVersion() >= 17) {
|
||||
Class<?> PacketPlayOutScoreboardTeam_PlayerAction = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$a");
|
||||
PacketPlayOutScoreboardTeam_of = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam).get(0);
|
||||
PacketPlayOutScoreboardTeam_ofBoolean = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam, boolean.class).get(0);
|
||||
PacketPlayOutScoreboardTeam_ofString = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam, String.class, PacketPlayOutScoreboardTeam_PlayerAction).get(0);
|
||||
PacketPlayOutScoreboardTeam_PlayerAction_values = getEnumValues(PacketPlayOutScoreboardTeam_PlayerAction);
|
||||
} else {
|
||||
newPacketPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.getConstructor(ScoreboardTeam, int.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class with given potential names in same order
|
||||
*
|
||||
* @param names
|
||||
* possible class names
|
||||
* @return class for specified names
|
||||
* @throws ClassNotFoundException
|
||||
* if class does not exist
|
||||
*/
|
||||
private Class<?> getNMSClass(String fullPath_1_17, String... names) throws ClassNotFoundException {
|
||||
if (minorVersion >= 17) {
|
||||
return Class.forName(fullPath_1_17);
|
||||
}
|
||||
for (String name : names) {
|
||||
try {
|
||||
return getLegacyClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
//not the first class name in array
|
||||
}
|
||||
}
|
||||
throw new ClassNotFoundException("No class found with possible names {modern = " + fullPath_1_17 + ", legacy = " + 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<?> getLegacyClass(String name) throws ClassNotFoundException {
|
||||
try {
|
||||
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
|
||||
} catch (ClassNotFoundException | NullPointerException e) {
|
||||
try {
|
||||
//modded server?
|
||||
Class<?> clazz = Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
|
||||
if (clazz != null) return clazz;
|
||||
throw new ClassNotFoundException(name);
|
||||
} catch (ClassNotFoundException | NullPointerException e1) {
|
||||
//maybe fabric?
|
||||
return Class.forName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns method with specified possible names and parameters. Throws exception if no such method was found
|
||||
*
|
||||
* @param clazz
|
||||
* lass 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 getMethod(clazz, name, parameterTypes);
|
||||
} catch (NoSuchMethodException e) {
|
||||
//not the first method in array
|
||||
}
|
||||
}
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Method m : clazz.getMethods()) {
|
||||
if (m.getParameterCount() != parameterTypes.length) continue;
|
||||
Class<?>[] types = m.getParameterTypes();
|
||||
boolean valid = true;
|
||||
for (int i=0; i<types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) list.add(m.getName());
|
||||
}
|
||||
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(names) + " with parameters " +
|
||||
Arrays.toString(parameterTypes) + " in class " + clazz.getName() + ". Methods with matching parameters: " + list);
|
||||
}
|
||||
|
||||
private Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException {
|
||||
List<Method> list = new ArrayList<>();
|
||||
for (Method m : clazz.getMethods()) {
|
||||
if (!m.getName().equals(name) || m.getParameterCount() != parameterTypes.length) continue;
|
||||
Class<?>[] types = m.getParameterTypes();
|
||||
boolean valid = true;
|
||||
for (int i=0; i<types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) list.add(m);
|
||||
}
|
||||
if (!list.isEmpty()) return list.get(0);
|
||||
throw new NoSuchMethodException("No method found with name " + name + " in class " + clazz.getName() + " with parameters " + Arrays.toString(parameterTypes));
|
||||
}
|
||||
|
||||
private List<Method> getMethods(Class<?> clazz, Class<?> returnType, Class<?>... parameterTypes){
|
||||
List<Method> list = new ArrayList<>();
|
||||
for (Method m : clazz.getDeclaredMethods()) {
|
||||
if (m.getReturnType() != returnType || m.getParameterCount() != parameterTypes.length || !Modifier.isPublic(m.getModifiers())) continue;
|
||||
Class<?>[] types = m.getParameterTypes();
|
||||
boolean valid = true;
|
||||
for (int i=0; i<types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) list.add(m);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private List<Field> getFields(Class<?> clazz, Class<?> type){
|
||||
if (clazz == null) throw new IllegalArgumentException("Source class cannot be null");
|
||||
List<Field> list = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type) {
|
||||
list.add(setAccessible(field));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all instance 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> getInstanceFields(Class<?> clazz, Class<?> type){
|
||||
if (clazz == null) throw new IllegalArgumentException("Source class cannot be null");
|
||||
List<Field> list = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) {
|
||||
list.add(setAccessible(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))) {
|
||||
return setAccessible(f);
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException("Field \"" + name + "\" was not found in class " + clazz.getName());
|
||||
}
|
||||
|
||||
private Field getField(Class<?> clazz, String... potentialNames) throws NoSuchFieldException {
|
||||
for (String name : potentialNames) {
|
||||
try {
|
||||
return getField(clazz, name);
|
||||
} catch (NoSuchFieldException e) {
|
||||
//not the first field name from array
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException("No field found in class " + clazz.getName() + " with potential names " + Arrays.toString(potentialNames));
|
||||
}
|
||||
|
||||
private Enum[] getEnumValues(Class<?> enumClass) {
|
||||
if (enumClass == null) throw new IllegalArgumentException("Class cannot be null");
|
||||
if (!enumClass.isEnum()) throw new IllegalArgumentException(enumClass.getName() + " is not an enum class");
|
||||
try {
|
||||
return (Enum[]) enumClass.getMethod("values").invoke(null);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
//this should never happen
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to load enum constants of " + enumClass.getName(), e);
|
||||
return new Enum[0];
|
||||
}
|
||||
}
|
||||
|
||||
public int getMinorVersion() {
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
public void setField(Object obj, Field field, Object value) throws IllegalAccessException {
|
||||
field.set(obj, value);
|
||||
}
|
||||
|
||||
public DataWatcherRegistry getDataWatcherRegistry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
public <T extends AccessibleObject> T setAccessible(T o) {
|
||||
o.setAccessible(true);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
+14
@@ -4,10 +4,20 @@ import java.util.Arrays;
|
||||
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityDestroy minecraft packet.
|
||||
*/
|
||||
public class PacketPlayOutEntityDestroy implements TabPacket {
|
||||
|
||||
/** Destroyed entities */
|
||||
private final int[] entities;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param entities
|
||||
* Destroyed entities
|
||||
*/
|
||||
public PacketPlayOutEntityDestroy(int... entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
@@ -17,6 +27,10 @@ public class PacketPlayOutEntityDestroy implements TabPacket {
|
||||
return String.format("PacketPlayOutEntityDestroy{entities=%s}", Arrays.toString(entities));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entities}
|
||||
* @return {@link #entities}
|
||||
*/
|
||||
public int[] getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
+21
@@ -4,12 +4,25 @@ import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityMetadata minecraft packet.
|
||||
*/
|
||||
public class PacketPlayOutEntityMetadata implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
/** Entity's data that changed */
|
||||
private final DataWatcher dataWatcher;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param entityId
|
||||
* Entity's id
|
||||
* @param dataWatcher
|
||||
* Entity's data that changed
|
||||
*/
|
||||
public PacketPlayOutEntityMetadata(int entityId, DataWatcher dataWatcher) {
|
||||
Preconditions.checkNotNull(dataWatcher, "data watcher");
|
||||
this.entityId = entityId;
|
||||
@@ -21,10 +34,18 @@ public class PacketPlayOutEntityMetadata implements TabPacket {
|
||||
return String.format("PacketPlayOutEntityMetadata{entityId=%s,dataWatcher=%s}", entityId, dataWatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entityId}
|
||||
* @return {@link #entityId}
|
||||
*/
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #dataWatcher}
|
||||
* @return {@link #dataWatcher}
|
||||
*/
|
||||
public DataWatcher getDataWatcher() {
|
||||
return dataWatcher;
|
||||
}
|
||||
|
||||
+21
@@ -5,12 +5,25 @@ import org.bukkit.Location;
|
||||
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityTeleport minecraft packet.
|
||||
*/
|
||||
public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
/** Entity's new location */
|
||||
private final Location location;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param entityId
|
||||
* Entity's id
|
||||
* @param location
|
||||
* Entity's spawn location
|
||||
*/
|
||||
public PacketPlayOutEntityTeleport(int entityId, Location location) {
|
||||
Preconditions.checkNotNull(location, "location");
|
||||
this.entityId = entityId;
|
||||
@@ -22,10 +35,18 @@ public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
return String.format("PacketPlayOutEntityTeleport{entityId=%s,location=%s}", entityId, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entityId}
|
||||
* @return {@link #entityId}
|
||||
*/
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #location}
|
||||
* @return {@link #location}
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
+42
@@ -9,18 +9,40 @@ import org.bukkit.entity.EntityType;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutSpawnEntityLiving minecraft packet.
|
||||
*/
|
||||
public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
/** Entity's UUID (for 1.9+ servers) */
|
||||
private final UUID uuid;
|
||||
|
||||
/** Type of entity */
|
||||
private final EntityType entityType;
|
||||
|
||||
/** Entity's spawn location */
|
||||
private final Location location;
|
||||
|
||||
/** Entity's data for 1.14- servers */
|
||||
private final DataWatcher dataWatcher;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param entityId
|
||||
* Entity's id
|
||||
* @param uuid
|
||||
* Entity's UUID (for 1.9+ servers)
|
||||
* @param entityType
|
||||
* Type of entity
|
||||
* @param location
|
||||
* Entity's spawn location
|
||||
* @param dataWatcher
|
||||
* Entity's data for 1.14- servers
|
||||
*/
|
||||
public PacketPlayOutSpawnEntityLiving(int entityId, UUID uuid, EntityType entityType, Location location, DataWatcher dataWatcher) {
|
||||
Preconditions.checkNotNull(uuid, "uuid");
|
||||
Preconditions.checkNotNull(entityType, "entityType");
|
||||
@@ -38,22 +60,42 @@ public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
entityId, uuid, entityType, location, dataWatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entityId}
|
||||
* @return {@link #entityId}
|
||||
*/
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #uuid}
|
||||
* @return {@link #uuid}
|
||||
*/
|
||||
public UUID getUniqueId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entityType}
|
||||
* @return {@link #entityType}
|
||||
*/
|
||||
public EntityType getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #location}
|
||||
* @return {@link #location}
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #dataWatcher}
|
||||
* @return {@link #dataWatcher}
|
||||
*/
|
||||
public DataWatcher getDataWatcher() {
|
||||
return dataWatcher;
|
||||
}
|
||||
|
||||
+11
-6
@@ -5,17 +5,17 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.DataWatcher class to make work with it much easier
|
||||
*/
|
||||
public class DataWatcher {
|
||||
|
||||
//DataWatcher data
|
||||
/** Watched data */
|
||||
private final Map<Integer, DataWatcherItem> dataValues = new HashMap<>();
|
||||
|
||||
//a helper for easier data write
|
||||
/** Helper for easier data write */
|
||||
private final DataWatcherHelper helper = new DataWatcherHelper(this);
|
||||
|
||||
/**
|
||||
@@ -73,19 +73,19 @@ public class DataWatcher {
|
||||
NMSStorage nms = NMSStorage.getInstance();
|
||||
Object nmsWatcher;
|
||||
if (nms.newDataWatcher.getParameterCount() == 1) { //1.7+
|
||||
Object[] args = new Object[] {null};
|
||||
nmsWatcher = nms.newDataWatcher.newInstance(args);
|
||||
nmsWatcher = nms.newDataWatcher.newInstance(new Object[] {null});
|
||||
} else {
|
||||
nmsWatcher = nms.newDataWatcher.newInstance();
|
||||
}
|
||||
for (DataWatcherItem item : dataValues.values()) {
|
||||
Object position;
|
||||
if (nms.getMinorVersion() >= 9) {
|
||||
position = nms.newDataWatcherObject.newInstance(item.getType().getPosition(), item.getType().getClassType());
|
||||
position = nms.newDataWatcherObject.newInstance(item.getType().getPosition(), item.getType().getSerializer());
|
||||
} else {
|
||||
position = item.getType().getPosition();
|
||||
}
|
||||
nms.DataWatcher_REGISTER.invoke(nmsWatcher, position, item.getValue());
|
||||
if (nms.is1_19_3Plus()) nms.DataWatcher_markDirty.invoke(nmsWatcher, position);
|
||||
}
|
||||
return nmsWatcher;
|
||||
}
|
||||
@@ -112,4 +112,9 @@ public class DataWatcher {
|
||||
}
|
||||
return watcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DataWatcher{values=%s}", dataValues);
|
||||
}
|
||||
}
|
||||
+28
-16
@@ -1,25 +1,26 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.platforms.bukkit.BukkitPacketBuilder;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A class to help to assign DataWatcher items as positions often change per-version
|
||||
*/
|
||||
public class DataWatcherHelper {
|
||||
|
||||
//position of armor stand flags
|
||||
/** Position of armor stand flags */
|
||||
private final int armorStandFlagsPosition = getArmorStandFlagsPosition();
|
||||
|
||||
//original DataWatcher to write to
|
||||
/** DataWatcher to write to */
|
||||
private final DataWatcher data;
|
||||
|
||||
//data watcher registry
|
||||
/** Data Watcher registry reference */
|
||||
private final DataWatcherRegistry registry;
|
||||
|
||||
/**
|
||||
@@ -39,16 +40,16 @@ public class DataWatcherHelper {
|
||||
* @return armor stand flags position based on server version
|
||||
*/
|
||||
private int getArmorStandFlagsPosition() {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 17) {
|
||||
//1.17.x, 1.18.x
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 17) {
|
||||
//1.17.x, 1.18.x, 1.19.x
|
||||
return 15;
|
||||
} else if (TAB.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
//1.15.x, 1.16.x
|
||||
return 14;
|
||||
} else if (TAB.getInstance().getServerVersion().getMinorVersion() >= 14) {
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 14) {
|
||||
//1.14.x
|
||||
return 13;
|
||||
} else if (TAB.getInstance().getServerVersion().getMinorVersion() >= 10) {
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 10) {
|
||||
//1.10.x - 1.13.x
|
||||
return 11;
|
||||
} else {
|
||||
@@ -76,18 +77,18 @@ public class DataWatcherHelper {
|
||||
* client version
|
||||
*/
|
||||
public void setCustomName(String customName, ProtocolVersion clientVersion) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 13) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 13) {
|
||||
try {
|
||||
data.setValue(new DataWatcherObject(2, registry.getOptionalComponent()), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPlatform().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to create component", e);
|
||||
}
|
||||
} else if (TAB.getInstance().getServerVersion().getMinorVersion() >= 8){
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 8){
|
||||
data.setValue(new DataWatcherObject(2, registry.getString()), customName);
|
||||
} else {
|
||||
//name length is limited to 64 characters on <1.8
|
||||
String cutName = (customName.length() > 64 ? customName.substring(0, 64) : customName);
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 6){
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 6){
|
||||
data.setValue(new DataWatcherObject(10, null), cutName);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(5, null), cutName);
|
||||
@@ -102,7 +103,7 @@ public class DataWatcherHelper {
|
||||
* if visible or not
|
||||
*/
|
||||
public void setCustomNameVisible(boolean visible) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
data.setValue(new DataWatcherObject(3, registry.getBoolean()), visible);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(3, null), (byte)(visible?1:0));
|
||||
@@ -116,7 +117,7 @@ public class DataWatcherHelper {
|
||||
* health of entity
|
||||
*/
|
||||
public void setHealth(float health) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 6) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 6) {
|
||||
data.setValue(new DataWatcherObject(6, registry.getFloat()), health);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(16, null), (int)health);
|
||||
@@ -132,4 +133,15 @@ public class DataWatcherHelper {
|
||||
public void setArmorStandFlags(byte flags) {
|
||||
data.setValue(new DataWatcherObject(armorStandFlagsPosition, registry.getByte()), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes wither invulnerable time
|
||||
* @param time
|
||||
* Time, apparently
|
||||
*/
|
||||
public void setWitherInvulnerableTime(int time) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() > 8)
|
||||
throw new UnsupportedOperationException("Not supported on 1.9+");
|
||||
data.setValue(new DataWatcherObject(20, null), time);
|
||||
}
|
||||
}
|
||||
+26
-7
@@ -1,15 +1,18 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
/**
|
||||
* Class representing NMS Data Watcher Item
|
||||
*/
|
||||
public class DataWatcherItem {
|
||||
|
||||
//type of value (position + data type (1.9+))
|
||||
/** Value type */
|
||||
private final DataWatcherObject type;
|
||||
|
||||
//actual data value
|
||||
/** Data value */
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
@@ -38,19 +41,35 @@ public class DataWatcherItem {
|
||||
*/
|
||||
public static DataWatcherItem fromNMS(Object nmsItem) throws ReflectiveOperationException {
|
||||
NMSStorage nms = NMSStorage.getInstance();
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
Object value = nms.DataWatcherItem_VALUE.get(nmsItem);
|
||||
DataWatcherObject object;
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
Object nmsObject = nms.DataWatcherItem_TYPE.get(nmsItem);
|
||||
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherObject_SLOT.getInt(nmsObject), nms.DataWatcherObject_SERIALIZER.get(nmsObject)), nms.DataWatcherItem_VALUE.get(nmsItem));
|
||||
object = new DataWatcherObject(nms.DataWatcherObject_SLOT.getInt(nmsObject), nms.DataWatcherObject_SERIALIZER.get(nmsObject));
|
||||
} else {
|
||||
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherItem_TYPE.getInt(nmsItem), null), nms.DataWatcherItem_VALUE.get(nmsItem));
|
||||
object = new DataWatcherObject(nms.DataWatcherItem_TYPE.getInt(nmsItem), null);
|
||||
}
|
||||
return new DataWatcherItem(object, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #type}
|
||||
* @return {@link #type}
|
||||
*/
|
||||
public DataWatcherObject getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #value}
|
||||
* @return {@link #value}
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DataWatcherItem{type=%s,value=%s}", type, value);
|
||||
}
|
||||
}
|
||||
+22
-9
@@ -5,30 +5,43 @@ package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
*/
|
||||
public class DataWatcherObject {
|
||||
|
||||
//position in DataWatcher
|
||||
/** Object's position in DataWatcher */
|
||||
private final int position;
|
||||
|
||||
//value class type used since 1.9
|
||||
private final Object classType;
|
||||
/** Value encoder for 1.9+ */
|
||||
private final Object serializer;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this class with given parameters
|
||||
*
|
||||
* @param position
|
||||
* position in DataWatcher
|
||||
* @param classType
|
||||
* value class type
|
||||
* @param serializer
|
||||
* Serializer for value in this slot
|
||||
*/
|
||||
public DataWatcherObject(int position, Object classType){
|
||||
public DataWatcherObject(int position, Object serializer){
|
||||
this.position = position;
|
||||
this.classType = classType;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #position}
|
||||
* @return {@link #position}
|
||||
*/
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public Object getClassType() {
|
||||
return classType;
|
||||
/**
|
||||
* Returns {@link #serializer}
|
||||
* @return {@link #serializer}
|
||||
*/
|
||||
public Object getSerializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DataWatcherObject{position=%d,serializer=%s}", position, serializer);
|
||||
}
|
||||
}
|
||||
+52
-33
@@ -1,21 +1,27 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.DataWatcherRegistry class to make work with it much easier
|
||||
*/
|
||||
public class DataWatcherRegistry {
|
||||
|
||||
/** Byte encoder */
|
||||
private Object registryByte;
|
||||
|
||||
/** Float encoder */
|
||||
private Object registryFloat;
|
||||
|
||||
/** String encoder */
|
||||
private Object registryString;
|
||||
|
||||
/** Encoder for optional component */
|
||||
private Object registryOptionalIChatBaseComponent;
|
||||
|
||||
/** Boolean encoder */
|
||||
private Object registryBoolean;
|
||||
|
||||
/**
|
||||
@@ -23,57 +29,70 @@ public class DataWatcherRegistry {
|
||||
*/
|
||||
public DataWatcherRegistry(NMSStorage nms) {
|
||||
if (nms.getMinorVersion() >= 9) {
|
||||
Map<String, Object> fields = getStaticFields(nms.DataWatcherRegistry, nms);
|
||||
registryByte = fields.get("a");
|
||||
registryFloat = fields.get("c");
|
||||
registryString = fields.get("d");
|
||||
if (nms.getMinorVersion() >= 13) {
|
||||
registryOptionalIChatBaseComponent = fields.get("f");
|
||||
registryBoolean = fields.get("i");
|
||||
Map<String, Object> fields = nms.getStaticFields(nms.DataWatcherRegistry);
|
||||
if (fields.containsKey("a")) {
|
||||
// Bukkit mapping
|
||||
registryByte = fields.get("a");
|
||||
registryFloat = fields.get("c");
|
||||
registryString = fields.get("d");
|
||||
if (nms.getMinorVersion() >= 13) {
|
||||
if (nms.is1_19_3Plus()) {
|
||||
registryOptionalIChatBaseComponent = fields.get("g");
|
||||
registryBoolean = fields.get("j");
|
||||
} else {
|
||||
registryOptionalIChatBaseComponent = fields.get("f");
|
||||
registryBoolean = fields.get("i");
|
||||
}
|
||||
} else {
|
||||
registryBoolean = fields.get("h");
|
||||
}
|
||||
} else {
|
||||
registryBoolean = fields.get("h");
|
||||
// Mojang mapping
|
||||
registryByte = fields.get("BYTE");
|
||||
registryFloat = fields.get("FLOAT");
|
||||
registryString = fields.get("STRING");
|
||||
registryOptionalIChatBaseComponent = fields.get("OPTIONAL_COMPONENT");
|
||||
registryBoolean = fields.get("BOOLEAN");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets values of all static fields in a class
|
||||
*
|
||||
* @param clazz
|
||||
* class to return field values from
|
||||
* @return map of values
|
||||
* Returns {@link #registryByte}
|
||||
* @return {@link #registryByte}
|
||||
*/
|
||||
private Map<String, Object> getStaticFields(Class<?> clazz, NMSStorage nms){
|
||||
Map<String, Object> fields = new HashMap<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
nms.setAccessible(field);
|
||||
try {
|
||||
fields.put(field.getName(), field.get(null));
|
||||
} catch (IllegalAccessException e) {
|
||||
//this will never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
public Object getByte() {
|
||||
return registryByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryFloat}
|
||||
* @return {@link #registryFloat}
|
||||
*/
|
||||
public Object getFloat() {
|
||||
return registryFloat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryString}
|
||||
* @return {@link #registryString}
|
||||
*/
|
||||
public Object getString() {
|
||||
return registryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryOptionalIChatBaseComponent}
|
||||
* @return {@link #registryOptionalIChatBaseComponent}
|
||||
*/
|
||||
public Object getOptionalComponent() {
|
||||
return registryOptionalIChatBaseComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryBoolean}
|
||||
* @return {@link #registryBoolean}
|
||||
*/
|
||||
public Object getBoolean() {
|
||||
return registryBoolean;
|
||||
}
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* NMS loader for Minecraft 1.5.2 - 1.16.5 using Bukkit mapping.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class BukkitLegacyNMSStorage extends NMSStorage {
|
||||
|
||||
/**
|
||||
* Creates new instance, initializes required NMS classes and fields
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If any class, field or method fails to load
|
||||
*/
|
||||
public BukkitLegacyNMSStorage() throws ReflectiveOperationException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class with given potential names in same order
|
||||
*
|
||||
* @param names
|
||||
* possible class names
|
||||
* @return class for specified names
|
||||
* @throws ClassNotFoundException
|
||||
* if class does not exist
|
||||
*/
|
||||
private Class<?> getLegacyClass(String... names) throws ClassNotFoundException {
|
||||
for (String name : names) {
|
||||
try {
|
||||
return getLegacyClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
//not the first class name in array
|
||||
}
|
||||
}
|
||||
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
|
||||
*/
|
||||
public Class<?> getLegacyClass(String name) throws ClassNotFoundException {
|
||||
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
|
||||
PING = getField(EntityPlayer, "ping");
|
||||
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[] {"setScore", "c"}, int.class); // 1.5.1+, 1.5
|
||||
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[] {"setAllowFriendlyFire", "a"}, boolean.class); // 1.5.1+, 1.5
|
||||
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[] {"setCanSeeFriendlyInvisibles", "b"}, boolean.class); // 1.5.1+, 1.5
|
||||
if (minorVersion >= 7) {
|
||||
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
|
||||
}
|
||||
if (minorVersion >= 8) {
|
||||
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[] {"setNameTagVisibility", "a"}, EnumNameTagVisibility); // {1.8.1+, 1.8}
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
DataWatcher_REGISTER = DataWatcher.getMethod("register", DataWatcherObject, Object.class);
|
||||
} else {
|
||||
DataWatcher_REGISTER = DataWatcher.getMethod("a", int.class, Object.class);
|
||||
}
|
||||
if (minorVersion >= 13) {
|
||||
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("setPrefix", IChatBaseComponent);
|
||||
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("setSuffix", IChatBaseComponent);
|
||||
} else {
|
||||
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[] {"setPrefix", "b"}, String.class); // 1.5.1+, 1.5
|
||||
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[] {"setSuffix", "c"}, String.class); // 1.5.1+, 1.5
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadClasses() throws ClassNotFoundException {
|
||||
EntityHuman = getLegacyClass("EntityHuman");
|
||||
World = getLegacyClass("World");
|
||||
Packet = getLegacyClass("Packet");
|
||||
EnumChatFormat = (Class<Enum>) getLegacyClass("EnumChatFormat");
|
||||
EntityPlayer = getLegacyClass("EntityPlayer");
|
||||
Entity = getLegacyClass("Entity");
|
||||
EntityLiving = getLegacyClass("EntityLiving");
|
||||
PlayerConnection = getLegacyClass("PlayerConnection");
|
||||
PacketPlayOutChat = getLegacyClass("PacketPlayOutChat", "Packet3Chat");
|
||||
NetworkManager = getLegacyClass("NetworkManager");
|
||||
if (minorVersion >= 7) {
|
||||
IChatBaseComponent = getLegacyClass("IChatBaseComponent");
|
||||
ChatSerializer = getLegacyClass("IChatBaseComponent$ChatSerializer", "ChatSerializer");
|
||||
}
|
||||
if (minorVersion >= 8) {
|
||||
PacketPlayOutPlayerListHeaderFooter = getLegacyClass("PacketPlayOutPlayerListHeaderFooter");
|
||||
EntityArmorStand = getLegacyClass("EntityArmorStand");
|
||||
}
|
||||
if (minorVersion >= 12) {
|
||||
ChatMessageType = (Class<Enum>) getLegacyClass("ChatMessageType");
|
||||
}
|
||||
|
||||
// DataWatcher
|
||||
DataWatcher = getLegacyClass("DataWatcher");
|
||||
DataWatcherItem = getLegacyClass("DataWatcher$Item", "DataWatcher$WatchableObject", "WatchableObject");
|
||||
if (minorVersion >= 9) {
|
||||
DataWatcherObject = getLegacyClass("DataWatcherObject");
|
||||
DataWatcherRegistry = getLegacyClass("DataWatcherRegistry");
|
||||
DataWatcherSerializer = getLegacyClass("DataWatcherSerializer");
|
||||
}
|
||||
|
||||
// Entities
|
||||
PacketPlayOutSpawnEntityLiving = getLegacyClass("PacketPlayOutSpawnEntityLiving", "Packet24MobSpawn");
|
||||
PacketPlayOutEntityTeleport = getLegacyClass("PacketPlayOutEntityTeleport", "Packet34EntityTeleport");
|
||||
PacketPlayInUseEntity = getLegacyClass("PacketPlayInUseEntity", "Packet7UseEntity");
|
||||
PacketPlayOutEntity = getLegacyClass("PacketPlayOutEntity", "Packet30Entity");
|
||||
PacketPlayOutEntityDestroy = getLegacyClass("PacketPlayOutEntityDestroy", "Packet29DestroyEntity");
|
||||
PacketPlayOutEntityLook = getLegacyClass("PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntityLook", "Packet32EntityLook");
|
||||
PacketPlayOutEntityMetadata = getLegacyClass("PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
|
||||
PacketPlayOutNamedEntitySpawn = getLegacyClass("PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
|
||||
if (minorVersion >= 7) {
|
||||
EnumEntityUseAction = (Class<Enum>) getLegacyClass("PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction");
|
||||
}
|
||||
|
||||
// Player Info
|
||||
if (minorVersion >= 8) {
|
||||
PacketPlayOutPlayerInfo = getLegacyClass("PacketPlayOutPlayerInfo");
|
||||
EnumPlayerInfoAction = (Class<Enum>) getLegacyClass("PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
|
||||
PlayerInfoData = getLegacyClass("PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
|
||||
EnumGamemode = (Class<Enum>) getLegacyClass("EnumGamemode", "WorldSettings$EnumGamemode");
|
||||
}
|
||||
|
||||
// Scoreboard
|
||||
PacketPlayOutScoreboardDisplayObjective = getLegacyClass("PacketPlayOutScoreboardDisplayObjective", "Packet208SetScoreboardDisplayObjective");
|
||||
PacketPlayOutScoreboardObjective = getLegacyClass("PacketPlayOutScoreboardObjective", "Packet206SetScoreboardObjective");
|
||||
PacketPlayOutScoreboardTeam = getLegacyClass("PacketPlayOutScoreboardTeam", "Packet209SetScoreboardTeam");
|
||||
PacketPlayOutScoreboardScore = getLegacyClass("PacketPlayOutScoreboardScore", "Packet207SetScoreboardScore");
|
||||
Scoreboard = getLegacyClass("Scoreboard");
|
||||
ScoreboardObjective = getLegacyClass("ScoreboardObjective");
|
||||
ScoreboardScore = getLegacyClass("ScoreboardScore");
|
||||
IScoreboardCriteria = getLegacyClass("IScoreboardCriteria", "IObjective"); // 1.5.1+, 1.5
|
||||
ScoreboardTeam = getLegacyClass("ScoreboardTeam");
|
||||
if (minorVersion >= 8) {
|
||||
EnumScoreboardHealthDisplay = (Class<Enum>) getLegacyClass("IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
|
||||
EnumScoreboardAction = (Class<Enum>) getLegacyClass("ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
|
||||
EnumNameTagVisibility = (Class<Enum>) getLegacyClass("ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
EnumTeamPush = (Class<Enum>) getLegacyClass("ScoreboardTeamBase$EnumTeamPush");
|
||||
}
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage;
|
||||
|
||||
/**
|
||||
* NMS loader for minecraft 1.17+ using Mojang packaging and bukkit names.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class BukkitModernNMSStorage extends NMSStorage {
|
||||
|
||||
/**
|
||||
* Creates new instance, initializes required NMS classes and fields
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If any class, field or method fails to load
|
||||
*/
|
||||
public BukkitModernNMSStorage() throws ReflectiveOperationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
|
||||
PING = getField(EntityPlayer, "e");
|
||||
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
|
||||
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"register", "a"}, DataWatcherObject, Object.class); // {Bukkit, Bukkit 1.18+}
|
||||
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[]{"setScore", "b"}, int.class); // {Bukkit, Bukkit 1.18+}
|
||||
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a"}, boolean.class); // {Bukkit, Bukkit 1.18+}
|
||||
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[]{"setCanSeeFriendlyInvisibles", "b"}, boolean.class); // {Bukkit, Bukkit 1.18+}
|
||||
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[]{"setPrefix", "b"}, IChatBaseComponent); // {Bukkit, Bukkit 1.18+}
|
||||
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[]{"setSuffix", "c"}, IChatBaseComponent); // {Bukkit, Bukkit 1.18+}
|
||||
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[]{"setNameTagVisibility", "a"}, EnumNameTagVisibility); // {Bukkit, Bukkit 1.18+}
|
||||
if (minorVersion >= 19) {
|
||||
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("d").get(null);
|
||||
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getField(PacketPlayOutSpawnEntityLiving, "e");
|
||||
DataWatcher_b = DataWatcher.getMethod("b");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadClasses() throws ClassNotFoundException {
|
||||
ChatSerializer = Class.forName("net.minecraft.network.chat.IChatBaseComponent$ChatSerializer");
|
||||
World = Class.forName("net.minecraft.world.level.World");
|
||||
EntityArmorStand = Class.forName("net.minecraft.world.entity.decoration.EntityArmorStand");
|
||||
EntityHuman = Class.forName("net.minecraft.world.entity.player.EntityHuman");
|
||||
NetworkManager = Class.forName("net.minecraft.network.NetworkManager");
|
||||
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
|
||||
Packet = Class.forName("net.minecraft.network.protocol.Packet");
|
||||
EnumChatFormat = (Class<Enum>) Class.forName("net.minecraft.EnumChatFormat");
|
||||
EntityPlayer = Class.forName("net.minecraft.server.level.EntityPlayer");
|
||||
Entity = Class.forName("net.minecraft.world.entity.Entity");
|
||||
EntityLiving = Class.forName("net.minecraft.world.entity.EntityLiving");
|
||||
PlayerConnection = Class.forName("net.minecraft.server.network.PlayerConnection");
|
||||
|
||||
PacketPlayOutPlayerListHeaderFooter = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter");
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundSystemChatPacket");
|
||||
} else {
|
||||
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutChat");
|
||||
ChatMessageType = (Class<Enum>) Class.forName("net.minecraft.network.chat.ChatMessageType");
|
||||
}
|
||||
|
||||
// DataWatcher
|
||||
DataWatcher = Class.forName("net.minecraft.network.syncher.DataWatcher");
|
||||
DataWatcherItem = Class.forName("net.minecraft.network.syncher.DataWatcher$Item");
|
||||
DataWatcherObject = Class.forName("net.minecraft.network.syncher.DataWatcherObject");
|
||||
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.DataWatcherRegistry");
|
||||
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.DataWatcherSerializer");
|
||||
if (is1_19_3Plus()) {
|
||||
DataWatcher$DataValue = Class.forName("net.minecraft.network.syncher.DataWatcher$b");
|
||||
}
|
||||
|
||||
// Entities
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
|
||||
} else {
|
||||
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
|
||||
}
|
||||
PacketPlayOutEntityTeleport = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport");
|
||||
PacketPlayInUseEntity = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity");
|
||||
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$d");
|
||||
PacketPlayOutEntity = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntity");
|
||||
PacketPlayOutEntityDestroy = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy");
|
||||
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook");
|
||||
PacketPlayOutEntityMetadata = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata");
|
||||
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn");
|
||||
EnumEntityUseAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction");
|
||||
if (minorVersion >= 19) {
|
||||
EntityTypes = Class.forName("net.minecraft.world.entity.EntityTypes");
|
||||
}
|
||||
|
||||
// Player Info
|
||||
if (minorVersion >= 19) {
|
||||
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
|
||||
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$a");
|
||||
}
|
||||
if (is1_19_3Plus()) {
|
||||
//1.19.3+
|
||||
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
|
||||
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
|
||||
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$a");
|
||||
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$b");
|
||||
RemoteChatSession = Class.forName("net.minecraft.network.chat.RemoteChatSession");
|
||||
RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$a");
|
||||
} else {
|
||||
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo");
|
||||
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
|
||||
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData");
|
||||
}
|
||||
EnumGamemode = (Class<Enum>) Class.forName("net.minecraft.world.level.EnumGamemode");
|
||||
|
||||
// Scoreboard
|
||||
PacketPlayOutScoreboardDisplayObjective = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective");
|
||||
PacketPlayOutScoreboardObjective = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective");
|
||||
Scoreboard = Class.forName("net.minecraft.world.scores.Scoreboard");
|
||||
PacketPlayOutScoreboardScore = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore");
|
||||
ScoreboardObjective = Class.forName("net.minecraft.world.scores.ScoreboardObjective");
|
||||
ScoreboardScore = Class.forName("net.minecraft.world.scores.ScoreboardScore");
|
||||
IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria");
|
||||
EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay");
|
||||
EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ScoreboardServer$Action");
|
||||
PacketPlayOutScoreboardTeam = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam");
|
||||
ScoreboardTeam = Class.forName("net.minecraft.world.scores.ScoreboardTeam");
|
||||
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility");
|
||||
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush");
|
||||
PacketPlayOutScoreboardTeam_PlayerAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$a");
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage;
|
||||
|
||||
/**
|
||||
* NMS loader for minecraft 1.17+ using Mojang packaging and names.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class MojangModernNMSStorage extends NMSStorage {
|
||||
|
||||
/**
|
||||
* Creates new instance, initializes required NMS classes and fields
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If any class, field or method fails to load
|
||||
*/
|
||||
public MojangModernNMSStorage() throws ReflectiveOperationException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
|
||||
PING = getField(EntityPlayer, "latency");
|
||||
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("fromJson", String.class);
|
||||
DataWatcher_REGISTER = DataWatcher.getMethod("define", DataWatcherObject, Object.class);
|
||||
ScoreboardScore_setScore = ScoreboardScore.getMethod("setScore", int.class);
|
||||
ScoreboardTeam_setAllowFriendlyFire = ScoreboardTeam.getMethod("setAllowFriendlyFire", boolean.class);
|
||||
ScoreboardTeam_setCanSeeFriendlyInvisibles = ScoreboardTeam.getMethod("setSeeFriendlyInvisibles", boolean.class);
|
||||
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("setPlayerPrefix", IChatBaseComponent);
|
||||
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("setPlayerSuffix", IChatBaseComponent);
|
||||
ScoreboardTeam_setNameTagVisibility = ScoreboardTeam.getMethod("setNameTagVisibility", EnumNameTagVisibility);
|
||||
if (minorVersion >= 19) {
|
||||
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("ARMOR_STAND").get(null);
|
||||
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getField(PacketPlayOutSpawnEntityLiving, "type");
|
||||
DataWatcher_b = DataWatcher.getMethod("packDirty");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadClasses() throws ClassNotFoundException {
|
||||
IChatBaseComponent = Class.forName("net.minecraft.network.chat.Component");
|
||||
ChatSerializer = Class.forName("net.minecraft.network.chat.Component$Serializer");
|
||||
World = Class.forName("net.minecraft.world.level.Level");
|
||||
EntityArmorStand = Class.forName("net.minecraft.world.entity.decoration.ArmorStand");
|
||||
EntityHuman = Class.forName("net.minecraft.world.entity.player.Player");
|
||||
NetworkManager = Class.forName("net.minecraft.network.Connection");
|
||||
Packet = Class.forName("net.minecraft.network.protocol.Packet");
|
||||
EnumChatFormat = (Class<Enum>) Class.forName("net.minecraft.ChatFormatting");
|
||||
EntityPlayer = Class.forName("net.minecraft.server.level.ServerPlayer");
|
||||
Entity = Class.forName("net.minecraft.world.entity.Entity");
|
||||
EntityLiving = Class.forName("net.minecraft.world.entity.LivingEntity");
|
||||
PlayerConnection = Class.forName("net.minecraft.server.network.ServerGamePacketListenerImpl");
|
||||
PacketPlayOutPlayerListHeaderFooter = Class.forName("net.minecraft.network.protocol.game.ClientboundTabListPacket");
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundSystemChatPacket");
|
||||
} else {
|
||||
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundChatPacket");
|
||||
ChatMessageType = (Class<Enum>) Class.forName("net.minecraft.network.chat.ChatType");
|
||||
}
|
||||
|
||||
// DataWatcher
|
||||
DataWatcher = Class.forName("net.minecraft.network.syncher.SynchedEntityData");
|
||||
DataWatcherItem = Class.forName("net.minecraft.network.syncher.SynchedEntityData$DataItem");
|
||||
DataWatcherObject = Class.forName("net.minecraft.network.syncher.EntityDataAccessor");
|
||||
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.EntityDataSerializers");
|
||||
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.EntityDataSerializer");
|
||||
if (is1_19_3Plus()) {
|
||||
DataWatcher$DataValue = Class.forName("net.minecraft.network.syncher.SynchedEntityData$DataValue");
|
||||
}
|
||||
|
||||
// Entities
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
|
||||
} else {
|
||||
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.ClientboundAddMobPacket");
|
||||
}
|
||||
PacketPlayOutEntityTeleport = Class.forName("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
|
||||
PacketPlayInUseEntity = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket");
|
||||
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$InteractionAction");
|
||||
PacketPlayOutEntity = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket");
|
||||
PacketPlayOutEntityDestroy = Class.forName("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket");
|
||||
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot");
|
||||
PacketPlayOutEntityMetadata = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket");
|
||||
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket");
|
||||
EnumEntityUseAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action");
|
||||
if (minorVersion >= 19) {
|
||||
EntityTypes = Class.forName("net.minecraft.world.entity.EntityType");
|
||||
}
|
||||
|
||||
// Player Info
|
||||
if (minorVersion >= 19) {
|
||||
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
|
||||
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$Data");
|
||||
}
|
||||
if (is1_19_3Plus()) {
|
||||
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
|
||||
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
|
||||
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action");
|
||||
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry");
|
||||
RemoteChatSession = Class.forName("net.minecraft.network.chat.RemoteChatSession");
|
||||
RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$Data");
|
||||
} else {
|
||||
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket");
|
||||
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$Action");
|
||||
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate");
|
||||
}
|
||||
EnumGamemode = (Class<Enum>) Class.forName("net.minecraft.world.level.GameType");
|
||||
|
||||
// Scoreboard
|
||||
PacketPlayOutScoreboardDisplayObjective = Class.forName("net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket");
|
||||
PacketPlayOutScoreboardObjective = Class.forName("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket");
|
||||
Scoreboard = Class.forName("net.minecraft.world.scores.Scoreboard");
|
||||
PacketPlayOutScoreboardScore = Class.forName("net.minecraft.network.protocol.game.ClientboundSetScorePacket");
|
||||
ScoreboardObjective = Class.forName("net.minecraft.world.scores.Objective");
|
||||
ScoreboardScore = Class.forName("net.minecraft.world.scores.Score");
|
||||
IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria");
|
||||
EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType");
|
||||
EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ServerScoreboard$Method");
|
||||
PacketPlayOutScoreboardTeam = Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket");
|
||||
ScoreboardTeam = Class.forName("net.minecraft.world.scores.PlayerTeam");
|
||||
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$Visibility");
|
||||
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$CollisionRule");
|
||||
PacketPlayOutScoreboardTeam_PlayerAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket$Action");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,758 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherRegistry;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class holding all NMS classes, methods, fields and constructors used by TAB.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public abstract class NMSStorage {
|
||||
|
||||
/** Instance of this class */
|
||||
private static NMSStorage instance;
|
||||
|
||||
/** Server's NMS/CraftBukkit package */
|
||||
protected final String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
|
||||
/** Server's minor version */
|
||||
protected final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
|
||||
|
||||
/** Flag determining whether the server version is at least 1.19.3 or not */
|
||||
private boolean is1_19_3Plus;
|
||||
|
||||
/** Basic universal values */
|
||||
protected Class<?> Packet;
|
||||
protected Class<?> NetworkManager;
|
||||
protected Class<?> EntityArmorStand;
|
||||
protected Class<?> World;
|
||||
public Class<Enum> EnumChatFormat;
|
||||
public Class<?> EntityPlayer;
|
||||
protected Class<?> EntityHuman;
|
||||
protected Class<?> Entity;
|
||||
protected Class<?> EntityLiving;
|
||||
protected Class<?> PlayerConnection;
|
||||
public Field PING;
|
||||
public Field PLAYER_CONNECTION;
|
||||
public Field NETWORK_MANAGER;
|
||||
public Field CHANNEL;
|
||||
public Field EntityHuman_ProfilePublicKey;
|
||||
public Field EntityPlayer_RemoteChatSession;
|
||||
public Method getHandle;
|
||||
public Method sendPacket;
|
||||
public Method getProfile;
|
||||
|
||||
/** Chat components */
|
||||
protected Class<?> IChatBaseComponent;
|
||||
protected Class<?> ChatSerializer;
|
||||
public Method ChatSerializer_DESERIALIZE;
|
||||
|
||||
/** PacketPlayOutChat */
|
||||
protected Class<?> PacketPlayOutChat;
|
||||
public Class<Enum> ChatMessageType;
|
||||
public Constructor<?> newPacketPlayOutChat;
|
||||
|
||||
/** DataWatcher */
|
||||
protected Class<?> DataWatcher;
|
||||
protected Class<?> DataWatcherItem;
|
||||
protected Class<?> DataWatcherObject;
|
||||
protected Class<?> DataWatcherSerializer;
|
||||
public Class<?> DataWatcherRegistry;
|
||||
public Constructor<?> newDataWatcher;
|
||||
public Constructor<?> newDataWatcherObject;
|
||||
public Field DataWatcherItem_TYPE;
|
||||
public Field DataWatcherItem_VALUE;
|
||||
public Field DataWatcherObject_SLOT;
|
||||
public Field DataWatcherObject_SERIALIZER;
|
||||
public Method DataWatcher_REGISTER;
|
||||
private final DataWatcherRegistry registry;
|
||||
//1.19.3+
|
||||
protected Class<?> DataWatcher$DataValue;
|
||||
public Field DataWatcher$DataValue_POSITION;
|
||||
public Field DataWatcher$DataValue_VALUE;
|
||||
public Method DataWatcher_markDirty;
|
||||
public Method DataWatcher_b;
|
||||
|
||||
/** PacketPlayOutSpawnEntityLiving */
|
||||
public Class<?> PacketPlayOutSpawnEntityLiving;
|
||||
public Class<?> EntityTypes;
|
||||
public Constructor<?> newPacketPlayOutSpawnEntityLiving;
|
||||
public Field PacketPlayOutSpawnEntityLiving_ENTITYID;
|
||||
public Field PacketPlayOutSpawnEntityLiving_ENTITYTYPE;
|
||||
public Field PacketPlayOutSpawnEntityLiving_YAW;
|
||||
public Field PacketPlayOutSpawnEntityLiving_PITCH;
|
||||
public Field PacketPlayOutSpawnEntityLiving_UUID;
|
||||
public Field PacketPlayOutSpawnEntityLiving_X;
|
||||
public Field PacketPlayOutSpawnEntityLiving_Y;
|
||||
public Field PacketPlayOutSpawnEntityLiving_Z;
|
||||
public Field PacketPlayOutSpawnEntityLiving_DATAWATCHER;
|
||||
public Object EntityTypes_ARMOR_STAND;
|
||||
|
||||
/** PacketPlayOutEntityTeleport */
|
||||
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;
|
||||
|
||||
/** PacketPlayOutPlayerListHeaderFooter */
|
||||
protected Class<?> PacketPlayOutPlayerListHeaderFooter;
|
||||
public Constructor<?> newPacketPlayOutPlayerListHeaderFooter;
|
||||
public Field PacketPlayOutPlayerListHeaderFooter_HEADER;
|
||||
public Field PacketPlayOutPlayerListHeaderFooter_FOOTER;
|
||||
|
||||
/** Other entity packets */
|
||||
public Class<?> PacketPlayInUseEntity;
|
||||
public Class<?> PacketPlayInUseEntity$d;
|
||||
protected Class<Enum> EnumEntityUseAction;
|
||||
public Field PacketPlayInUseEntity_ENTITY;
|
||||
public Field PacketPlayInUseEntity_ACTION;
|
||||
|
||||
public Class<?> PacketPlayOutEntity;
|
||||
public Field PacketPlayOutEntity_ENTITYID;
|
||||
|
||||
public Class<?> PacketPlayOutEntityDestroy;
|
||||
public Constructor<?> newPacketPlayOutEntityDestroy;
|
||||
public Field PacketPlayOutEntityDestroy_ENTITIES;
|
||||
|
||||
public Class<?> PacketPlayOutEntityLook;
|
||||
|
||||
public Class<?> PacketPlayOutEntityMetadata;
|
||||
public Constructor<?> newPacketPlayOutEntityMetadata;
|
||||
public Field PacketPlayOutEntityMetadata_LIST;
|
||||
|
||||
public Class<?> PacketPlayOutNamedEntitySpawn;
|
||||
public Field PacketPlayOutNamedEntitySpawn_ENTITYID;
|
||||
|
||||
/** PacketPlayOutPlayerInfo */
|
||||
public Class<?> PacketPlayOutPlayerInfo;
|
||||
public Class<Enum> EnumPlayerInfoAction;
|
||||
protected Class<?> PlayerInfoData;
|
||||
public Class<Enum> EnumGamemode;
|
||||
protected Class<?> ProfilePublicKey;
|
||||
protected Class<?> ProfilePublicKey$a;
|
||||
public Constructor<?> newPacketPlayOutPlayerInfo;
|
||||
public Constructor<?> newPlayerInfoData;
|
||||
public Field PacketPlayOutPlayerInfo_ACTION;
|
||||
public Field PacketPlayOutPlayerInfo_PLAYERS;
|
||||
public Method PlayerInfoData_getProfile;
|
||||
public Method PlayerInfoData_isListed;
|
||||
public Method PlayerInfoData_getLatency;
|
||||
public Method PlayerInfoData_getGamemode;
|
||||
public Method PlayerInfoData_getDisplayName;
|
||||
public Method PlayerInfoData_getProfilePublicKeyRecord;
|
||||
public Method ProfilePublicKey_getRecord;
|
||||
//1.19.3+
|
||||
public Class<?> ClientboundPlayerInfoRemovePacket;
|
||||
public Class<?> RemoteChatSession;
|
||||
public Class<?> RemoteChatSession$Data;
|
||||
public Constructor<?> newClientboundPlayerInfoRemovePacket;
|
||||
public Constructor<?> newRemoteChatSession;
|
||||
public Constructor<?> newRemoteChatSession$Data;
|
||||
public Method ClientboundPlayerInfoRemovePacket_getEntries;
|
||||
public Method RemoteChatSession_getSessionId;
|
||||
public Method RemoteChatSession_getProfilePublicKey;
|
||||
public Method RemoteChatSession$Data_getSessionId;
|
||||
public Method RemoteChatSession$Data_getProfilePublicKey;
|
||||
|
||||
|
||||
/** Scoreboard objectives */
|
||||
public Class<?> PacketPlayOutScoreboardDisplayObjective;
|
||||
public Class<?> PacketPlayOutScoreboardObjective;
|
||||
protected Class<?> Scoreboard;
|
||||
protected Class<?> PacketPlayOutScoreboardScore;
|
||||
protected Class<?> ScoreboardObjective;
|
||||
protected Class<?> ScoreboardScore;
|
||||
protected Class<?> IScoreboardCriteria;
|
||||
public Class<Enum> EnumScoreboardHealthDisplay;
|
||||
public Class<Enum> EnumScoreboardAction;
|
||||
public Constructor<?> newScoreboardObjective;
|
||||
protected Constructor<?> newScoreboard;
|
||||
public Constructor<?> newScoreboardScore;
|
||||
public Constructor<?> newPacketPlayOutScoreboardDisplayObjective;
|
||||
public Constructor<?> newPacketPlayOutScoreboardObjective;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore_1_13;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore_String;
|
||||
public Constructor<?> newPacketPlayOutScoreboardScore;
|
||||
public Field PacketPlayOutScoreboardDisplayObjective_POSITION;
|
||||
public Field PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME;
|
||||
public Field PacketPlayOutScoreboardObjective_OBJECTIVENAME;
|
||||
public Field PacketPlayOutScoreboardObjective_METHOD;
|
||||
public Field IScoreboardCriteria_self;
|
||||
public Field PacketPlayOutScoreboardObjective_RENDERTYPE;
|
||||
public Field PacketPlayOutScoreboardObjective_DISPLAYNAME;
|
||||
public Method ScoreboardScore_setScore;
|
||||
|
||||
/** PacketPlayOutScoreboardTeam */
|
||||
public Class<?> PacketPlayOutScoreboardTeam;
|
||||
protected Class<?> ScoreboardTeam;
|
||||
public Class<Enum> EnumNameTagVisibility;
|
||||
public Class<Enum> EnumTeamPush;
|
||||
public Class<Enum> PacketPlayOutScoreboardTeam_PlayerAction;
|
||||
public Constructor<?> newScoreboardTeam;
|
||||
public Constructor<?> newPacketPlayOutScoreboardTeam;
|
||||
public Field PacketPlayOutScoreboardTeam_NAME;
|
||||
public Field PacketPlayOutScoreboardTeam_ACTION;
|
||||
public Field PacketPlayOutScoreboardTeam_PLAYERS;
|
||||
public Method ScoreboardTeam_getPlayerNameSet;
|
||||
public Method ScoreboardTeam_setNameTagVisibility;
|
||||
public Method ScoreboardTeam_setCollisionRule;
|
||||
public Method ScoreboardTeam_setPrefix;
|
||||
public Method ScoreboardTeam_setSuffix;
|
||||
public Method ScoreboardTeam_setColor;
|
||||
public Method ScoreboardTeam_setAllowFriendlyFire;
|
||||
public Method ScoreboardTeam_setCanSeeFriendlyInvisibles;
|
||||
public Method PacketPlayOutScoreboardTeam_of;
|
||||
public Method PacketPlayOutScoreboardTeam_ofBoolean;
|
||||
public Method PacketPlayOutScoreboardTeam_ofString;
|
||||
|
||||
/** Other */
|
||||
public Object emptyScoreboard;
|
||||
public Object dummyEntity;
|
||||
|
||||
/**
|
||||
* Constructs new instance and attempts to load all fields, methods and constructors.
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some field, constructor or method was not found
|
||||
*/
|
||||
public NMSStorage() throws ReflectiveOperationException {
|
||||
try {
|
||||
Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
|
||||
is1_19_3Plus = true;
|
||||
} catch (ClassNotFoundException | NullPointerException ignored) {}
|
||||
ProtocolVersion.UNKNOWN_SERVER_VERSION.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
|
||||
loadClasses();
|
||||
if (minorVersion >= 7) {
|
||||
NETWORK_MANAGER = getFields(PlayerConnection, NetworkManager).get(0);
|
||||
}
|
||||
if (minorVersion >= 8) {
|
||||
CHANNEL = getFields(NetworkManager, Channel.class).get(0);
|
||||
getProfile = getMethods(EntityHuman, GameProfile.class).get(0);
|
||||
Constructor<?> newEntityArmorStand = EntityArmorStand.getConstructor(World, double.class, double.class, double.class);
|
||||
Method World_getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".CraftWorld").getMethod("getHandle");
|
||||
dummyEntity = newEntityArmorStand.newInstance(World_getHandle.invoke(Bukkit.getWorlds().get(0)), 0, 0, 0);
|
||||
}
|
||||
PLAYER_CONNECTION = getFields(EntityPlayer, PlayerConnection).get(0);
|
||||
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
|
||||
sendPacket = getMethods(PlayerConnection, void.class, Packet).get(0);
|
||||
registry = new DataWatcherRegistry(this);
|
||||
dataWatcher();
|
||||
entityTeleport();
|
||||
spawnEntityLiving();
|
||||
chat();
|
||||
playerListHeaderFooter();
|
||||
playerInfo();
|
||||
otherEntity();
|
||||
scoreboard();
|
||||
scoreboardTeam();
|
||||
loadNamedFieldsAndMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PacketPlayOutChat's methods, fields and constructors
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void chat() throws ReflectiveOperationException {
|
||||
if (minorVersion >= 19) {
|
||||
try {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, boolean.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
//1.19.0
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, int.class);
|
||||
}
|
||||
} else if (minorVersion >= 16) {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType, UUID.class);
|
||||
} else if (minorVersion >= 12) {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType);
|
||||
} else if (minorVersion >= 8) {
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, byte.class);
|
||||
} else if (minorVersion >= 7){
|
||||
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PacketPlayOutPlayerInfo's methods, fields and constructors
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void playerInfo() throws ReflectiveOperationException {
|
||||
if (minorVersion < 8) return;
|
||||
if (is1_19_3Plus()) {
|
||||
newClientboundPlayerInfoRemovePacket = ClientboundPlayerInfoRemovePacket.getConstructor(List.class);
|
||||
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumSet.class, Collection.class);
|
||||
ClientboundPlayerInfoRemovePacket_getEntries = getMethods(ClientboundPlayerInfoRemovePacket, List.class).get(0);
|
||||
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumSet.class).get(0);
|
||||
newRemoteChatSession$Data = RemoteChatSession$Data.getConstructor(UUID.class, ProfilePublicKey$a);
|
||||
RemoteChatSession$Data_getSessionId = getMethods(RemoteChatSession$Data, UUID.class).get(0);
|
||||
RemoteChatSession$Data_getProfilePublicKey = getMethods(RemoteChatSession$Data, ProfilePublicKey$a).get(0);
|
||||
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, RemoteChatSession$Data).get(0);
|
||||
EntityPlayer_RemoteChatSession = getFields(EntityPlayer, RemoteChatSession).get(0);
|
||||
PlayerInfoData_isListed = getMethods(PlayerInfoData, boolean.class).get(0);
|
||||
} else {
|
||||
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
|
||||
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
|
||||
if (minorVersion >= 19) {
|
||||
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, ProfilePublicKey$a).get(0);
|
||||
EntityHuman_ProfilePublicKey = getFields(EntityHuman, ProfilePublicKey).get(0);
|
||||
}
|
||||
}
|
||||
newPlayerInfoData = PlayerInfoData.getConstructors()[0];
|
||||
PacketPlayOutPlayerInfo_PLAYERS = getFields(PacketPlayOutPlayerInfo, List.class).get(0);
|
||||
PlayerInfoData_getProfile = getMethods(PlayerInfoData, GameProfile.class).get(0);
|
||||
for (Method m: getMethods(PlayerInfoData, int.class)) {
|
||||
// do not take .hashCode(), which is final
|
||||
if (!Modifier.isFinal(m.getModifiers())) PlayerInfoData_getLatency = m;
|
||||
}
|
||||
PlayerInfoData_getGamemode = getMethods(PlayerInfoData, EnumGamemode).get(0);
|
||||
PlayerInfoData_getDisplayName = getMethods(PlayerInfoData, IChatBaseComponent).get(0);
|
||||
if (minorVersion >= 19) {
|
||||
ProfilePublicKey_getRecord = getMethods(ProfilePublicKey, ProfilePublicKey$a).get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PacketPlayOutSpawnEntityLiving's methods, fields and constructors
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void spawnEntityLiving() throws ReflectiveOperationException {
|
||||
if (minorVersion >= 17) {
|
||||
if (is1_19_3Plus()) {
|
||||
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(Entity);
|
||||
} else {
|
||||
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
|
||||
}
|
||||
} else {
|
||||
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor();
|
||||
}
|
||||
PacketPlayOutSpawnEntityLiving_ENTITYID = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(0);
|
||||
PacketPlayOutSpawnEntityLiving_YAW = getFields(PacketPlayOutSpawnEntityLiving, byte.class).get(0);
|
||||
PacketPlayOutSpawnEntityLiving_PITCH = getFields(PacketPlayOutSpawnEntityLiving, byte.class).get(0);
|
||||
if (minorVersion >= 9) {
|
||||
PacketPlayOutSpawnEntityLiving_UUID = getFields(PacketPlayOutSpawnEntityLiving, UUID.class).get(0);
|
||||
if (minorVersion >= 19) {
|
||||
PacketPlayOutSpawnEntityLiving_X = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(2);
|
||||
PacketPlayOutSpawnEntityLiving_Y = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(3);
|
||||
PacketPlayOutSpawnEntityLiving_Z = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(4);
|
||||
} else {
|
||||
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 {
|
||||
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 < 19) {
|
||||
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(1);
|
||||
}
|
||||
if (minorVersion <= 14) {
|
||||
PacketPlayOutSpawnEntityLiving_DATAWATCHER = getFields(PacketPlayOutSpawnEntityLiving, DataWatcher).get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PacketPlayOutEntityTeleport's methods, fields and constructors
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void entityTeleport() throws ReflectiveOperationException {
|
||||
PacketPlayOutEntityTeleport_ENTITYID = getFields(PacketPlayOutEntityTeleport, int.class).get(0);
|
||||
PacketPlayOutEntityTeleport_YAW = getFields(PacketPlayOutEntityTeleport, byte.class).get(0);
|
||||
PacketPlayOutEntityTeleport_PITCH = getFields(PacketPlayOutEntityTeleport, byte.class).get(1);
|
||||
if (minorVersion >= 17) {
|
||||
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor(Entity);
|
||||
} else {
|
||||
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor();
|
||||
}
|
||||
if (minorVersion >= 9) {
|
||||
PacketPlayOutEntityTeleport_X = getFields(PacketPlayOutEntityTeleport, double.class).get(0);
|
||||
PacketPlayOutEntityTeleport_Y = getFields(PacketPlayOutEntityTeleport, double.class).get(1);
|
||||
PacketPlayOutEntityTeleport_Z = getFields(PacketPlayOutEntityTeleport, double.class).get(2);
|
||||
} else {
|
||||
PacketPlayOutEntityTeleport_X = getFields(PacketPlayOutEntityTeleport, int.class).get(1);
|
||||
PacketPlayOutEntityTeleport_Y = getFields(PacketPlayOutEntityTeleport, int.class).get(2);
|
||||
PacketPlayOutEntityTeleport_Z = getFields(PacketPlayOutEntityTeleport, int.class).get(3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PacketPlayOutPlayerListHeaderFooter's methods, fields and constructors
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void playerListHeaderFooter() throws ReflectiveOperationException {
|
||||
if (minorVersion < 8) return;
|
||||
PacketPlayOutPlayerListHeaderFooter_HEADER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(0);
|
||||
PacketPlayOutPlayerListHeaderFooter_FOOTER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(1);
|
||||
if (minorVersion >= 17) {
|
||||
newPacketPlayOutPlayerListHeaderFooter = PacketPlayOutPlayerListHeaderFooter.getConstructor(IChatBaseComponent, IChatBaseComponent);
|
||||
} else {
|
||||
newPacketPlayOutPlayerListHeaderFooter = PacketPlayOutPlayerListHeaderFooter.getConstructor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads methods, fields and constructors for other entity packets
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void otherEntity() throws ReflectiveOperationException {
|
||||
PacketPlayOutEntity_ENTITYID = getFields(PacketPlayOutEntity, int.class).get(0);
|
||||
PacketPlayOutEntityDestroy_ENTITIES = setAccessible(PacketPlayOutEntityDestroy.getDeclaredFields()[0]);
|
||||
if (is1_19_3Plus()) {
|
||||
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, List.class);
|
||||
} else {
|
||||
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class);
|
||||
}
|
||||
PacketPlayOutEntityMetadata_LIST = getFields(PacketPlayOutEntityMetadata, List.class).get(0);
|
||||
PacketPlayOutNamedEntitySpawn_ENTITYID = getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
|
||||
if (minorVersion >= 7) {
|
||||
PacketPlayInUseEntity_ENTITY = getFields(PacketPlayInUseEntity, int.class).get(0);
|
||||
PacketPlayInUseEntity_ACTION = getFields(PacketPlayInUseEntity, EnumEntityUseAction).get(0);
|
||||
}
|
||||
try {
|
||||
newPacketPlayOutEntityDestroy = PacketPlayOutEntityDestroy.getConstructor(int[].class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
//1.17.0
|
||||
newPacketPlayOutEntityDestroy = PacketPlayOutEntityDestroy.getConstructor(int.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads DataWatcher's methods, fields and constructors
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void dataWatcher() throws ReflectiveOperationException {
|
||||
newDataWatcher = DataWatcher.getConstructors()[0];
|
||||
DataWatcherItem_VALUE = getFields(DataWatcherItem, Object.class).get(0);
|
||||
if (minorVersion >= 9) {
|
||||
newDataWatcherObject = DataWatcherObject.getConstructor(int.class, DataWatcherSerializer);
|
||||
DataWatcherItem_TYPE = getFields(DataWatcherItem, DataWatcherObject).get(0);
|
||||
DataWatcherObject_SLOT = getFields(DataWatcherObject, int.class).get(0);
|
||||
DataWatcherObject_SERIALIZER = getFields(DataWatcherObject, DataWatcherSerializer).get(0);
|
||||
if (is1_19_3Plus()) {
|
||||
DataWatcher$DataValue_POSITION = getFields(DataWatcher$DataValue, int.class).get(0);
|
||||
DataWatcher$DataValue_VALUE = getFields(DataWatcher$DataValue, Object.class).get(0);
|
||||
DataWatcher_markDirty = getMethods(DataWatcher, void.class, DataWatcherObject).get(0);
|
||||
}
|
||||
} else {
|
||||
DataWatcherItem_TYPE = getFields(DataWatcherItem, int.class).get(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Scoreboard methods, fields and constructors
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void scoreboard() throws ReflectiveOperationException {
|
||||
newScoreboardObjective = ScoreboardObjective.getConstructors()[0];
|
||||
newScoreboard = Scoreboard.getConstructor();
|
||||
emptyScoreboard = newScoreboard.newInstance();
|
||||
newScoreboardScore = ScoreboardScore.getConstructor(Scoreboard, ScoreboardObjective, String.class);
|
||||
newPacketPlayOutScoreboardDisplayObjective = PacketPlayOutScoreboardDisplayObjective.getConstructor(int.class, ScoreboardObjective);
|
||||
PacketPlayOutScoreboardDisplayObjective_POSITION = getFields(PacketPlayOutScoreboardDisplayObjective, int.class).get(0);
|
||||
PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME = getFields(PacketPlayOutScoreboardDisplayObjective, String.class).get(0);
|
||||
PacketPlayOutScoreboardObjective_OBJECTIVENAME = getFields(PacketPlayOutScoreboardObjective, String.class).get(0);
|
||||
IScoreboardCriteria_self = getFields(IScoreboardCriteria, IScoreboardCriteria).get(0);
|
||||
List<Field> list = getFields(PacketPlayOutScoreboardObjective, int.class);
|
||||
PacketPlayOutScoreboardObjective_METHOD = list.get(list.size()-1);
|
||||
if (minorVersion >= 8) {
|
||||
PacketPlayOutScoreboardObjective_RENDERTYPE = getFields(PacketPlayOutScoreboardObjective, EnumScoreboardHealthDisplay).get(0);
|
||||
}
|
||||
if (minorVersion >= 13) {
|
||||
newPacketPlayOutScoreboardObjective = PacketPlayOutScoreboardObjective.getConstructor(ScoreboardObjective, int.class);
|
||||
newPacketPlayOutScoreboardScore_1_13 = PacketPlayOutScoreboardScore.getConstructor(EnumScoreboardAction, String.class, String.class, int.class);
|
||||
PacketPlayOutScoreboardObjective_DISPLAYNAME = getFields(PacketPlayOutScoreboardObjective, IChatBaseComponent).get(0);
|
||||
} else {
|
||||
newPacketPlayOutScoreboardObjective = PacketPlayOutScoreboardObjective.getConstructor();
|
||||
newPacketPlayOutScoreboardScore_String = PacketPlayOutScoreboardScore.getConstructor(String.class);
|
||||
PacketPlayOutScoreboardObjective_DISPLAYNAME = getFields(PacketPlayOutScoreboardObjective, String.class).get(1);
|
||||
if (minorVersion >= 8) {
|
||||
newPacketPlayOutScoreboardScore = PacketPlayOutScoreboardScore.getConstructor(ScoreboardScore);
|
||||
} else {
|
||||
newPacketPlayOutScoreboardScore = PacketPlayOutScoreboardScore.getConstructor(ScoreboardScore, int.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PacketPlayOutScoreboardTeam's methods, fields and constructors
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
protected void scoreboardTeam() throws ReflectiveOperationException {
|
||||
newScoreboardTeam = ScoreboardTeam.getConstructor(Scoreboard, String.class);
|
||||
PacketPlayOutScoreboardTeam_NAME = getFields(PacketPlayOutScoreboardTeam, String.class).get(0);
|
||||
PacketPlayOutScoreboardTeam_ACTION = getInstanceFields(PacketPlayOutScoreboardTeam, int.class).get(0);
|
||||
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
|
||||
ScoreboardTeam_getPlayerNameSet = getMethods(ScoreboardTeam, Collection.class).get(0);
|
||||
if (minorVersion >= 9) {
|
||||
ScoreboardTeam_setCollisionRule = getMethods(ScoreboardTeam, void.class, EnumTeamPush).get(0);
|
||||
}
|
||||
if (minorVersion >= 13) {
|
||||
ScoreboardTeam_setColor = getMethods(ScoreboardTeam, void.class, EnumChatFormat).get(0);
|
||||
}
|
||||
if (minorVersion >= 17) {
|
||||
PacketPlayOutScoreboardTeam_of = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam).get(0);
|
||||
PacketPlayOutScoreboardTeam_ofBoolean = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam, boolean.class).get(0);
|
||||
PacketPlayOutScoreboardTeam_ofString = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam, String.class, PacketPlayOutScoreboardTeam_PlayerAction).get(0);
|
||||
} else {
|
||||
newPacketPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.getConstructor(ScoreboardTeam, int.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all classes used by the plugin
|
||||
*
|
||||
* @throws ClassNotFoundException
|
||||
* If a class was not found
|
||||
*/
|
||||
public abstract void loadClasses() throws ClassNotFoundException;
|
||||
|
||||
/**
|
||||
* Loads all fields and methods which must be loaded by their name due to
|
||||
* lack of other methods of reliably retrieving them.
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If a field or method was not found
|
||||
*/
|
||||
public abstract void loadNamedFieldsAndMethods() throws ReflectiveOperationException;
|
||||
|
||||
/**
|
||||
* Returns method with specified possible names and parameters. Throws exception if no such method was found
|
||||
*
|
||||
* @param clazz
|
||||
* lass 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
|
||||
*/
|
||||
protected Method getMethod(Class<?> clazz, String[] names, Class<?>... parameterTypes) throws NoSuchMethodException {
|
||||
for (String name : names) {
|
||||
try {
|
||||
return clazz.getMethod(name, parameterTypes);
|
||||
} catch (NoSuchMethodException e) {
|
||||
//not the first method in array
|
||||
}
|
||||
}
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Method m : clazz.getMethods()) {
|
||||
if (m.getParameterCount() != parameterTypes.length) continue;
|
||||
Class<?>[] types = m.getParameterTypes();
|
||||
boolean valid = true;
|
||||
for (int i=0; i<types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) list.add(m.getName());
|
||||
}
|
||||
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(names) + " with parameters " +
|
||||
Arrays.toString(parameterTypes) + " in class " + clazz.getName() + ". Methods with matching parameters: " + list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all methods from class which return specified class type and have specified parameter types.
|
||||
*
|
||||
* @param clazz
|
||||
* Class to get methods from
|
||||
* @param returnType
|
||||
* Return type of methods
|
||||
* @param parameterTypes
|
||||
* Parameter types of methods
|
||||
* @return List of found methods matching requirements. If nothing is found, empty list is returned.
|
||||
*/
|
||||
protected List<Method> getMethods(Class<?> clazz, Class<?> returnType, Class<?>... parameterTypes) {
|
||||
List<Method> list = new ArrayList<>();
|
||||
for (Method m : clazz.getDeclaredMethods()) {
|
||||
if (m.getReturnType() != returnType || m.getParameterCount() != parameterTypes.length || !Modifier.isPublic(m.getModifiers())) continue;
|
||||
Class<?>[] types = m.getParameterTypes();
|
||||
boolean valid = true;
|
||||
for (int i=0; i<types.length; i++) {
|
||||
if (types[i] != parameterTypes[i]) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) list.add(m);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
protected List<Field> getFields(Class<?> clazz, Class<?> type) {
|
||||
List<Field> list = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type) {
|
||||
list.add(setAccessible(field));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all instance 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> getInstanceFields(Class<?> clazz, Class<?> type) {
|
||||
List<Field> list = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) {
|
||||
list.add(setAccessible(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
|
||||
*/
|
||||
protected 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))) {
|
||||
return setAccessible(f);
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException("Field \"" + name + "\" was not found in class " + clazz.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server's minor version
|
||||
* @return server's minor version
|
||||
*/
|
||||
public int getMinorVersion() {
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value of a field in object
|
||||
*
|
||||
* @param obj
|
||||
* Instance of object where field should be set
|
||||
* @param field
|
||||
* Field to set value of
|
||||
* @param value
|
||||
* Value to set field to
|
||||
* @throws IllegalAccessException
|
||||
* If thrown by reflective operation
|
||||
*/
|
||||
public void setField(Object obj, Field field, Object value) throws IllegalAccessException {
|
||||
field.set(obj, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data watcher registry
|
||||
* @return data watcher registry
|
||||
*/
|
||||
public DataWatcherRegistry getDataWatcherRegistry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes object accessible and returns it for chaining.
|
||||
*
|
||||
* @param o
|
||||
* Object to make accessible
|
||||
* @return Entered object, for chaining
|
||||
*/
|
||||
public <T extends AccessibleObject> T setAccessible(T o) {
|
||||
o.setAccessible(true);
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets values of all static fields in a class
|
||||
*
|
||||
* @param clazz
|
||||
* class to return field values from
|
||||
* @return map of values
|
||||
*/
|
||||
public Map<String, Object> getStaticFields(Class<?> clazz){
|
||||
Map<String, Object> fields = new HashMap<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
setAccessible(field);
|
||||
try {
|
||||
fields.put(field.getName(), field.get(null));
|
||||
} catch (IllegalAccessException e) {
|
||||
//this will never happen
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
public boolean is1_19_3Plus() {
|
||||
return is1_19_3Plus;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.storage;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.Main;
|
||||
|
||||
/**
|
||||
* NMS loader for Thermos 1.7.10.
|
||||
*/
|
||||
public class ThermosNMSStorage extends BukkitLegacyNMSStorage {
|
||||
|
||||
/**
|
||||
* Creates new instance, initializes required NMS classes and fields
|
||||
*
|
||||
* @throws ReflectiveOperationException
|
||||
* If any class, field or method fails to load
|
||||
*/
|
||||
public ThermosNMSStorage() throws ReflectiveOperationException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class from given name
|
||||
*
|
||||
* @param name
|
||||
* class name
|
||||
* @return class from given name
|
||||
* @throws ClassNotFoundException
|
||||
* if class was not found
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getLegacyClass(String name) throws ClassNotFoundException {
|
||||
try {
|
||||
return Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
|
||||
} catch (NullPointerException e) {
|
||||
// nested class not found
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
|
||||
PING = getField(EntityPlayer, "field_71138_i");
|
||||
ScoreboardScore_setScore = ScoreboardScore.getMethod("func_96647_c", int.class);
|
||||
ScoreboardTeam_setAllowFriendlyFire = ScoreboardTeam.getMethod("func_96660_a", boolean.class);
|
||||
ScoreboardTeam_setCanSeeFriendlyInvisibles = ScoreboardTeam.getMethod("func_98300_b", boolean.class);
|
||||
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("func_150699_a", String.class);
|
||||
DataWatcher_REGISTER = DataWatcher.getMethod("func_75682_a", int.class, Object.class);
|
||||
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("func_96666_b", String.class);
|
||||
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("func_96662_c", String.class);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit.permission;
|
||||
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Header-&-Footer
|
||||
header-footer:
|
||||
enabled: true
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
header:
|
||||
- "&3&lServer name"
|
||||
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
|
||||
- "&7Online players: &f%online%"
|
||||
- "&6Online staff: &e%staffonline%"
|
||||
- ""
|
||||
- "%animation:MyAnimation1%"
|
||||
footer:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "%animation:time%"
|
||||
- "%animation:ping-tps%"
|
||||
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
|
||||
- ""
|
||||
- "&7Visit our webpage %animation:web%"
|
||||
per-world:
|
||||
world1:
|
||||
header:
|
||||
- "an example of world with custom"
|
||||
footer:
|
||||
- "header/footer and prefix/suffix"
|
||||
world2;world3:
|
||||
header:
|
||||
- "This is a shared header for"
|
||||
- "world2 and world3"
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting
|
||||
tablist-name-formatting:
|
||||
enabled: true
|
||||
align-tabsuffix-on-the-right: false
|
||||
character-width-overrides: {}
|
||||
anti-override: true
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Nametags
|
||||
scoreboard-teams:
|
||||
enabled: true
|
||||
enable-collision: true
|
||||
invisible-nametags: false
|
||||
anti-override: true
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Sorting-players-in-tablist
|
||||
sorting-types:
|
||||
- "GROUPS:owner,admin,mod,helper,builder,vip,default"
|
||||
- "PLACEHOLDER_A_TO_Z:%player%"
|
||||
case-sensitive-sorting: true
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Unlimited-nametag-mode
|
||||
unlimited-nametag-mode:
|
||||
enabled: false
|
||||
use-marker-tag-for-1-8-x-clients: false
|
||||
disable-on-boats: true
|
||||
space-between-lines: 0.22
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
dynamic-lines:
|
||||
- abovename
|
||||
- nametag #the original one, combination of tagprefix + customtagname + tagsuffix
|
||||
- belowname
|
||||
- another
|
||||
static-lines:
|
||||
myCustomLine: 0.66
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Yellow-number
|
||||
yellow-number-in-tablist:
|
||||
enabled: true
|
||||
value: "%ping%"
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Belowname
|
||||
belowname-objective:
|
||||
enabled: false
|
||||
number: "%health%"
|
||||
text: "&cHealth"
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix
|
||||
prevent-spectator-effect:
|
||||
enabled: false
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar
|
||||
bossbar:
|
||||
enabled: false
|
||||
toggle-command: /bossbar
|
||||
remember-toggle-choice: false
|
||||
hidden-by-default: false
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
bars:
|
||||
ServerInfo:
|
||||
style: "PROGRESS" # for 1.9+: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20
|
||||
color: "%animation:barcolors%" # for 1.9+: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
|
||||
progress: "100" # in %
|
||||
text: "&fWebsite: &bwww.domain.com"
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Per-world-playerlist
|
||||
per-world-playerlist:
|
||||
enabled: false
|
||||
# players with tab.staff will always see all players
|
||||
allow-bypass-permission: false
|
||||
# players in these worlds will always see all players
|
||||
ignore-effect-in-worlds:
|
||||
- ignoredworld
|
||||
- build
|
||||
shared-playerlist-world-groups:
|
||||
lobby:
|
||||
- lobby1
|
||||
- lobby2
|
||||
minigames:
|
||||
- paintball
|
||||
- bedwars
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard
|
||||
scoreboard:
|
||||
enabled: false
|
||||
toggle-command: /sb
|
||||
remember-toggle-choice: false
|
||||
hidden-by-default: false
|
||||
use-numbers: false
|
||||
static-number: 0
|
||||
delay-on-join-milliseconds: 0
|
||||
respect-other-plugins: true
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
scoreboards:
|
||||
admin:
|
||||
# Only players with tab.scoreboard.admin can see this scoreboard, others will see scoreboard1
|
||||
display-condition: "permission:tab.scoreboard.admin"
|
||||
title: "Admin scoreboard"
|
||||
lines:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "&6Online:"
|
||||
- "* &eOnline&7: &f%online%"
|
||||
- "* &eCurrent World&7: &f%worldonline%"
|
||||
- "* &eStaff&7: &f%staffonline%"
|
||||
- " "
|
||||
- "&6Server Info:"
|
||||
- "* &bTPS&7: %tps%"
|
||||
- "* &bUptime&7: &f%server_uptime%"
|
||||
- "* &bMemory&7: &f%memory-used%&7/&4%memory-max%"
|
||||
- "%animation:MyAnimation1%"
|
||||
scoreboard1:
|
||||
title: "Default"
|
||||
lines:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "&6My Stats:"
|
||||
- "* &eKills&7: &f%statistic_player_kills%"
|
||||
- "* &eDeaths&7: &f%statistic_deaths%"
|
||||
- "* &eHealth&7: &f%health%"
|
||||
- " "
|
||||
- "&6Personal Info:"
|
||||
- "* &bRank&7: &f%group%"
|
||||
- "* &bPing&7: &f%ping%&7ms"
|
||||
- "* &bWorld&7: &f%world%"
|
||||
- "%animation:MyAnimation1%"
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Ping-Spoof
|
||||
ping-spoof:
|
||||
enabled: false
|
||||
value: 0
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Pet-name-fix
|
||||
fix-pet-names:
|
||||
enabled: false
|
||||
|
||||
placeholders:
|
||||
date-format: "dd.MM.yyyy"
|
||||
time-format: "[HH:mm:ss / h:mm a]"
|
||||
time-offset: 0
|
||||
register-tab-expansion: false
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements
|
||||
placeholder-output-replacements:
|
||||
"%essentials_vanished%":
|
||||
"yes": "&7| Vanished"
|
||||
"no": ""
|
||||
"%afk%":
|
||||
true: " &o[AFK]&r"
|
||||
false: ""
|
||||
"%essentials_nickname%":
|
||||
"%essentials_nickname%": "%player%" #placeholder did not parse correctly, show real name instead
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders
|
||||
conditions:
|
||||
nick: # use it with %condition:nick%
|
||||
conditions:
|
||||
- "%player%=%essentials_nickname%"
|
||||
yes: "%player%"
|
||||
no: "~%essentials_nickname%"
|
||||
|
||||
placeholderapi-refresh-intervals:
|
||||
default-refresh-interval: 500
|
||||
server:
|
||||
"%server_uptime%": 1000
|
||||
"%server_tps_1_colored%": 1000
|
||||
"%server_unique_joins%": 5000
|
||||
player:
|
||||
"%player_health%": 200
|
||||
"%player_ping%": 1000
|
||||
"%vault_prefix%": 1000
|
||||
relational:
|
||||
"%rel_factionsuuid_relation_color%": 1000
|
||||
|
||||
# assigning groups by permission nodes instead of taking them from permission plugin
|
||||
assign-groups-by-permissions: false
|
||||
|
||||
# if the option above is true, all groups are taken based on permissions and the one higher in this list is used as primary
|
||||
# Warning! This is not sorting list and has nothing to do with sorting players in tablist!
|
||||
primary-group-finding-list:
|
||||
- Owner
|
||||
- Admin
|
||||
- Mod
|
||||
- Helper
|
||||
- default
|
||||
|
||||
debug: false
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/MySQL
|
||||
mysql:
|
||||
enabled: false
|
||||
host: 127.0.0.1
|
||||
port: 3306
|
||||
database: tab
|
||||
username: user
|
||||
password: password
|
||||
+28
-22
@@ -1,8 +1,7 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.proxy.ProxyPlatform;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.ChatEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
@@ -16,6 +15,19 @@ import net.md_5.bungee.event.EventHandler;
|
||||
*/
|
||||
public class BungeeEventListener implements Listener {
|
||||
|
||||
/** Reference to BungeeCord platform implementation */
|
||||
private final BungeePlatform platform;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param platform
|
||||
* BungeeCord implementation of platform
|
||||
*/
|
||||
public BungeeEventListener(BungeePlatform platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect event listener to forward the event to all features
|
||||
*
|
||||
@@ -24,9 +36,9 @@ public class BungeeEventListener implements Listener {
|
||||
*/
|
||||
@EventHandler
|
||||
public void onQuit(PlayerDisconnectEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask(() ->
|
||||
TAB.getInstance().getFeatureManager().onQuit(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
if (TabAPI.getInstance().isPluginDisabled()) return;
|
||||
TabAPI.getInstance().getThreadManager().runTask(() ->
|
||||
TabAPI.getInstance().getFeatureManager().onQuit(TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,12 +49,12 @@ public class BungeeEventListener implements Listener {
|
||||
*/
|
||||
@EventHandler
|
||||
public void onSwitch(ServerSwitchEvent e){
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
TAB.getInstance().getCPUManager().runTask(() -> {
|
||||
if (TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()) == null) {
|
||||
TAB.getInstance().getFeatureManager().onJoin(new BungeeTabPlayer(e.getPlayer()));
|
||||
if (TabAPI.getInstance().isPluginDisabled()) return;
|
||||
TabAPI.getInstance().getThreadManager().runTask(() -> {
|
||||
if (TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId()) == null) {
|
||||
TabAPI.getInstance().getFeatureManager().onJoin(new BungeeTabPlayer(e.getPlayer()));
|
||||
} else {
|
||||
TAB.getInstance().getFeatureManager().onServerChange(e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
|
||||
TabAPI.getInstance().getFeatureManager().onServerChange(e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -55,8 +67,9 @@ public class BungeeEventListener implements Listener {
|
||||
*/
|
||||
@EventHandler
|
||||
public void onChat(ChatEvent e) {
|
||||
if (TAB.getInstance().isDisabled()) return;
|
||||
if (e.isCommand() && TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
if (TabAPI.getInstance().isPluginDisabled()) return;
|
||||
if (e.isCommand() && TabAPI.getInstance().getFeatureManager().onCommand(TabAPI.getInstance().getPlayer(
|
||||
((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,15 +83,8 @@ public class BungeeEventListener implements Listener {
|
||||
if (!event.getTag().equals(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME)) return;
|
||||
if (event.getReceiver() instanceof ProxiedPlayer) {
|
||||
event.setCancelled(true);
|
||||
TAB.getInstance().getCPUManager().getThreadPool().submit(() -> {
|
||||
try {
|
||||
while (TAB.getInstance().getPlayer(((ProxiedPlayer) event.getReceiver()).getUniqueId()) == null) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler().onPluginMessage(
|
||||
((ProxiedPlayer) event.getReceiver()).getUniqueId(), event.getData());
|
||||
} catch (InterruptedException ignored) {}
|
||||
});
|
||||
platform.getPluginMessageHandler().onPluginMessage(
|
||||
((ProxiedPlayer) event.getReceiver()).getUniqueId(), event.getData());
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
-21
@@ -1,7 +1,7 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
@@ -10,16 +10,11 @@ import me.neznamy.tab.api.protocol.*;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import 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.PlayerPublicKey;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import net.md_5.bungee.protocol.packet.*;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Packet builder for BungeeCord platform
|
||||
@@ -40,11 +35,46 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) {
|
||||
return new Chat(packet.getMessage().toString(clientVersion), (byte) packet.getType().ordinal());
|
||||
if (clientVersion.getMinorVersion() >= 19) {
|
||||
return new SystemChat(packet.getMessage().toString(clientVersion), (byte) packet.getType().ordinal());
|
||||
} else {
|
||||
return new Chat(packet.getMessage().toString(clientVersion), (byte) packet.getType().ordinal());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) {
|
||||
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
if (packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
|
||||
PlayerListItemRemove remove = new PlayerListItemRemove();
|
||||
remove.setUuids(packet.getEntries().stream().map(PlayerInfoData::getUniqueId).toArray(UUID[]::new));
|
||||
return remove;
|
||||
}
|
||||
List<Item> items = new ArrayList<>();
|
||||
for (PlayerInfoData data : packet.getEntries()) {
|
||||
Item item = new Item();
|
||||
if (data.getDisplayName() != null) item.setDisplayName(data.getDisplayName().toString(clientVersion));
|
||||
if (data.getGameMode() != null) item.setGamemode(data.getGameMode().ordinal()-1);
|
||||
item.setListed(data.isListed());
|
||||
item.setPing(data.getLatency());
|
||||
if (data.getSkin() != null) {
|
||||
item.setProperties(new Property[]{new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature())});
|
||||
} else {
|
||||
item.setProperties(new Property[0]);
|
||||
}
|
||||
item.setUsername(data.getName());
|
||||
item.setUuid(data.getUniqueId());
|
||||
item.setChatSessionId(data.getChatSessionId());
|
||||
item.setPublicKey((PlayerPublicKey) data.getProfilePublicKey());
|
||||
items.add(item);
|
||||
}
|
||||
PlayerListItemUpdate bungeePacket = new PlayerListItemUpdate();
|
||||
PlayerListItemUpdate.Action[] array = packet.getActions().stream().map(action -> PlayerListItemUpdate.Action.valueOf(
|
||||
action.toString().replace("GAME_MODE", "GAMEMODE"))).toArray(PlayerListItemUpdate.Action[]::new);
|
||||
bungeePacket.setActions(EnumSet.of(array[0], array));
|
||||
bungeePacket.setItems(items.toArray(new Item[0]));
|
||||
return bungeePacket;
|
||||
}
|
||||
List<Item> items = new ArrayList<>();
|
||||
for (PlayerInfoData data : packet.getEntries()) {
|
||||
Item item = new Item();
|
||||
@@ -55,21 +85,24 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
item.setDisplayName(data.getDisplayName().toLegacyText());
|
||||
}
|
||||
} else if (clientVersion.getMinorVersion() < 8) {
|
||||
item.setDisplayName(data.getName()); //avoiding NPE, 1.7 client requires this, 1.8 added a leading boolean
|
||||
item.setDisplayName(String.valueOf(data.getName())); //avoiding NPE, 1.7 client requires this, 1.8 added a leading boolean
|
||||
}
|
||||
if (data.getGameMode() != null) item.setGamemode(data.getGameMode().ordinal()-1);
|
||||
item.setPing(data.getLatency());
|
||||
if (data.getSkin() != null) {
|
||||
item.setProperties(new String[][]{{"textures", data.getSkin().getValue(), data.getSkin().getSignature()}});
|
||||
item.setProperties(new Property[]{new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature())});
|
||||
} else {
|
||||
item.setProperties(new String[0][0]);
|
||||
item.setProperties(new Property[0]);
|
||||
}
|
||||
item.setUsername(data.getName());
|
||||
item.setUuid(data.getUniqueId());
|
||||
item.setPublicKey((PlayerPublicKey) data.getProfilePublicKey());
|
||||
items.add(item);
|
||||
}
|
||||
PlayerListItem bungeePacket = new PlayerListItem();
|
||||
bungeePacket.setAction(PlayerListItem.Action.valueOf(packet.getAction().toString().replace("GAME_MODE", "GAMEMODE")));
|
||||
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
|
||||
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
|
||||
bungeePacket.setAction(PlayerListItem.Action.valueOf(action.toString().replace("GAME_MODE", "GAMEMODE")));
|
||||
bungeePacket.setItems(items.toArray(new Item[0]));
|
||||
return bungeePacket;
|
||||
}
|
||||
@@ -106,13 +139,50 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
|
||||
@Override
|
||||
public PacketPlayOutPlayerInfo readPlayerInfo(Object bungeePacket, ProtocolVersion clientVersion) {
|
||||
PlayerListItem item = (PlayerListItem) bungeePacket;
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Item i : item.getItems()) {
|
||||
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0][1], i.getProperties()[0][2]);
|
||||
listData.add(new PlayerInfoData(i.getUsername(), i.getUuid(), skin, i.getPing(), EnumGamemode.VALUES[i.getGamemode()+1], IChatBaseComponent.deserialize(i.getDisplayName())));
|
||||
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
if (bungeePacket instanceof PlayerListItemRemove) {
|
||||
return new PacketPlayOutPlayerInfo(
|
||||
EnumPlayerInfoAction.REMOVE_PLAYER,
|
||||
Arrays.stream(((PlayerListItemRemove) bungeePacket).getUuids()).map(PlayerInfoData::new).collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
PlayerListItemUpdate item = (PlayerListItemUpdate) bungeePacket;
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Item i : item.getItems()) {
|
||||
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
|
||||
listData.add(new PlayerInfoData(
|
||||
i.getUsername(),
|
||||
i.getUuid(),
|
||||
skin,
|
||||
Boolean.TRUE.equals(i.getListed()),
|
||||
i.getPing() == null ? 0 : i.getPing(),
|
||||
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
|
||||
IChatBaseComponent.deserialize(i.getDisplayName()),
|
||||
i.getChatSessionId(),
|
||||
i.getPublicKey()));
|
||||
}
|
||||
EnumPlayerInfoAction[] array = item.getActions().stream().map(action ->
|
||||
EnumPlayerInfoAction.valueOf(action.toString().replace("GAMEMODE", "GAME_MODE"))).toArray(EnumPlayerInfoAction[]::new);
|
||||
EnumSet<EnumPlayerInfoAction> actions = EnumSet.of(array[0], array);
|
||||
return new PacketPlayOutPlayerInfo(actions, listData);
|
||||
} else {
|
||||
PlayerListItem item = (PlayerListItem) bungeePacket;
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Item i : item.getItems()) {
|
||||
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
|
||||
listData.add(new PlayerInfoData(
|
||||
i.getUsername(),
|
||||
i.getUuid(),
|
||||
skin,
|
||||
true,
|
||||
i.getPing() == null ? 0 : i.getPing(),
|
||||
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
|
||||
IChatBaseComponent.deserialize(i.getDisplayName()),
|
||||
null,
|
||||
i.getPublicKey()));
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+8
-4
@@ -8,9 +8,10 @@ import io.netty.channel.ChannelPromise;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.redis.RedisPlayer;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
|
||||
@@ -68,6 +69,8 @@ public class BungeePipelineInjector extends PipelineInjector {
|
||||
try {
|
||||
switch(packet.getClass().getSimpleName()) {
|
||||
case "PlayerListItem":
|
||||
case "PlayerListItemUpdate":
|
||||
case "PlayerListItemRemove":
|
||||
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
|
||||
return;
|
||||
case "Team":
|
||||
@@ -91,7 +94,7 @@ public class BungeePipelineInjector extends PipelineInjector {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (Exception e){
|
||||
} catch (Exception | NoSuchMethodError e){
|
||||
TAB.getInstance().getErrorManager().printError("An error occurred when analyzing packets for player " + player.getName() + " with client version " + player.getVersion().getFriendlyName(), e);
|
||||
}
|
||||
try {
|
||||
@@ -111,9 +114,10 @@ public class BungeePipelineInjector extends PipelineInjector {
|
||||
if (packet.getMode() == 1 || packet.getMode() == 2 || packet.getMode() == 4) return;
|
||||
Collection<String> col = Lists.newArrayList(packet.getPlayers());
|
||||
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
|
||||
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
|
||||
if (col.contains(p.getNickname()) && !((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
|
||||
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !packet.getName().equals(p.getTeamName())) {
|
||||
logTeamOverride(packet.getName(), p.getName(), p.getTeamName());
|
||||
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !packet.getName().equals(sorting.getShortTeamName(p))) {
|
||||
logTeamOverride(packet.getName(), p.getName(), sorting.getShortTeamName(p));
|
||||
col.remove(p.getNickname());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.proxy.ProxyPlatform;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
@@ -14,7 +14,7 @@ import net.md_5.bungee.api.plugin.Plugin;
|
||||
public class BungeePlatform extends ProxyPlatform {
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
* Constructs new instance with BungeeCord packet builder
|
||||
*/
|
||||
public BungeePlatform() {
|
||||
super(new BungeePacketBuilder());
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* TabPlayer implementation for BungeeCord
|
||||
@@ -23,35 +27,18 @@ import java.lang.reflect.Method;
|
||||
public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
|
||||
/** Inaccessible bungee internals */
|
||||
private static Method InitialHandler_getLoginProfile;
|
||||
private static Method ChannelWrapper_getHandle;
|
||||
private static Method LoginResult_Property_getValue;
|
||||
private static Method LoginResult_Property_getSignature;
|
||||
private static Method LoginResult_getProperties;
|
||||
private static Method UserConnection_getGamemode;
|
||||
private static Field wrapperField;
|
||||
private static Object directionData;
|
||||
private static Method getId;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class<?> initialHandler = Class.forName("net.md_5.bungee.connection.InitialHandler");
|
||||
InitialHandler_getLoginProfile = initialHandler.getMethod("getLoginProfile");
|
||||
Class<?> channelWrapper = Class.forName("net.md_5.bungee.netty.ChannelWrapper");
|
||||
ChannelWrapper_getHandle = channelWrapper.getMethod("getHandle");
|
||||
Class<?> loginResult = Class.forName("net.md_5.bungee.connection.LoginResult");
|
||||
Class<?> loginResult_Property = Class.forName("net.md_5.bungee.connection.LoginResult$Property");
|
||||
LoginResult_Property_getValue = loginResult_Property.getMethod("getValue");
|
||||
LoginResult_Property_getSignature = loginResult_Property.getMethod("getSignature");
|
||||
LoginResult_getProperties = loginResult.getMethod("getProperties");
|
||||
Class<?> userConnection = Class.forName("net.md_5.bungee.UserConnection");
|
||||
UserConnection_getGamemode = userConnection.getMethod("getGamemode");
|
||||
Field f = Protocol.class.getDeclaredField("TO_CLIENT");
|
||||
f.setAccessible(true);
|
||||
directionData = f.get(Protocol.GAME);
|
||||
getId = directionData.getClass().getDeclaredMethod("getId", Class.class, int.class);
|
||||
getId.setAccessible(true);
|
||||
wrapperField = initialHandler.getDeclaredField("ch");
|
||||
wrapperField = InitialHandler.class.getDeclaredField("ch");
|
||||
wrapperField.setAccessible(true);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to initialize bungee internal fields", e);
|
||||
@@ -65,10 +52,10 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
* BungeeCord player
|
||||
*/
|
||||
public BungeeTabPlayer(ProxiedPlayer p) {
|
||||
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1);
|
||||
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1, true);
|
||||
try {
|
||||
channel = (Channel) ChannelWrapper_getHandle.invoke(wrapperField.get(getPlayer().getPendingConnection()));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
channel = ((ChannelWrapper)wrapperField.get(getPlayer().getPendingConnection())).getHandle();
|
||||
} catch (IllegalAccessException e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to get channel of " + getPlayer().getName(), e);
|
||||
}
|
||||
}
|
||||
@@ -96,17 +83,11 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
|
||||
@Override
|
||||
public Skin getSkin() {
|
||||
try {
|
||||
Object loginResult = InitialHandler_getLoginProfile.invoke(getPlayer().getPendingConnection());
|
||||
if (loginResult == null) return null;
|
||||
Object[] properties = (Object[]) LoginResult_getProperties.invoke(loginResult);
|
||||
if (properties == null || properties.length == 0) return null;
|
||||
return new Skin((String) LoginResult_Property_getValue.invoke(properties[0]),
|
||||
(String) LoginResult_Property_getSignature.invoke(properties[0]));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get skin of " + getName(), e);
|
||||
return null;
|
||||
}
|
||||
LoginResult loginResult = ((InitialHandler)getPlayer().getPendingConnection()).getLoginProfile();
|
||||
if (loginResult == null) return null;
|
||||
Property[] properties = loginResult.getProperties();
|
||||
if (properties == null || properties.length == 0) return null;
|
||||
return new Skin(properties[0].getValue(), properties[0].getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,12 +135,17 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
|
||||
@Override
|
||||
public int getGamemode() {
|
||||
try {
|
||||
return (int) UserConnection_getGamemode.invoke(player);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get gamemode of " + getPlayer().getName(), e);
|
||||
return 0;
|
||||
}
|
||||
return ((UserConnection)player).getGamemode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProfilePublicKey() {
|
||||
return ((InitialHandler)getPlayer().getPendingConnection()).getLoginRequest().getPublicKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getChatSessionId() {
|
||||
throw new UnsupportedOperationException("Not implemented yet");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,7 +9,7 @@ import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
@@ -26,12 +26,13 @@ public class Main extends Plugin {
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if (!isVersionSupported()) {
|
||||
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1330 and up to work. Get it at https://ci.md-5.net/job/BungeeCord/"));
|
||||
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1671 and up (or an equivalent fork) to work."));
|
||||
return;
|
||||
}
|
||||
BungeePlatform platform = new BungeePlatform();
|
||||
ProxyServer.getInstance().registerChannel(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME);
|
||||
TAB.setInstance(new TAB(new BungeePlatform(), ProtocolVersion.PROXY, getProxy().getVersion(), getDataFolder(), getLogger()));
|
||||
getProxy().getPluginManager().registerListener(this, new BungeeEventListener());
|
||||
TAB.setInstance(new TAB(platform, ProtocolVersion.PROXY, getProxy().getVersion(), getDataFolder(), getLogger()));
|
||||
getProxy().getPluginManager().registerListener(this, new BungeeEventListener(platform));
|
||||
getProxy().getPluginManager().registerCommand(this, new BTABCommand());
|
||||
TAB.getInstance().load();
|
||||
Metrics metrics = new Metrics(this, 10535);
|
||||
@@ -40,13 +41,13 @@ public class Main extends Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for compatibility and returns true if version is supported, false if not
|
||||
* Checks for compatibility and returns {@code true} if version is supported, {@code false} if not
|
||||
*
|
||||
* @return true if version is compatible, false if not
|
||||
* @return {@code true} if version is compatible, {@code false} if not
|
||||
*/
|
||||
private boolean isVersionSupported() {
|
||||
try {
|
||||
Class.forName("net.md_5.bungee.protocol.packet.ScoreboardObjective$HealthDisplay");
|
||||
Class.forName("net.md_5.bungee.protocol.packet.PlayerListItemUpdate");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
@@ -72,7 +73,7 @@ public class Main extends Plugin {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (TAB.getInstance().isDisabled()) {
|
||||
if (TAB.getInstance().isPluginDisabled()) {
|
||||
for (String message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
|
||||
sender.sendMessage(new TextComponent(EnumChatFormat.color(message)));
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
|
||||
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
|
||||
import me.neznamy.tab.shared.TabConstants;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
|
||||
projectVersion=3.1.1
|
||||
projectVersion=3.2.3-SNAPSHOT
|
||||
|
||||
# Gradle properties
|
||||
org.gradle.daemon=true
|
||||
|
||||
+29
-19
@@ -3,36 +3,40 @@ metadata.format.version = "1.1"
|
||||
[versions]
|
||||
|
||||
# API
|
||||
netty = "4.1.75.Final"
|
||||
netty = "4.1.86.Final"
|
||||
jsonSimple = "1.1.1"
|
||||
snakeyaml = "1.30"
|
||||
snakeyaml = "1.33"
|
||||
yamlAssist = "1.0.5"
|
||||
gson = "2.9.0"
|
||||
slf4j = "1.7.36"
|
||||
gson = "2.10"
|
||||
slf4j = "2.0.6"
|
||||
minimessage = "4.12.0"
|
||||
|
||||
# General
|
||||
luckperms = "5.4"
|
||||
guava = "31.1-jre"
|
||||
floodgate = "2.0-SNAPSHOT"
|
||||
kotlin = "1.6.10"
|
||||
floodgate = "2.2.0-SNAPSHOT"
|
||||
kotlin = "1.8.0"
|
||||
event = "3.0.0"
|
||||
|
||||
# Platforms
|
||||
bukkit = "1.16.5-R0.1-SNAPSHOT"
|
||||
bungeecord = "1.18-R0.1-SNAPSHOT"
|
||||
bungeecord = "1.19-R0.1-SNAPSHOT"
|
||||
krypton = "0.59.2"
|
||||
velocity = "1.1.0"
|
||||
velocity = "3.1.2-SNAPSHOT"
|
||||
|
||||
# Platform extras
|
||||
placeholderapi = "2.10.9"
|
||||
placeholderapi = "2.11.2"
|
||||
vault = "1.7"
|
||||
via = "4.0.0"
|
||||
authlib = "1.11"
|
||||
bstats = "2.2.1"
|
||||
libsDisguises = "10.0.26"
|
||||
essentials = "2.19.2"
|
||||
premiumVanish = "2.0.3"
|
||||
redisBungee = "0.6.5-SNAPSHOT"
|
||||
via = "4.5.1"
|
||||
authlib = "1.5.25"
|
||||
bstats = "3.0.0"
|
||||
essentials = "2.19.7"
|
||||
premiumVanish = "2.7.11-2"
|
||||
redisBungee = "0.10.1"
|
||||
|
||||
# Plugins
|
||||
indra = "2.1.1"
|
||||
shadow = "7.1.2"
|
||||
|
||||
[libraries]
|
||||
|
||||
@@ -43,6 +47,9 @@ snakeyaml = { group = "org.yaml", name = "snakeyaml", version.ref = "snakeyaml"
|
||||
yamlAssist = { group = "me.neznamy", name = "yamlassist", version.ref = "yamlAssist" }
|
||||
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
|
||||
slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j"}
|
||||
adventureMiniMessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "minimessage" }
|
||||
adventureApi = { group = "net.kyori", name = "adventure-api", version.ref = "minimessage" }
|
||||
adventureLegacy = { group = "net.kyori", name = "adventure-text-serializer-legacy", version.ref = "minimessage" }
|
||||
|
||||
# General
|
||||
luckperms = { group = "net.luckperms", name = "api", version.ref = "luckperms" }
|
||||
@@ -53,7 +60,7 @@ event = { group = "net.kyori", name = "event-method", version.ref = "event" }
|
||||
|
||||
# Platforms
|
||||
bukkit = { group = "org.purpurmc.purpur", name = "purpur-api", version.ref = "bukkit" }
|
||||
bungeecord = { group = "net.md-5", name = "bungeecord-api", version.ref = "bungeecord" }
|
||||
bungeecord = { group = "net.md-5", name = "bungeecord-proxy", version.ref = "bungeecord" }
|
||||
krypton-api = { group = "org.kryptonmc", name = "krypton-api", version.ref = "krypton" }
|
||||
krypton-server = { group = "org.kryptonmc", name = "krypton-server", version.ref = "krypton" }
|
||||
krypton-annotationProcessor = { group = "org.kryptonmc", name = "krypton-annotation-processor", version.ref = "krypton" }
|
||||
@@ -64,12 +71,15 @@ placeholderapi = { group = "me.clip", name = "placeholderapi", version.ref = "pl
|
||||
vault = { group = "com.github.MilkBowl", name = "VaultAPI", version.ref = "vault" }
|
||||
via = { group = "com.viaversion", name = "viaversion-api", version.ref = "via" }
|
||||
authlib = { group = "com.mojang", name = "authlib", version.ref = "authlib" }
|
||||
libsDisguises = { group = "LibsDisguises", name = "LibsDisguises", version.ref = "libsDisguises" }
|
||||
essentials = { group = "net.essentialsx", name = "EssentialsX", version.ref = "essentials" }
|
||||
premiumVanish = { group = "com.github.MyzelYam", name = "PremiumVanishAPI", version.ref = "premiumVanish" }
|
||||
redisBungee = { group = "com.imaginarycode.minecraft", name = "RedisBungee", version.ref = "redisBungee" }
|
||||
redisBungee = { group = "com.github.limework.redisbungee", name = "RedisBungee-Bungee", version.ref = "redisBungee" }
|
||||
|
||||
# Metrics
|
||||
bstats-bukkit = { group = "org.bstats", name = "bstats-bukkit", version.ref = "bstats" }
|
||||
bstats-bungeecord = { group = "org.bstats", name = "bstats-bungeecord", version.ref = "bstats" }
|
||||
bstats-velocity = { group = "org.bstats", name = "bstats-velocity", version.ref = "bstats" }
|
||||
|
||||
# Plugins
|
||||
plugin-indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" }
|
||||
plugin-shadow = { group = "gradle.plugin.com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -205,6 +205,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
Vendored
+8
-6
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,7 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -8,7 +8,7 @@ val platforms = setOf(
|
||||
rootProject.projects.bukkit,
|
||||
rootProject.projects.bungeecord,
|
||||
// rootProject.projects.krypton,
|
||||
// rootProject.projects.velocity
|
||||
rootProject.projects.velocity
|
||||
).map { it.dependencyProject }
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -95,8 +95,6 @@ class KryptonPlatform(
|
||||
|
||||
override fun isPluginEnabled(plugin: String): Boolean = server.pluginManager.isLoaded(plugin)
|
||||
|
||||
override fun getConfigName(): String = "kryptonconfig.yml"
|
||||
|
||||
private fun loadNametagFeature() {
|
||||
if (!TAB.getInstance().config.getBoolean("scoreboard-teams.enabled", true)) return
|
||||
if (TAB.getInstance().config.getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false)) {
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
|
||||
|
||||
scoreboard-teams:
|
||||
enabled: true
|
||||
enable-collision: true
|
||||
invisible-nametags: false
|
||||
anti-override: true
|
||||
|
||||
# types: GROUPS:<group1>,<group2>... , PERMISSIONS:<permission1>,<perm2>... , PLACEHOLDER:<output1>,<output2>... , PLACEHOLDER_A_TO_Z, PLACEHOLDER_Z_TO_A, PLACEHOLDER_HIGH_TO_LOW, PLACEHOLDER_LOW_TO_HIGH
|
||||
sorting-types:
|
||||
- "GROUPS:owner,admin,mod,helper,builder,vip,default"
|
||||
- "PLACEHOLDER_A_TO_Z:%player%"
|
||||
case-sensitive-sorting: true
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
unlimited-nametag-mode:
|
||||
enabled: false
|
||||
use-marker-tag-for-1-8-x-clients: false
|
||||
disable-on-boats: true
|
||||
space-between-lines: 0.22
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
dynamic-lines:
|
||||
- abovename
|
||||
- nametag #the original one, combination of tagprefix + customtagname + tagsuffix
|
||||
- belowname
|
||||
- another
|
||||
static-lines:
|
||||
myCustomLine: 0.66
|
||||
|
||||
tablist-name-formatting:
|
||||
enabled: true
|
||||
align-tabsuffix-on-the-right: false
|
||||
character-width-overrides: {}
|
||||
anti-override: true
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
|
||||
header-footer:
|
||||
enabled: true
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
header:
|
||||
- "%animation:ServerName%"
|
||||
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
|
||||
- "&7Online players: &f%online%"
|
||||
- "&6Online staff: &e%staffonline%"
|
||||
- ""
|
||||
- "%animation:MyAnimation1%"
|
||||
footer:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "%animation:time%"
|
||||
- "%animation:ping-tps%"
|
||||
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
|
||||
- ""
|
||||
- "&7Visit our webpage %animation:web%"
|
||||
per-world:
|
||||
world1:
|
||||
header:
|
||||
- "an example of world with custom"
|
||||
footer:
|
||||
- "header/footer and prefix/suffix"
|
||||
world2-world3:
|
||||
header:
|
||||
- "This is a shared header for"
|
||||
- "world2 and world3"
|
||||
|
||||
|
||||
yellow-number-in-tablist:
|
||||
enabled: true
|
||||
value: "%ping%"
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
|
||||
belowname-objective:
|
||||
enabled: false
|
||||
number: "%health%"
|
||||
text: "&c❤"
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix
|
||||
prevent-spectator-effect:
|
||||
enabled: false
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar
|
||||
bossbar:
|
||||
enabled: false
|
||||
toggle-command: /bossbar
|
||||
remember-toggle-choice: false
|
||||
hidden-by-default: false
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
bars:
|
||||
ServerInfo:
|
||||
style: "PROGRESS" # can be one of: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20
|
||||
color: "%animation:barcolors%" # can be one of: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
|
||||
progress: "100" # in %
|
||||
text: "&fWebsite: &bwww.domain.com"
|
||||
tpsbar:
|
||||
announcement-bar: true #marks this bossbar as announcement one and doesn't send it always
|
||||
style: NOTCHED_20
|
||||
color: GREEN
|
||||
progress: "%mspt_mean_10s%"
|
||||
text: "&7TPS&e: %condition:tps_5s% &7MSPT&e: %condition:mspt_mean_10s% &7Ping&e: %condition:ping%&7ms"
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Per-world-playerlist
|
||||
per-world-playerlist:
|
||||
enabled: false
|
||||
# players with tab.staff will always see all players
|
||||
allow-bypass-permission: false
|
||||
# players in those worlds will always see all players
|
||||
ignore-effect-in-worlds:
|
||||
- ignoredworld
|
||||
- build
|
||||
shared-playerlist-world-groups:
|
||||
lobby:
|
||||
- lobby1
|
||||
- lobby2
|
||||
minigames:
|
||||
- paintball
|
||||
- bedwars
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard-(premium-only)
|
||||
scoreboard:
|
||||
enabled: false
|
||||
# show/hide the scoreboard
|
||||
toggle-command: /sb
|
||||
remember-toggle-choice: false
|
||||
hidden-by-default: false
|
||||
# when enabled, will show 0-14 in red next to lines
|
||||
use-numbers: false
|
||||
# if use-numbers is disabled, this number will show in red next to all lines
|
||||
static-number: 0
|
||||
delay-on-join-milliseconds: 0
|
||||
respect-other-plugins: true
|
||||
disable-in-worlds:
|
||||
- disabledworld
|
||||
scoreboards:
|
||||
admin:
|
||||
# Only players with tab.scoreboard.admin can see this scoreboard, others will see scoreboard1
|
||||
display-condition: "permission:tab.scoreboard.admin"
|
||||
title: "Admin scoreboard"
|
||||
lines:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "&6Online:"
|
||||
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
|
||||
- "* &eCurrent World&7: &f%worldonline%"
|
||||
- "* &eStaff&7: &f%staffonline%"
|
||||
- ""
|
||||
- "&6Server Info:"
|
||||
- "* &bTPS&7: %tps_5s%"
|
||||
- "* &bUptime&7: &f%server_uptime%"
|
||||
- "* &bMemory&7: &f%memory-used%&7/&4%memory-max%"
|
||||
- "%animation:MyAnimation1%"
|
||||
scoreboard1:
|
||||
title: "Default"
|
||||
lines:
|
||||
- "%animation:MyAnimation1%"
|
||||
- "&6My Stats:"
|
||||
- "* &eKills&7: &f%statistic_player_kills%"
|
||||
- "* &eDeaths&7: &f%statistic_deaths%"
|
||||
- "* &eHealth&7: &f%health%"
|
||||
- ""
|
||||
- "&6Personal Info:"
|
||||
- "* &bRank&7: &f%rank%"
|
||||
- "* &bPing&7: &f%ping%&7ms"
|
||||
- "* &bWorld&7: &f%world%"
|
||||
- "%animation:MyAnimation1%"
|
||||
|
||||
ping-spoof:
|
||||
enabled: false
|
||||
# when enabled, all players will have this value as their ping
|
||||
value: 0
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Pet-name-fix
|
||||
fix-pet-names:
|
||||
enabled: false
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/config.yml#placeholders
|
||||
placeholders:
|
||||
date-format: "dd.MM.yyyy"
|
||||
time-format: "[HH:mm:ss / h:mm a]"
|
||||
time-offset: 0
|
||||
|
||||
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements-(premium-only)
|
||||
placeholder-output-replacements:
|
||||
"%essentials_vanished%":
|
||||
"yes": "&7| Vanished"
|
||||
"no": ""
|
||||
"%afk%":
|
||||
true: " &o[AFK]&r"
|
||||
false: ""
|
||||
|
||||
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list below> permission
|
||||
assign-groups-by-permissions: false
|
||||
|
||||
#if the option above is true, all groups are taken based on permissions and the one higher in this list is used as primary
|
||||
primary-group-finding-list:
|
||||
- Owner
|
||||
- Admin
|
||||
- Mod
|
||||
- Helper
|
||||
- default
|
||||
|
||||
debug: false
|
||||
|
||||
mysql:
|
||||
enabled: false
|
||||
host: 127.0.0.1
|
||||
port: 3306
|
||||
database: tab
|
||||
username: user
|
||||
password: password
|
||||
+11
-4
@@ -3,21 +3,28 @@ enableFeaturePreview("VERSION_CATALOGS")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral() // Netty, SnakeYaml, json-simple, Gson, slf4j, Guava, Kyori event, bStats
|
||||
mavenCentral() // Netty, SnakeYaml, json-simple, Gson, slf4j, Guava, Kyori event, bStats, AuthLib
|
||||
maven("https://jitpack.io") // PremiumVanish
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // PlaceholderAPI
|
||||
maven("https://repo.viaversion.com/") // ViaVersion
|
||||
maven("https://repo.essentialsx.net/releases/") // Essentials
|
||||
maven("https://repo.codemc.org/repository/maven-public/") // RedisBungee, BungeeCord
|
||||
maven("https://repo.codemc.org/repository/maven-public/") // RedisBungee
|
||||
maven("https://repo.codemc.io/repository/maven-snapshots/") // Fixing compilation
|
||||
maven("https://repo.kryptonmc.org/releases") // YamlAssist, LuckPerms
|
||||
maven("https://repo.kryptonmc.org/snapshots") // BungeeCord
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
|
||||
maven("https://repo.purpurmc.org/snapshots") // Purpur, LibsDisguises, AuthLib, Vault
|
||||
maven("https://repo.purpurmc.org/snapshots") // Purpur, Vault
|
||||
maven("https://repo.papermc.io/repository/maven-public/") // Velocity
|
||||
}
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
}
|
||||
|
||||
pluginManagement {
|
||||
includeBuild("build-logic")
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "TAB"
|
||||
@@ -25,7 +32,7 @@ rootProject.name = "TAB"
|
||||
include(":api")
|
||||
include(":shared")
|
||||
//include(":krypton")
|
||||
//include(":velocity")
|
||||
include(":velocity")
|
||||
include(":bukkit")
|
||||
include(":bungeecord")
|
||||
include(":jar")
|
||||
+1
-10
@@ -1,7 +1,3 @@
|
||||
plugins {
|
||||
id("net.kyori.blossom") version "1.2.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(projects.api)
|
||||
api(libs.event) {
|
||||
@@ -12,9 +8,4 @@ dependencies {
|
||||
compileOnlyApi(libs.guava)
|
||||
compileOnlyApi(libs.floodgate)
|
||||
compileOnlyApi(libs.slf4j)
|
||||
}
|
||||
|
||||
blossom {
|
||||
replaceToken("@plugin_version@", project.version)
|
||||
replaceTokenIn("src/main/java/me/neznamy/tab/shared/TabConstants.java")
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.task.RepeatingTask;
|
||||
import me.neznamy.tab.api.task.ThreadManager;
|
||||
|
||||
/**
|
||||
@@ -45,10 +44,10 @@ public class CpuManager implements ThreadManager {
|
||||
private Map<String, AtomicInteger> packetsPrevious = new ConcurrentHashMap<>();
|
||||
|
||||
/** TAB's main thread where all tasks are executed */
|
||||
private ExecutorService thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
|
||||
private final ExecutorService thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
|
||||
|
||||
/** Thread pool for delayed and repeating tasks to perform sleep before submitting task to main thread */
|
||||
private final ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool(
|
||||
private final ScheduledThreadPoolExecutor threadPool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(20,
|
||||
new ThreadFactoryBuilder().setNameFormat("TAB Repeating / Delayed Thread %d").build());
|
||||
|
||||
/** Tasks submitted to main thread before plugin was fully enabled */
|
||||
@@ -75,13 +74,10 @@ public class CpuManager implements ThreadManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all tasks, new instance is set to avoid errors when starting tasks on shutdown (such as packet readers)
|
||||
* Cancels all tasks and shuts down thread pools
|
||||
*/
|
||||
public void cancelAllTasks() {
|
||||
//preventing errors when tasks are inserted while shutting down
|
||||
ExecutorService old = thread;
|
||||
thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
|
||||
old.shutdownNow();
|
||||
thread.shutdownNow();
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
@@ -90,7 +86,7 @@ public class CpuManager implements ThreadManager {
|
||||
*/
|
||||
public void enable() {
|
||||
enabled = true;
|
||||
taskQueue.forEach(this::submit);
|
||||
new ArrayList<>(taskQueue).forEach(this::submit);
|
||||
taskQueue.clear();
|
||||
}
|
||||
|
||||
@@ -108,13 +104,20 @@ public class CpuManager implements ThreadManager {
|
||||
taskQueue.add(task);
|
||||
return null;
|
||||
}
|
||||
return (Future<Void>) thread.submit(() -> {
|
||||
try {
|
||||
task.run();
|
||||
} catch (Exception | LinkageError | StackOverflowError e) {
|
||||
TAB.getInstance().getErrorManager().printError("An error was thrown when executing task", e);
|
||||
}
|
||||
});
|
||||
if (thread.isShutdown()) return null;
|
||||
try {
|
||||
return (Future<Void>) thread.submit(() -> {
|
||||
try {
|
||||
task.run();
|
||||
} catch (Exception | LinkageError | StackOverflowError e) {
|
||||
TAB.getInstance().getErrorManager().printError("An error was thrown when executing task", e);
|
||||
}
|
||||
});
|
||||
} catch (OutOfMemoryError e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to schedule task due to " + e.getClass().getName() +
|
||||
": " + e.getMessage() + ". Threads created by TAB: " + (threadPool.getActiveCount()+1), null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,34 +362,26 @@ public class CpuManager implements ThreadManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepeatingTask startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task) {
|
||||
return new TabRepeatingTask(threadPool, task, feature, type, intervalMilliseconds);
|
||||
public Future<?> startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task) {
|
||||
if (threadPool.isShutdown()) return null;
|
||||
return threadPool.scheduleAtFixedRate(() -> runMeasuredTask(feature, type, task), 0, intervalMilliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepeatingTask startRepeatingTask(int intervalMilliseconds, Runnable task) {
|
||||
return new TabRepeatingTask(threadPool, task, null, null, intervalMilliseconds);
|
||||
public Future<?> startRepeatingTask(int intervalMilliseconds, Runnable task) {
|
||||
if (threadPool.isShutdown()) return null;
|
||||
return threadPool.scheduleAtFixedRate(() -> runTask(task), 0, intervalMilliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task) {
|
||||
try {
|
||||
return threadPool.submit(() -> {
|
||||
try {
|
||||
Thread.sleep(delayMilliseconds);
|
||||
runMeasuredTask(feature, type, task);
|
||||
} catch (InterruptedException pluginDisabled) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
} catch (OutOfMemoryError e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Failed to create new delayed task, active thread count: " +
|
||||
threadPool.getActiveCount() + " / " + threadPool.getPoolSize(), e);
|
||||
return null;
|
||||
}
|
||||
if (threadPool.isShutdown()) return null;
|
||||
return threadPool.schedule(() -> runMeasuredTask(feature, type, task), delayMilliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public ThreadPoolExecutor getThreadPool() {
|
||||
return threadPool;
|
||||
@Override
|
||||
public Future<?> runTaskLater(int delayMilliseconds, Runnable task) {
|
||||
if (threadPool.isShutdown()) return null;
|
||||
return threadPool.schedule(() -> submit(task), delayMilliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
@@ -225,6 +225,6 @@ public class DynamicText implements Property {
|
||||
RelationalPlaceholderImpl pl = (RelationalPlaceholderImpl) TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
|
||||
format = format.replace(pl.getIdentifier(), viewer == null ? "" : pl.getLastValue(viewer, owner));
|
||||
}
|
||||
return format;
|
||||
return EnumChatFormat.color(format);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import java.nio.file.Files;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
|
||||
@@ -226,7 +227,8 @@ public class ErrorManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes interval divisible by 50 and sends error message if it was not already or was 0 or less
|
||||
* Makes interval divisible by {@link me.neznamy.tab.api.TabConstants.Placeholder#MINIMUM_REFRESH_INTERVAL}
|
||||
* and sends error message if it was not already or was 0 or less
|
||||
*
|
||||
* @param name
|
||||
* name of animation used in error message
|
||||
@@ -243,10 +245,10 @@ public class ErrorManager {
|
||||
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s. Refresh cannot be negative! &bUsing 1000.", name, interval));
|
||||
return 1000;
|
||||
}
|
||||
if (interval % 50 != 0) {
|
||||
int newInterval = interval - interval%50;
|
||||
if (newInterval == 0) newInterval = 50;
|
||||
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s which is not divisible by 50! &bUsing %s.", name, interval, newInterval));
|
||||
if (interval % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL != 0) {
|
||||
int newInterval = interval - interval % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL;
|
||||
if (newInterval == 0) newInterval = TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL;
|
||||
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s which is not divisible by " + TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL + "! &bUsing %s.", name, interval, newInterval));
|
||||
return newInterval;
|
||||
}
|
||||
return interval;
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.FeatureManager;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
|
||||
@@ -28,6 +29,10 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
/** All registered features in an array to avoid memory allocations on iteration */
|
||||
private TabFeature[] values = new TabFeature[0];
|
||||
|
||||
private boolean objectiveListeners = false;
|
||||
|
||||
private boolean displayObjectiveListeners = false;
|
||||
|
||||
/**
|
||||
* Calls load() on all features.
|
||||
* This function is called on plugin startup.
|
||||
@@ -94,12 +99,7 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
return pack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onQuit(TabPlayer) on all features
|
||||
*
|
||||
* @param disconnectedPlayer
|
||||
* player who disconnected
|
||||
*/
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
if (disconnectedPlayer == null) return;
|
||||
long millis = System.currentTimeMillis();
|
||||
@@ -113,26 +113,17 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
TAB.getInstance().debug("Player quit of " + disconnectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onJoin(TabPlayer) on all features
|
||||
*
|
||||
* @param connectedPlayer
|
||||
* player who connected
|
||||
*/
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
if (!connectedPlayer.isOnline()) return;
|
||||
long millis = System.currentTimeMillis();
|
||||
TAB.getInstance().addPlayer(connectedPlayer);
|
||||
for (TabFeature f : values) {
|
||||
try {
|
||||
if (!f.overridesMethod("onJoin")) continue;
|
||||
long time = System.nanoTime();
|
||||
f.onJoin(connectedPlayer);
|
||||
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
|
||||
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Feature " + f.getClass().getSimpleName() + " failed to process player " + connectedPlayer.getName(), e);
|
||||
}
|
||||
if (!f.overridesMethod("onJoin")) continue;
|
||||
long time = System.nanoTime();
|
||||
f.onJoin(connectedPlayer);
|
||||
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
|
||||
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
|
||||
}
|
||||
((ITabPlayer)connectedPlayer).markAsLoaded(true);
|
||||
TAB.getInstance().debug("Player join of " + connectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
|
||||
@@ -142,14 +133,7 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onWorldChange(TabPlayer, String, String) on all features
|
||||
*
|
||||
* @param playerUUID
|
||||
* player who switched world
|
||||
* @param to
|
||||
* name of the new world
|
||||
*/
|
||||
@Override
|
||||
public void onWorldChange(UUID playerUUID, String to) {
|
||||
TabPlayer changed = TAB.getInstance().getPlayer(playerUUID);
|
||||
if (changed == null) return;
|
||||
@@ -164,21 +148,15 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.WORLD)).updateValue(changed, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onServerChange(TabPlayer, String, String) on all features
|
||||
*
|
||||
* @param playerUUID
|
||||
* player who switched server
|
||||
* @param to
|
||||
* name of the new server
|
||||
*/
|
||||
@Override
|
||||
public void onServerChange(UUID playerUUID, String to) {
|
||||
TabPlayer changed = TAB.getInstance().getPlayer(playerUUID);
|
||||
if (changed == null) return;
|
||||
String from = changed.getServer();
|
||||
((ITabPlayer)changed).setServer(to);
|
||||
((ProxyTabPlayer)changed).sendJoinPluginMessage();
|
||||
if (!isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION)) onLoginPacket(changed);
|
||||
if (!isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION) || changed.getVersion().getMinorVersion() < 8)
|
||||
onLoginPacket(changed);
|
||||
for (TabFeature f : values) {
|
||||
if (!f.overridesMethod("onServerChange")) continue;
|
||||
long time = System.nanoTime();
|
||||
@@ -188,15 +166,7 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.SERVER)).updateValue(changed, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onCommand(TabPlayer, String) on all features
|
||||
*
|
||||
* @param sender
|
||||
* command sender
|
||||
* @param command
|
||||
* command line including /
|
||||
* @return {@code true} if command should be cancelled, {@code false} if not
|
||||
*/
|
||||
@Override
|
||||
public boolean onCommand(TabPlayer sender, String command) {
|
||||
if (sender == null) return false;
|
||||
boolean cancel = false;
|
||||
@@ -209,6 +179,16 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markObjective() {
|
||||
objectiveListeners = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markDisplayObjective() {
|
||||
displayObjectiveListeners = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls onPacketReceive(TabPlayer, Object) on all features
|
||||
*
|
||||
@@ -281,6 +261,7 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
* if reflective operation fails
|
||||
*/
|
||||
public void onDisplayObjective(TabPlayer packetReceiver, Object packet) throws ReflectiveOperationException {
|
||||
if (!displayObjectiveListeners) return;
|
||||
long time = System.nanoTime();
|
||||
PacketPlayOutScoreboardDisplayObjective display = TAB.getInstance().getPlatform().getPacketBuilder().readDisplayObjective(packet);
|
||||
TAB.getInstance().getCPUManager().addTime(TabConstants.Feature.PACKET_DESERIALIZING, TabConstants.CpuUsageCategory.PACKET_DISPLAY_OBJECTIVE, System.nanoTime()-time);
|
||||
@@ -301,6 +282,7 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
* if reflective operation fails
|
||||
*/
|
||||
public void onObjective(TabPlayer packetReceiver, Object packet) throws ReflectiveOperationException {
|
||||
if (!objectiveListeners) return;
|
||||
long time = System.nanoTime();
|
||||
PacketPlayOutScoreboardObjective display = TAB.getInstance().getPlatform().getPacketBuilder().readObjective(packet);
|
||||
TAB.getInstance().getCPUManager().addTime(TabConstants.Feature.PACKET_DESERIALIZING, TabConstants.CpuUsageCategory.PACKET_OBJECTIVE, System.nanoTime()-time);
|
||||
@@ -342,4 +324,9 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
public TabFeature getFeature(String name) {
|
||||
return features.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabFeature[] getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 205 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user