Compare commits

...
132 Commits
Author SHA1 Message Date
NEZNAMY e1c152d95d Add missing change in previous commit 2022-06-11 18:00:08 +02:00
NEZNAMY dffac5deb8 BungeeCord - Retry plugin message processing if player isn't loaded for any reason (#564) 2022-06-11 17:57:18 +02:00
NEZNAMY 8aa3bbfad3 Scoreboard - Fix RGB codes not translating into legacy correctly in long lines 2022-06-09 23:10:44 +02:00
NEZNAMY 5bb4006e55 Scoreboard - Fix additional empty lines when use-numbers is enabled 2022-06-08 00:46:32 +02:00
NEZNAMY e06a406682 Clean up repositories 2022-06-08 00:45:21 +02:00
NEZNAMY a19719be4b Configuration - Remove deleted placeholder from default config 2022-06-03 23:47:41 +02:00
NEZNAMY 35856a9f3d Merge branch 'master' of https://github.com/NEZNAMY/TAB 2022-06-02 15:50:46 +02:00
NEZNAMY cf1d5d5ee2 Code style - Remove unused method 2022-06-01 20:23:16 +02:00
NEZNAMY f69a845e51 Bukkit - Implement chat message sending on <1.7 2022-05-31 12:21:07 +02:00
NEZNAMY db67e93bb8 Disable compilation of a module 2022-05-31 11:51:21 +02:00
NEZNAMY 313f089222 Aligned Suffix - Fix memory leak 2022-05-20 01:10:00 +02:00
NEZNAMY 458c08ded7 Tab expansion - Slightly optimize placeholder retrieving 2022-05-18 01:39:20 +02:00
NEZNAMY fe6cc7417f Placeholders - Fix refresh interval forwarding to bridge (#597) (2nd attempt) 2022-05-17 19:00:23 +02:00
NEZNAMY 181c94a808 Code style - Extract strings into constants 2022-05-13 00:42:30 +02:00
NEZNAMY 9ff6f02d8d Code style - Remove unused code and other minor changes 2022-05-12 22:49:41 +02:00
NEZNAMY 3cf74516d9 Bukkit - Remove unused data watcher registries 2022-05-12 22:25:18 +02:00
NEZNAMY 31f5502c16 BungeeCord - Delete TAB's bungeecord events (to make users switch to universal ones) 2022-05-12 22:04:37 +02:00
NEZNAMY 671d60bd72 API - Throw user-friendly exception if player is not loaded yet (#585) 2022-05-10 18:32:09 +02:00
NEZNAMY 294e866501 Placeholders - Fix refresh interval forwarding to bridge (#597) 2022-05-10 18:21:13 +02:00
NEZNAMY 96789100e3 Bukkit - Wrap incoming components instead of deserializing and then serializing them 2022-05-10 13:22:27 +02:00
NEZNAMY ea934fc7fb GroupManager - Add null check for player group from permission plugin 2022-05-08 12:48:06 +02:00
NEZNAMY d529b7b340 Sorting - Allow placeholders with ":" inside to work properly in PLACEHOLDER sorting type 2022-05-08 12:47:40 +02:00
NEZNAMY 092ca7f9f8 Aligned Suffix - Assume players can see themselves when vanished 2022-05-03 18:03:29 +02:00
NEZNAMY 79f4bdb869 Unlimited Nametags - Do not delay respawn by 1 tick anymore to fix OOM 2022-05-01 13:07:42 +02:00
NEZNAMY b0d2d0f7dc NickCompatibility - Clean up code 2022-04-30 20:01:40 +02:00
NEZNAMY cdf58c1dfe Teams - set canSeeFriendlyInvisibles to true 2022-04-30 18:21:46 +02:00
NEZNAMY 4680de6022 Feature management - Add try/catch for each feature in join processing (#560) 2022-04-30 17:22:03 +02:00
NEZNAMY e8f61181b6 Error logging - Log (critical) errors into console even when error file is full 2022-04-30 17:20:24 +02:00
NEZNAMY 75e9e007d0 Fix disabled worlds not loading correctly (from #6f07cf4) 2022-04-28 17:27:23 +02:00
NEZNAMY 3574ef7c0b Belowname - Do not update score when entering disabled world 2022-04-28 12:20:54 +02:00
NEZNAMY 86158036d2 Commands - Make permission group assigning more obvious in debug output 2022-04-28 11:55:21 +02:00
NEZNAMY e73beb47a3 API - Fix scoreboard Line#setText not working 2022-04-26 23:23:42 +02:00
NEZNAMY 0db5f1dd73 Scoreboard - Fix NPE when a line is null 2022-04-23 14:18:30 +02:00
NEZNAMY 6dcdc2aa58 PetFix - Fix attack packet cancellation 2022-04-20 10:46:08 +02:00
NEZNAMY 64601137e8 Scoreboard - Show empty lines when defined as empty, only hide when placeholder output is empty 2022-04-15 00:07:44 +02:00
NEZNAMY af7697b670 BungeeCord - Fixed NPE from moving to reflection 2022-04-14 23:24:45 +02:00
NEZNAMY 9e4f46088b Commands - Do not force refresh players who are not loaded yet (#560) 2022-04-12 11:21:06 +02:00
NEZNAMY 91e71a9b9c Layout - Fixed NPE when player had no layout visible 2022-04-10 23:32:13 +02:00
NEZNAMY 1198ffb847 BungeeCord - Use reflection for proxy module access due to the repository no longer being available 2022-04-10 23:31:51 +02:00
NEZNAMY 72116ecd0c Javadoc - Further improve documentation 2022-04-10 21:08:19 +02:00
NEZNAMY 04ebb7123d Code style - Move NMS object declaration and initialization 2022-04-10 20:28:29 +02:00
NEZNAMY 43096beafe Bukkit - Use ConsoleCommandSender for colored messages (#555) 2022-04-09 18:27:37 +02:00
NEZNAMY 2a08c62456 Tab expansion - Fix server placeholders not loading on join 2022-04-07 15:44:58 +02:00
NEZNAMY b62d74c3fa Code style - Remove unnecessary reflection 2022-04-06 20:38:03 +02:00
NEZNAMY 652fb5a6ad Bukkit - Switch from Paper to Purpur API 2022-04-06 19:11:19 +02:00
NEZNAMY d1f25cc19f Bukkit - Disable checking for plugins with compatibility issue 2022-04-06 18:25:12 +02:00
NEZNAMY dc6a9d9761 Fix layout startup 2022-04-06 13:44:16 +02:00
NEZNAMY 838d725715 Code style - Removed redundant static modifiers 2022-04-06 13:31:31 +02:00
NEZNAMY c45acb39ca Code style - Clean up variable initialization 2022-04-06 13:10:11 +02:00
NEZNAMY 957edf2a52 Javadoc - Fixed compilation warn 2022-04-06 12:44:54 +02:00
NEZNAMY d969c6ed0e Code style - Change way of checking if server is proxy 2022-04-06 12:41:20 +02:00
NEZNAMY dd6fca961c Code style - Clean up configuration loading 2022-04-06 12:34:39 +02:00
NEZNAMY e88a6df456 Config - remove hidden unregister-before-register option and remove the function entirely. Hopefully with improved checks this should not be necessary anymore. It caused client errors, as well as #549 2022-04-06 11:35:03 +02:00
NEZNAMY 3a4530c999 RGB - Finish #552 - Only replace clean gradients in property initialization 2022-04-05 23:24:37 +02:00
NEZNAMY b623dba40a Merge branch 'master' of https://github.com/NEZNAMY/TAB 2022-04-05 23:20:13 +02:00
NEZNAMY e2fd945109 Merge pull request #552 from lucypoulton/feat/kyori-rgb-format
Add support for Kyori MiniMessage RGB format
2022-04-05 23:19:52 +02:00
NEZNAMY e6dc3cda31 Unlimited nametags - Manually destroy armor stands on world switch 2022-04-05 20:21:36 +02:00
Lucy Poulton 18d8ff127a add support for kyori minimessage rgb format 2022-04-05 14:25:22 +01:00
NEZNAMY 4a9526451f Code style - Remove redundant "triggerMode" flag from Placeholder class due being redundant. Setting refresh to -1 has the same effect. 2022-04-04 19:03:51 +02:00
NEZNAMY 669d89c870 Code style - Move tab expansion code to interface 2022-04-04 18:15:06 +02:00
NEZNAMY 39161f2af5 Nametags - Reduced CPU usage of anti-override 2022-04-04 16:02:32 +02:00
NEZNAMY be8c33e3fc Velocity - Delay join event processing to avoid unformatted tablist names 2022-04-03 14:08:19 +02:00
NEZNAMY 7c1a27f9df Javadoc - Further improve documentation 2022-04-02 19:07:18 +02:00
NEZNAMY 342cc93dbf Code style - Clean up code 2022-04-02 13:17:15 +02:00
NEZNAMY 6f07cf439b Code style - Clean up loading of disabled servers/worlds 2022-04-01 23:40:04 +02:00
NEZNAMY 1d4d00b69b Code style - Clean up code with usage of @Inject 2022-04-01 23:21:30 +02:00
NEZNAMY 54cb3e4d54 3.1.1 2022-04-01 10:02:26 +02:00
NEZNAMY b0fc2caef9 RedisBungee - Detect player name change from other proxies 2022-03-31 21:54:51 +02:00
NEZNAMY 1221509d24 MySQL - Do not lowercase _DEFAULT_ group name, which caused it to not load (#546) (for real this time) 2022-03-31 20:52:12 +02:00
NEZNAMY 17f5f60504 MySQL - Do not lowercase _DEFAULT_ group name, which caused it to not load (#546) 2022-03-31 17:02:12 +02:00
NEZNAMY a8b0b488c6 Layout - Implement latency updating for player slots (#545) 2022-03-31 15:45:14 +02:00
NEZNAMY 6d922d5b70 Unlimited nametags - Fix NPE when player moves too quickly after joining 2022-03-31 14:50:54 +02:00
NEZNAMY 1ab62a33e3 Placeholders - Improve refresh task interval adaptation (#543) 2022-03-31 10:01:10 +02:00
NEZNAMY f9540a1e78 Tab Expansion - Fix placeholders not loading on player join 2022-03-29 11:17:04 +02:00
NEZNAMY a720bf2c8b Unlimited nametags - Do not allow preview command when armor stands are disabled for player 2022-03-28 21:25:42 +02:00
NEZNAMY c143eab6fb Global playerlist - Fix player possibly being removed from playerlist after reconnecting rapidly (#539) 2022-03-28 20:57:47 +02:00
NEZNAMY 1afb20af8b Code style - Avoid reassigning parameters 2022-03-28 12:57:31 +02:00
NEZNAMY c6f27c839d Code style - Document empty method body 2022-03-28 12:49:37 +02:00
NEZNAMY 742f1708c5 Code style - Remove unused import 2022-03-28 12:47:19 +02:00
NEZNAMY ac9601a48b Unlimited nametags - Fix nametag toggle command not hiding vanilla name on players with disabled armor stands via API 2022-03-28 12:13:23 +02:00
NEZNAMY 4cd109a5fc Javadoc - Update style and fix alignment after replacing tabs with spaces 2022-03-26 23:13:19 +01:00
NEZNAMY 078554a8b0 RedisBungee - Fix /btab reload removing players from tablist when global playerlist is disabled 2022-03-25 14:46:59 +01:00
NEZNAMY 464e2f4f64 RedisBungee - Fix duplicated team names through proxies 2022-03-25 14:46:22 +01:00
NEZNAMY b84f55aa18 RedisBungee - Fix errors and issues when unlimited nametag mode is enabled 2022-03-25 14:45:55 +01:00
NEZNAMY be2f780bfa Drop LuckPerms event hook and go back to periodic task. Too many issues related to events. 2022-03-25 10:27:36 +01:00
NEZNAMY 262d8493a6 fix NPE 2022-03-25 09:33:32 +01:00
NEZNAMY 24e12e871b finish #534 2022-03-24 22:33:18 +01:00
NEZNAMY a2ee75339c 3.1.1-pre2 2022-03-24 22:20:25 +01:00
NEZNAMY 9c28c4d63c add auto complete for group names in group command 2022-03-22 22:12:55 +01:00
NEZNAMY 7d57da1424 fix disableArmorStands method not destroying armor stands 2022-03-22 13:06:28 +01:00
NEZNAMY b77b20ba4d expose placeholder output getters in the API 2022-03-22 11:54:30 +01:00
NEZNAMY 57ae58f093 call super.onQuit as it should 2022-03-21 20:26:56 +01:00
NEZNAMY b29c878688 Merge remote-tracking branch 'origin/master' 2022-03-21 08:51:52 +01:00
NEZNAMY 5a5a6c76c1 another banger that took a week to fix 2022-03-21 08:51:32 +01:00
NEZNAMY f6a53f537c fix NPE 2022-03-21 08:46:02 +01:00
NEZNAMY d86ed37208 allow negative values in replacement intervals using ~ 2022-03-20 17:55:17 +01:00
NEZNAMY 92b945f28d Merge pull request #534 from itsmemac/master
fix nametag preview help text
2022-03-20 15:03:07 +01:00
Mac cb164f0055 fix nametag preview help text
it seems new update `/tab ntpreview` has been replaced with `/tab nametag preview`
Help text was not updated
2022-03-20 19:31:38 +05:30
NEZNAMY acfe37c1b5 fix NPE 2022-03-20 12:50:30 +01:00
NEZNAMY 44ded26df4 change method call order in which groups are refreshed 2022-03-19 16:13:39 +01:00
NEZNAMY 6dbd92a010 remove players from map on quit 2022-03-19 16:12:06 +01:00
NEZNAMY f729ca0ad2 fix NPE 2022-03-19 16:11:45 +01:00
NEZNAMY 065490c077 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2022-03-18 17:12:20 +01:00
NEZNAMY da6425dd93 Merge pull request #530 from CoachYT1/master
Update dependencies & GitHub actions
2022-03-15 08:25:12 +01:00
CoachYT 250dcbc676 Update build.yml 2022-03-14 12:18:41 +01:00
CoachYT de4ca4a88f Update libs.versions.toml 2022-03-14 12:15:58 +01:00
NEZNAMY 723a0c890e fix NPE 2022-03-13 22:36:37 +01:00
NEZNAMY 7085e7df20 do not forward group if assigning by permissions 2022-03-13 22:36:25 +01:00
NEZNAMY 09496d60f3 add argument check 2022-03-13 13:19:05 +01:00
NEZNAMY bf6bc26a5c add check into API for all the users who shade API into their projects 2022-03-13 13:16:20 +01:00
NEZNAMY a7217eae15 fix placeholders used only in tab expansion not refreshing 2022-03-13 13:12:37 +01:00
NEZNAMY 2a532ef9b7 update README.md 2022-03-13 13:11:37 +01:00
NEZNAMY c304ded924 avoid thread spam from LP events 2022-03-11 15:06:13 +01:00
NEZNAMY 4d8c47a505 #517 - make group names not case-sensitive when connected to MySQL 2022-03-09 10:22:51 +01:00
NEZNAMY d3e0ccbf34 forward nametag preview on server switch 2022-03-09 09:08:49 +01:00
NEZNAMY 3c82b46148 fix updating of invalid score on server switch when pipeline injection is disabled 2022-03-08 23:09:29 +01:00
NEZNAMY 07d7f2cc17 catch StackOverflowError as well 2022-03-08 21:50:10 +01:00
NEZNAMY 54d4f09ab1 fix StackOverflowError when using placeholder identifier inside its own replacements (will act the same as %value%) 2022-03-08 21:45:11 +01:00
NEZNAMY e8c698b5e8 #517 - fix mysql header/footer download/upload 2022-03-07 22:14:49 +01:00
NEZNAMY b096906ffd 3.1.1-pre1 2022-03-07 18:59:40 +01:00
NEZNAMY 5a9cec12fa WeakHashMap isn't so weak after all (fixed memory leak) 2022-03-07 18:59:22 +01:00
NEZNAMY a2ed71751f fix NPE 2022-03-07 18:58:10 +01:00
NEZNAMY 5a3a2df920 simplify 2022-03-07 15:50:24 +01:00
NEZNAMY 9d07e669ea merge ifs 2022-03-07 15:50:12 +01:00
NEZNAMY aa18f1cc35 remove unnecessary full path 2022-03-07 14:03:04 +01:00
NEZNAMY 668d78ea64 remove redundant constructor 2022-03-07 13:59:38 +01:00
NEZNAMY 6f62581d6e print OOM into file instead of throwing into console 2022-03-07 12:53:12 +01:00
NEZNAMY 06686ada5c add synchronized lock to printError method 2022-03-07 12:21:47 +01:00
NEZNAMY 4380482606 remove direct reference to player channel on bukkit 2022-03-07 12:21:24 +01:00
NEZNAMY d5112b3d32 fix group refreshing introduced in previous commit which in the end didn't even fix the issue it was supposed to 2022-03-06 20:26:49 +01:00
NEZNAMY ef67506640 fix error caused by concurrent access 2022-03-06 12:01:50 +01:00
NEZNAMY c2ea4d2793 #516 - skin signature can be null 2022-03-05 16:29:16 +01:00
190 changed files with 4796 additions and 3465 deletions

No files matched your search

+5 -4
View File
@@ -11,15 +11,16 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 11
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 11
- name: Cache SonarCloud packages
uses: actions/cache@v1
uses: actions/cache@v2
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
@@ -38,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@v2.3.1
uses: actions/upload-artifact@v3
with:
name: Jar
path: jar/build/libs/TAB-**.jar
+2 -3
View File
@@ -16,16 +16,15 @@ Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
# Links
The precompiled JAR can be downloaded for free from:
* Releases: https://github.com/NEZNAMY/TAB/releases
* 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
* Donation link: https://paypal.me/neznamy1
Other links:
* Discord: https://discord.gg/EaSvdk6
* Discord support for buyers: https://discord.gg/EaSvdk6
* Wiki: https://github.com/NEZNAMY/TAB/wiki
# Issues
@@ -4,25 +4,30 @@ public interface ArmorStand {
/**
* Returns true if offset is static, false if not
* @return true if static, false if dynamic
*
* @return true if static, false if dynamic
*/
boolean hasStaticOffset();
/**
* Changes offset of the armor stand
* @param offset - new offset
*
* @param offset
* new offset
*/
void setOffset(double offset);
/**
* Returns offset of the armor stand
* @return offset from base
*
* @return offset from base
*/
double getOffset();
/**
* Returns property for armor stand's name
* @return property for armor stand's name
*
* @return property for armor stand's name
*/
Property getProperty();
@@ -33,14 +38,17 @@ public interface ArmorStand {
/**
* Teleports armor stand to owner's current location for specified player
* @param viewer - player to send packet to
*
* @param viewer
* player to send packet to
*/
void teleport(TabPlayer viewer);
/**
* Changes sneaking flag and sends packets to everyone in range
* @param sneaking
* new sneaking status
*
* @param sneaking
* new sneaking status
*/
void sneak(boolean sneaking);
@@ -51,7 +59,9 @@ public interface ArmorStand {
/**
* DeSpawns armor stand for specified player
* @param viewer - player to send packet to
*
* @param viewer
* player to send packet to
*/
void destroy(TabPlayer viewer);
@@ -62,19 +72,24 @@ public interface ArmorStand {
/**
* Updates visibility if needed
* @param force - if refresh should be forced
*
* @param force
* if refresh should be forced
*/
void updateVisibility(boolean force);
/**
* Returns entity ID of this armor stand
* @return entity ID of this armor stand
*
* @return entity ID of this armor stand
*/
int getEntityId();
/**
* Spawns armor stand for specified player
* @param viewer - player to spawn for
*
* @param viewer
* player to spawn for
*/
void spawn(TabPlayer viewer);
@@ -1,35 +1,44 @@
package me.neznamy.tab.api;
/**
* Feature manager allows registration and work with features.
*/
public interface FeatureManager {
/**
* Registers a feature, which will start receiving events
*
* @param name - name of feature
* @param feature - the handler
* @param name
* name of feature
* @param feature
* the handler
*/
void registerFeature(String name, TabFeature feature);
/**
* Unregisters feature making it no longer receive events. This does not run unload method nor cancel
* tasks created by the feature
* @param name - feature name defined in registerFeature
*
* @param name
* feature name defined in registerFeature
*/
void unregisterFeature(String name);
/**
* Returns whether a feature with said name is registered or not
*
* @param name - name of feature defined in registerFeature method
* @return true if feature exists, false if not
* @param name
* name of feature defined in registerFeature method
* @return true if feature exists, false if not
*/
boolean isFeatureEnabled(String name);
/**
* Returns feature handler by its name
*
* @param name - name of feature defined in registerFeature method
* @return the feature or null if feature does not exist
* @param name
* name of feature defined in registerFeature method
* @return the feature or null if feature does not exist
*/
TabFeature getFeature(String name);
}
@@ -1,54 +1,68 @@
package me.neznamy.tab.api;
/**
* An interface representing a property assigned to a player,
* which may contain placeholders and can be changed using the API.
*/
public interface Property {
/**
* Returns original raw value ignoring API calls
* @return original raw value
*
* @return original raw value
*/
String getOriginalRawValue();
/**
* Temporarily overrides current raw value with an API call
* @param temporaryValue - temporary value to be assigned
*
* @param temporaryValue
* temporary value to be assigned
*/
void setTemporaryValue(String temporaryValue);
/**
* Returns current temporary value
* @return temporary value or null if not set
*
* @return temporary value or null if not set
*/
String getTemporaryValue();
/**
* Returns temporary value (via API) if present, raw value otherwise
* @return current raw value
*
* @return current raw value
*/
String getCurrentRawValue();
/**
* Returns last known value
* @return last known value
*
* @return last known value
*/
String get();
/**
* Replaces all placeholders in current raw value, colorizes it, removes remove-strings and returns whether value changed or not
* @return if updating changed value or not
*
* @return if updating changed value or not
*/
boolean update();
/**
* Replaces all placeholders in current raw value, colorizes it, removes remove-strings and returns it.
* Equal to calling update() and then get().
* @return updated value
*
* @return updated value
*/
String updateAndGet();
/**
* Returns value for defined viewer by applying relational placeholders to last known value
* @param viewer - the viewer
* @return format for the viewer
*
* @param viewer
* the viewer
* @return format for the viewer
*/
String getFormat(TabPlayer viewer);
}
@@ -1,30 +1,147 @@
package me.neznamy.tab.api;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
/**
* Interface representing user or group configuration.
* Implementation is either using file or MySQL, if it's enabled.
*/
public interface PropertyConfiguration {
void setProperty(String name, String property, String server, String world, String value);
/**
* Sets property value of group or user to specified value. If {@code world} or
* {@code server} is not {@code null}, value will be inserted as a per-world or
* per-server setting. If both {@code world} and {@code server} are {@code null},
* value is inserted as a global setting.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @param property
* Name of property to set
* @param server
* Name of server to apply setting to, {@code null} for global setting
* @param world
* Name of world to apply setting to, {@code null} for global setting
* @param value
* Value of the property
*/
void setProperty(String groupOrUser, String property, String server, String world, String value);
String[] getProperty(String name, String property, String server, String world);
/**
* Gets property of group or user. If {@code server} or {@code world}
* are not {@code null}, per-server / per-world settings are checked first
* to try to find a match. If no match is found, global value is returned.
* Returned value is an array with 2 elements, with first argument being value
* and second being source. If nothing is found, empty array is returned.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @param property
* Name of property to get
* @param server
* Server the player is currently in, to allow check for per-server settings
* @param world
* World the player is currently in, to allow check for per-world settings
* @return Array with 2 elements with value being first, source second if found,
* empty array if nothing was found.
*/
String[] getProperty(String groupOrUser, String property, String server, String world);
void remove(String name);
/**
* Removes all data applied to specified group or user.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
*/
void remove(String groupOrUser);
Map<String, String> getGlobalSettings(String name);
/**
* Returns map of global settings applied to specified group or user.
* Map key is property name, value is property value.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @return Map of global settings of specified group or user
*/
Map<String, Object> getGlobalSettings(String groupOrUser);
Map<String, Map<String, String>> getPerWorldSettings(String name);
/**
* Returns map of per-world settings of specified group or user.
* Map key is world name, value is property name-value map in that world.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @return Map of per-world settings of specified group or user
*/
Map<String, Map<String, Object>> getPerWorldSettings(String groupOrUser);
Map<String, Map<String, String>> getPerServerSettings(String name);
/**
* Returns map of per-server settings of specified group or user.
* Map key is server name, value is property name-value map in that server.
*
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @return Map of per-server settings of specified group or user
*/
Map<String, Map<String, Object>> getPerServerSettings(String groupOrUser);
/**
* Returns set of all groups or users that have anything configured,
* either globally or in some world or server.
*
* @return Set of all entries with something configured
*/
Set<String> getAllEntries();
default Map<String, Map<String, String>> convertMap(Map<String, Map<String, Map<String, String>>> map, String key) {
Map<String, Map<String, String>> converted = new HashMap<>();
for (Map.Entry<String, Map<String, Map<String, String>>> entry : map.entrySet()) {
converted.put(entry.getKey(), entry.getValue().get(key));
/**
* Converts per-world or per-server map into a per-world or per-server map
* with values only belonging to entered group or user. In other words, extracts
* settings belonging to group or user from the map.
*
* @param map
* Original per-world or per-server setting map
* @param groupOrUser
* Name of group or user, depending on what this instance handles
* @return Converted map only containing data of specified group or user
*/
default Map<String, Map<String, Object>> convertMap(Map<String, Map<String, Map<String, Object>>> map, String groupOrUser) {
Map<String, Map<String, Object>> converted = new HashMap<>();
for (Map.Entry<String, Map<String, Map<String, Object>>> entry : map.entrySet()) {
converted.put(entry.getKey(), entry.getValue().get(groupOrUser));
}
return converted;
}
/**
* Converts object into string. For most objects, {@link Object#toString()} is called.
* For lists, entries are connected with {@code "\n"}.
*
* @param obj
* Object to convert
* @return Converted string
*/
@SuppressWarnings("unchecked")
default String toString(Object obj) {
if (obj instanceof List) {
return ((List<Object>)obj).stream().map(Object::toString).collect(Collectors.joining("\n"));
}
return obj.toString();
}
/**
* Converts string into object. If string contains {@code "\n"},
* a list is created using {@code "\n"} as a separator. Otherwise,
* inserted string is returned.
*
* @param string
* String to convert
* @return Converted object
*/
default Object fromString(String string) {
if (string != null && string.contains("\n")) {
return Arrays.asList(string.split("\n"));
}
return string;
}
}
@@ -65,18 +65,20 @@ public enum ProtocolVersion {
/** Value array to iterate over to avoid array creations on each call */
public static final ProtocolVersion[] VALUES = values();
//version's network id found at https://wiki.vg/Protocol_version_numbers
/** Version's network id found at https://wiki.vg/Protocol_version_numbers */
private final int networkId;
//minor version, such as 16
/** Version's minor version, such as 16 for 1.16.x */
private int minorVersion;
//friendly name displayed in %player-version% placeholder
/** Version's friendly name displayed in %player-version% placeholder */
private final String friendlyName;
/**
* Constructs new instance with given network id
* @param networkId - network id of this version
*
* @param networkId
* network id of this version
*/
ProtocolVersion(int networkId){
this.networkId = networkId;
@@ -86,7 +88,9 @@ public enum ProtocolVersion {
/**
* Constructs new instance with given friendly name
* @param friendlyName - friendly name to display
*
* @param friendlyName
* friendly name to display
*/
ProtocolVersion(String friendlyName){
this.networkId = 999;
@@ -96,7 +100,8 @@ public enum ProtocolVersion {
/**
* Returns the version's network id
* @return version's network id
*
* @return version's network id
*/
public int getNetworkId() {
return networkId;
@@ -104,7 +109,8 @@ public enum ProtocolVersion {
/**
* Returns user-friendly name of the version (such as 1.16.2 instead of V1_16_2)
* @return user-friendly name of the version
*
* @return user-friendly name of the version
*/
public String getFriendlyName() {
return friendlyName;
@@ -112,7 +118,8 @@ public enum ProtocolVersion {
/**
* Returns minor version of this release, such as 8 for 1.8.x or 13 for 1.13.x
* @return version's minor version
*
* @return version's minor version
*/
public int getMinorVersion() {
return minorVersion;
@@ -122,8 +129,9 @@ public enum ProtocolVersion {
* Overrides minor version of the version instance. This is used internally to
* set minor version of UNKNOWN_SERVER_VERSION value to value from package to fix compatibility
* with server forks that set bukkit version field value to "Unknown".
* @param minorVersion
* minor version to change value to
*
* @param minorVersion
* minor version to change value to
*/
public void setMinorVersion(int minorVersion) {
this.minorVersion = minorVersion;
@@ -131,8 +139,10 @@ public enum ProtocolVersion {
/**
* Returns enum constant of entered version or UNKNOWN_SERVER_VERSION if unknown version
* @param friendlyName - friendly name of the version
* @return version or UNKNOWN_SERVER_VERSION if version is unknown
*
* @param friendlyName
* friendly name of the version
* @return version or UNKNOWN_SERVER_VERSION if version is unknown
*/
public static ProtocolVersion fromFriendlyName(String friendlyName) {
if (friendlyName.startsWith("1.8")) return V1_8;
@@ -145,8 +155,10 @@ public enum ProtocolVersion {
/**
* Returns version from given network id
* @param networkId - network id of protocol version
* @return version from given network id
*
* @param networkId
* network id of protocol version
* @return version from given network id
*/
public static ProtocolVersion fromNetworkId(int networkId) {
for (ProtocolVersion v : VALUES) {
+107 -17
View File
@@ -15,103 +15,193 @@ import me.neznamy.tab.api.team.TeamManager;
*/
public abstract class TabAPI {
/** Instance of the API */
private static TabAPI instance;
/**
* Returns API instance. If instance was not set by the plugin, throws
* {@code IllegalStateException}. This is usually caused by shading the API
* into own project, which is not allowed. Another option is calling the method
* before plugin was able to load.
*
* @return API instance
* @throws IllegalStateException
* If instance is {@code null}
*/
public static TabAPI getInstance() {
if (instance == null) throw new IllegalStateException("API instance is null. This likely means you shaded TAB's API into your project" +
" instead of only using it, which is not allowed.");
return instance;
}
/**
* 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
* @return player object from given UUID
* @param id - Player UUID
*
* @param id
* Player UUID
* @return player object from given UUID
*/
public abstract TabPlayer getPlayer(UUID id);
/**
* Returns player object from given name
* @return player object from given name
* @param name - Player name
*
* @param name
* Player name
* @return player object from given name
*/
public abstract TabPlayer getPlayer(String name);
/**
* Returns collection of all online players. Will return empty list if plugin is disabled (due to a broken configuration file for example).
* @return collection of online players
*
* @return collection of online players
*/
public abstract TabPlayer[] getOnlinePlayers();
/**
* Return BossBar manager instance if the feature is enabled. Returns null otherwise.
* @return BossBar manager
* Return BossBar manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return BossBar manager
*/
public abstract BossBarManager getBossBarManager();
/**
* Returns scoreboard manager instance if the feature is enabled. Returns null otherwise.
* @return scoreboard manager
* Returns scoreboard manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return scoreboard manager
*/
public abstract ScoreboardManager getScoreboardManager();
/**
* Returns team manager instance if the feature is enabled, false otherwise
* @return team manager
* Returns team manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return team manager
*/
public abstract TeamManager getTeamManager();
/**
* Returns header/footer manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return Header/footer manager
*/
public abstract HeaderFooterManager getHeaderFooterManager();
/**
* Returns PlaceholderManager instance
* @return PlaceholderManager instance
*
* @return PlaceholderManager instance
*/
public abstract PlaceholderManager getPlaceholderManager();
/**
* Returns feature manager instance
* @return feature manager instance
*
* @return feature manager instance
*/
public abstract FeatureManager getFeatureManager();
/**
* Returns Tablist name format manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return Tablist name format manager
*/
public abstract TablistFormatManager getTablistFormatManager();
/**
* Gets the event bus for registering listeners for TAB events.
*
* @return the event bus
* @return the event bus
*/
public abstract EventBus getEventBus();
/**
* Returns server version. On proxy installation returns PROXY.
* @return server version
*
* @return server version
*/
public abstract ProtocolVersion getServerVersion();
/**
* Prints message into console
* @param message - message to print
* @param translateColors - true if colors should be translated, false if not
*
* @param message
* message to print
* @param translateColors
* {@code true} if colors should be translated, {@code false} if not
*/
public abstract void sendConsoleMessage(String message, boolean translateColors);
/**
* Returns TAB's Thread manager, which allows task submitting
*
* @return ThreadManager instance
*/
public abstract ThreadManager getThreadManager();
/**
* Returns TAB's cache file used to store player toggle data
*
* @return TAB's player cache file
*/
public abstract ConfigurationFile getPlayerCache();
/**
* Returns TAB's config.yml file
*
* @return config.yml file
*/
public abstract ConfigurationFile getConfig();
/**
* Returns TAB's group configuration
*
* @return TAB's group configuration
*/
public abstract PropertyConfiguration getGroups();
/**
* Returns TAB's user configuration
*
* @return TAB's user configuration
*/
public abstract PropertyConfiguration getUsers();
/**
* Sends a debug message into console if the option
* is enabled in config.
*
* @param message
* Message to send
*/
public abstract void debug(String message);
/**
* Logs an error into errors.log file
*
* @param message
* Error message
* @param t
* Thrown error
*/
public abstract void logError(String message, Throwable t);
/**
* Sets name of file with syntax error, which prevented
* the plugin from enabling. Internal use only.
*
* @param file
* Name of file with syntax error
*/
public abstract void setBrokenFile(String file);
}
@@ -6,38 +6,53 @@ import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
/**
* Abstract class representing a core feature of the plugin.
* <p>
* It receives all kinds of events and can react to them.
*/
public abstract class TabFeature {
/** Feature's name displayed in /tab cpu */
private final String featureName;
/** Feature's function name displayed in place of refreshing in /tab cpu */
private final String refreshDisplayName;
protected final String[] disabledServers;
private final boolean serverWhitelistMode;
protected final String[] disabledWorlds;
private final boolean worldWhitelistMode;
/** Servers where the feature is disabled (or enabled if using whitelist mode) */
protected String[] disabledServers = new String[0];
/** Flag tracking whether disabled server list is a whitelist or blacklist */
private boolean serverWhitelistMode = false;
/** Worlds where the feature is disabled (or enabled if using whitelist mode) */
protected String[] disabledWorlds = new String[0];
/** Flag tracking whether disabled world list is a whitelist or blacklist */
private boolean worldWhitelistMode = false;
/** Players located in currently disabled worlds / servers */
private final Set<TabPlayer> disabledPlayers = Collections.newSetFromMap(new WeakHashMap<>());
/**
* List of methods overridden (implemented) by each feature.
* If a method is not overridden, it is not called at all.
* This avoids a massive spam for every single method
* in every single feature in /tab cpu output
*/
private final List<String> methodOverrides = new ArrayList<>();
/**
* Constructs new instance with given parameters and loads method overrides
*
* @param featureName
* Feature's name in /tab cpu
* @param refreshDisplayName
* "refreshing" cpu display type name of the feature
*/
protected TabFeature(String featureName, String refreshDisplayName) {
this(featureName, refreshDisplayName, null, null);
}
protected TabFeature(String featureName, String refreshDisplayName, List<String> disabledServers, List<String> disabledWorlds) {
this.featureName = featureName;
this.refreshDisplayName = refreshDisplayName;
if (disabledServers != null) {
this.disabledServers = disabledServers.toArray(new String[0]);
serverWhitelistMode = disabledServers.contains("WHITELIST");
} else {
this.disabledServers = new String[0];
serverWhitelistMode = false;
}
if (disabledWorlds != null) {
this.disabledWorlds = disabledWorlds.toArray(new String[0]);
worldWhitelistMode = disabledWorlds.contains("WHITELIST");
} else {
this.disabledWorlds = new String[0];
worldWhitelistMode = false;
}
try {
if (getClass().getMethod("onCommand", TabPlayer.class, String.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onCommand");
@@ -71,7 +86,34 @@ public abstract class TabFeature {
}
/**
* Loads all players and sends packets
* Constructs new instance with given parameters and loads method overrides.
* Also loads lists of disabled worlds and servers of this feature with config
* section path specified with {@code configSection} parameter.
*
* @param featureName
* Feature's name in /tab cpu
* @param refreshDisplayName
* "refreshing" cpu display type name of the feature
* @param configSection
* Configuration section of the feature to load disabled
* servers / worlds from
*/
protected TabFeature(String featureName, String refreshDisplayName, String configSection) {
this(featureName, refreshDisplayName);
List<String> disabledServers = TabAPI.getInstance().getConfig().getStringList(configSection + ".disable-in-servers");
List<String> disabledWorlds = TabAPI.getInstance().getConfig().getStringList(configSection + ".disable-in-worlds");
if (disabledServers != null) {
this.disabledServers = disabledServers.toArray(new String[0]);
serverWhitelistMode = disabledServers.contains("WHITELIST");
}
if (disabledWorlds != null) {
this.disabledWorlds = disabledWorlds.toArray(new String[0]);
worldWhitelistMode = disabledWorlds.contains("WHITELIST");
}
}
/**
* Loads all online players and sends packets
*/
public void load() {
//empty by default
@@ -85,10 +127,13 @@ public abstract class TabFeature {
}
/**
* Processes command from player
* @param sender - command sender
* @param message - command line
* @return true if event should be cancelled, false if not
* Processes command from player. This is typically a toggle command.
*
* @param sender
* command sender
* @param message
* command line
* @return {@code true} if event should be cancelled, {@code true} if not
*/
public boolean onCommand(TabPlayer sender, String message) {
return false;
@@ -96,7 +141,9 @@ public abstract class TabFeature {
/**
* Processes join event
* @param connectedPlayer - player who connected
*
* @param connectedPlayer
* player who connected
*/
public void onJoin(TabPlayer connectedPlayer) {
//empty by default
@@ -104,16 +151,23 @@ public abstract class TabFeature {
/**
* Processes quit event
* @param disconnectedPlayer - player who disconnected
*
* @param disconnectedPlayer
* player who disconnected
*/
public void onQuit(TabPlayer disconnectedPlayer) {
//empty by default
}
/**
* Processes world switch
* @param changed - player who switched world
* @param from - world player changed from
* @param to - world player changed to
*
* @param changed
* player who switched world
* @param from
* world player changed from
* @param to
* world player changed to
*/
public void onWorldChange(TabPlayer changed, String from, String to) {
//empty by default
@@ -121,9 +175,13 @@ public abstract class TabFeature {
/**
* Processes server switch
* @param changed - player who switched server
* @param from - server player changed from
* @param to - server player changed to
*
* @param changed
* player who switched server
* @param from
* server player changed from
* @param to
* server player changed to
*/
public void onServerChange(TabPlayer changed, String from, String to) {
//empty by default
@@ -131,8 +189,11 @@ public abstract class TabFeature {
/**
* Processes the packet send and returns true if packet should be cancelled
* @param receiver - player receiving packet
* @param packet - received packet
*
* @param receiver
* player receiving packet
* @param packet
* received packet
*/
public void onDisplayObjective(TabPlayer receiver, PacketPlayOutScoreboardDisplayObjective packet) {
//empty by default
@@ -140,7 +201,9 @@ public abstract class TabFeature {
/**
* Processes login packet, only available on BungeeCord
* @param packetReceiver - player receiving client reset packet
*
* @param packetReceiver
* player receiving client reset packet
*/
public void onLoginPacket(TabPlayer packetReceiver) {
//empty by default
@@ -148,8 +211,11 @@ public abstract class TabFeature {
/**
* Processes the packet send
* @param receiver - player receiving packet
* @param packet - received packet
*
* @param receiver
* player receiving packet
* @param packet
* received packet
*/
public void onObjective(TabPlayer receiver, PacketPlayOutScoreboardObjective packet) {
//empty by default
@@ -157,8 +223,11 @@ public abstract class TabFeature {
/**
* Processes the packet send and possibly modifies it
* @param receiver - player receiving packet
* @param info - received packet
*
* @param receiver
* player receiving packet
* @param info
* received packet
*/
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
//empty by default
@@ -166,11 +235,14 @@ public abstract class TabFeature {
/**
* Processes raw packet sent by client
* @param sender - packet sender
* @param packet - packet received
* @return true if false should be cancelled, false if not
* @throws ReflectiveOperationException
* if thrown by reflective operation
*
* @param sender
* packet sender
* @param packet
* packet received
* @return true if false should be cancelled, false if not
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
return false;
@@ -178,10 +250,13 @@ public abstract class TabFeature {
/**
* Processes raw packet sent to client
* @param receiver - packet receiver
* @param packet - the packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*
* @param receiver
* packet receiver
* @param packet
* the packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
//empty by default
@@ -189,20 +264,31 @@ public abstract class TabFeature {
/**
* Performs refresh of specified player
* @param refreshed - player to refresh
* @param force - if refresh should be forced despite refresh seemingly not needed
*
* @param refreshed
* player to refresh
* @param force
* if refresh should be forced despite refresh seemingly not needed
*/
public void refresh(TabPlayer refreshed, boolean force) {
//empty by default
}
/**
* Processes vanish status change of player
*
* @param player
* Player who changed vanish status
*/
public void onVanishStatusChange(TabPlayer player){
//empty by default
}
/**
* Registers this feature as one using specified placeholders
* @param placeholders - placeholders to add as used in this feature
*
* @param placeholders
* placeholders to add as used in this feature
*/
public void addUsedPlaceholders(Collection<String> placeholders) {
if (placeholders.isEmpty()) return;
@@ -210,10 +296,13 @@ public abstract class TabFeature {
}
/**
* Returns true if world or server is disabled, false if not
* @param world - world to check
* @param server - server to check
* @return true if feature should be disabled, false if not
* Returns {@code true} if world or server is disabled, {@code false} if not
*
* @param server
* server to check
* @param world
* world to check
* @return {@code true} if feature should be disabled, {@code false} if not
*/
public boolean isDisabled(String server, String world) {
boolean contains = contains(disabledWorlds, world);
@@ -224,6 +313,17 @@ public abstract class TabFeature {
return contains;
}
/**
* Returns {@code true} if list contains the specified element or element
* ends with {@code "*"} and element meeting that requirement is present,
* {@code false} otherwise.
*
* @param list
* List to check
* @param element
* Element to find
* @return {@code true} if element was found, {@code false} if not
*/
protected boolean contains(String[] list, String element) {
if (element == null) return false;
for (String s : list) {
@@ -238,30 +338,67 @@ public abstract class TabFeature {
/**
* Returns name of the feature displayed in /tab cpu.
* @return name of the feature displayed in /tab cpu
*
* @return name of the feature displayed in /tab cpu
*/
public String getFeatureName() {
return featureName;
}
/**
* Returns text to display in /tab cpu of this feature refreshing visuals
*
* @return Text to display as refreshing of this feature
*/
public String getRefreshDisplayName() {
return refreshDisplayName;
}
/**
* Returns {@code true} if method with specified name is overridden
* in the implementation, {@code false} if not
*
* @param method
* Method to check
* @return {@code true} if overridden, {@code false} if not
*/
public boolean overridesMethod(String method) {
return methodOverrides.contains(method);
}
/**
* Returns {@code true} if player is currently located in a server
* or world, which is marked as disabled, {@code false} if not
*
* @param p
* Player to check
* @return {@code true} if player is in disabled server / world,
* {@code false} if not
*/
public boolean isDisabledPlayer(TabPlayer p) {
return disabledPlayers.contains(p);
}
/**
* Adds specified player into list of players in disabled
* servers / worlds.
*
* @param p
* Player to add
*/
public void addDisabledPlayer(TabPlayer p) {
if (disabledPlayers.contains(p)) return;
disabledPlayers.add(p);
}
/**
* Removes specified player from list of players in disabled
* servers / worlds
*
* @param p
* Player to check
*/
public void removeDisabledPlayer(TabPlayer p) {
disabledPlayers.remove(p);
}
public String getRefreshDisplayName() {
return refreshDisplayName;
}
}
@@ -19,225 +19,295 @@ public interface TabPlayer {
/**
* Returns player's name
* @return Player's name
*
* @return Player's name
*/
String getName();
/**
* Returns player's UUID
* @return Player's UUID
*
* @return Player's UUID
*/
UUID getUniqueId();
/**
* Returns player's uuid used in TabList. This may only be different from real uuid if
* TAB is installed on velocity with some specific velocity setup
* @return player's uuid in TabList
*
* @return player's uuid in TabList
*/
UUID getTablistUUID();
/**
* Returns player's protocol version
* @return player's protocol version
*
* @return player's protocol version
*/
ProtocolVersion getVersion();
/**
* Returns platform-specific entity
* @return an instance of bukkit/bungee player depending on platform
*
* @return an instance of bukkit/bungee player depending on platform
*/
Object getPlayer();
/**
* Returns player's current world name (on BungeeCord this requires bridge installed)
* @return name of world where player is currently in, "N/A" on BungeeCord if bridge is not installed
*
* @return name of world where player is currently in, "N/A" on BungeeCord if bridge is not installed
*/
String getWorld();
/**
* Returns player's current server name on BungeeCord (on bukkit this returns "N/A")
* @return name of server where player is currently in, "N/A" on bukkit
*
* @return name of server where player is currently in, "N/A" on bukkit
*/
String getServer();
/**
* Performs platform-specific API call to check for permission and returns the result
* @param permission - the permission to check for
* @return - true if player has permission, false if not
*
* @param permission
* the permission to check for
* @return true if player has permission, false if not
*/
boolean hasPermission(String permission);
/**
* Sends the player a custom universal packet
* @param packet - packet to send
*
* @param packet
* packet to send
*/
void sendCustomPacket(TabPacket packet);
/**
* Sends the player a custom universal packet and adds that packet into counter that
* is displayed in /tab cpu
* @param packet - packet to send
* @param feature - feature to increment sent packet counter of
*
* @param packet
* packet to send
* @param feature
* feature to increment sent packet counter of
*/
void sendCustomPacket(TabPacket packet, TabFeature feature);
/**
* Sends the player a custom universal packet and adds that packet into counter that
* is displayed in /tab cpu
* @param packet - packet to send
* @param feature - feature to increment sent packet counter of
*
* @param packet
* packet to send
* @param feature
* feature to increment sent packet counter of
*/
void sendCustomPacket(TabPacket packet, String feature);
/**
* Sends the player a platform-specific packet
* @param packet - an instance of packet depending on platform
*
* @param packet
* an instance of packet depending on platform
*/
void sendPacket(Object packet);
/**
* Sends the player a platform-specific packet and adds that packet into counter that
* is displayed in /tab cpu
* @param packet - an instance of packet depending on platform
* @param feature - feature to increment sent packet counter of
*/
void sendPacket(Object packet, TabFeature feature);
/**
* Sends the player a platform-specific packet and adds that packet into counter that
* is displayed in /tab cpu
* @param packet - an instance of packet depending on platform
* @param feature - feature to increment sent packet counter of
*/
void sendPacket(Object packet, String feature);
/**
* Returns player's property by name
* @param name - name of property
* @return the property or null if not found
*
* @param name
* name of property
* @return the property or {@code null} if not found
*/
Property getProperty(String name);
/**
* Sends a message to the player
* @param message - message to be sent
* @param translateColors - whether colors should be translated or not
*
* @param message
* message to be sent
* @param translateColors
* whether colors should be translated or not
*/
void sendMessage(String message, boolean translateColors);
/**
* Sends specified component as a chat message
* @param message - message to send
*
* @param message
* message to send
*/
void sendMessage(IChatBaseComponent message);
/**
* Returns player's primary permission group
* @return player's group depending on configuration
*/
String getGroup();
/**
* Returns player's channel or null if server is 1.7 or older
* @return player's channel
* Returns player's netty channel. On servers running version 1.7 or older returns {@code null}.
*
* @return player's channel
*/
Channel getChannel();
/**
* Returns player's ping calculated by server
* @return player's ping
*
* @return player's ping
*/
int getPing();
/**
* Returns player's skin data
* @return player's skin
*
* @return player's skin
*/
Skin getSkin();
/**
* Returns true once the player is successfully loaded (onJoin method ran through all methods)
* @return true if player is fully loaded, false otherwise
*
* @return {@code true} if player is fully loaded, {@code false} otherwise
*/
boolean isLoaded();
/**
* Sets property with specified name to new value. If property did not exist before, it is
* created and true is returned. If it existed, it is overridden and true is returned. False is returned otherwise.
* @param feature - feature using this property to get placeholders registered
* @param identifier - property name
* @param rawValue - new raw value
* @return true if value changed / did not exist, false if value did not change
* created and {@code true} is returned. If it existed, it is overridden and {@code true} is returned.
* {@code false} is returned otherwise.
*
* @param feature
* feature using this property to get placeholders registered
* @param identifier
* property name
* @param rawValue
* new raw value
* @return {@code true} if value changed / did not exist, {@code false} if value did not change
*/
boolean setProperty(TabFeature feature, String identifier, String rawValue);
/**
* Loads property from config using standard property loading algorithm
* @param property - property name to load
* @return true if value did not exist or changed, false otherwise
*
* @param property
* property name to load
* @return {@code true} if value did not exist or changed, {@code false} otherwise
*/
boolean loadPropertyFromConfig(TabFeature feature, String property);
/**
* Loads property from config using standard property loading algorithm. If the property is
* not set in config, sets it to ifNotSet value
* @param property - property name to load
* @param ifNotSet - value to use if property is not defined in config
* @return true if value did not exist or changed, false otherwise
* not set in config, {@code ifNotSet} value is used.
*
* @param property
* property name to load
* @param ifNotSet
* value to use if property is not defined in config
* @return {@code true} if value did not exist or changed, {@code false} otherwise
*/
boolean loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
/**
* Returns name of player's scoreboard team or null if NameTag feature is disabled
* @return name of player's team
* Returns name of player's scoreboard team or {@code null} if NameTag feature is disabled
*
* @return name of player's team
*/
String getTeamName();
/**
* Returns user-friendly explanation of team name
* @return explanation behind team name
*
* @return explanation behind team name
*/
String getTeamNameNote();
/**
* Returns true if player is disguised using LibsDisguises
* @return true if player is disguised, false if not
* Returns {@code true} if player is disguised using LibsDisguises, {@code false} if not
*
* @return {@code true} if player is disguised, {@code false} if not
*/
boolean isDisguised();
/**
* Returns true if player has invisibility potion, false if not. For bukkit, API is used, for BungeeCord
* bukkit bridge is used
* @return true if player has invisibility potion, false if not
* Returns {@code true} if player has invisibility potion, {@code false} if not.
* For bukkit, API is used, for BungeeCord, bridge is used.
*
* @return {@code true} if player has invisibility potion, {@code false} if not
*/
boolean hasInvisibilityPotion();
/**
* Currently only PV hook on BungeeCord for global PlayerList function. Looking to expand this
* in the future
* @return true if vanished with PV on BungeeCord
* Checks if player is vanished using any supported vanish plugin and returns the result.
*
* @return {@code true} if player is vanished, {@code false} if not
*/
boolean isVanished();
/**
* Returns true if player is online according to server
* @return true if online, false if not
* Calls platform-specific method and returns the result
*
* @return {@code true} if player is online, {@code false} if not
*/
boolean isOnline();
/**
* Returns GameMode of the player (0 for survival, 1 creative, 2 adventure, 3 spectator)
* @return GameMode of the player
*
* @return GameMode of the player
*/
int getGamemode();
/**
* Returns {@code true} if this is a bedrock player, {@code false} if not
*
* @return {@code true} if player is a bedrock player, {@code false} if not
*/
boolean isBedrockPlayer();
/**
* Returns player's primary permission group. If group has been changed using
* {@link #setTemporaryGroup(String)}, returns that value. Otherwise, returns group
* detected by standard group assign logic
*
* @return player's primary permission group
*/
String getGroup();
/**
* Temporarily overrides player's group and applies all changes coming from new group.
* This includes all properties and sorting, if used.
*
* @param group
* New group to use
* @see #hasTemporaryGroup()
* @see #resetTemporaryGroup()
*/
void setTemporaryGroup(String group);
/**
* Returns temporary group applied to the player using {@link #setTemporaryGroup(String)}.
* If no group was set, returns {@code null}
*
* @return Temporary group assigned to player or {@code null} if not set
* @see #setTemporaryGroup(String)
* @see #resetTemporaryGroup()
*/
boolean hasTemporaryGroup();
/**
* Resets temporary group assigned using {@link #setTemporaryGroup(String)}.
* If no temporary group is set, doesn't do anything.
*
* @see #setTemporaryGroup(String)
* @see #hasTemporaryGroup()
*/
void resetTemporaryGroup();
/**
* Returns player's game profile name as seen by other players. By default,
* this returns player's name. If using a nickname / disguise plugin that changes
* game profile name for other players and change was successfully detected, returns
* that value.
*
* @return player's game profile name as seen by other players
*/
String getNickname();
}
@@ -1,29 +1,168 @@
package me.neznamy.tab.api;
/**
* An interface allowing to modify tablist display names
* of players.
* <p>
* Instance can be obtained using {@link TabAPI#getTablistFormatManager()}.
* This requires the feature to be enabled in config, otherwise the method will
* return {@code null}.
*/
public interface TablistFormatManager {
/**
* Changes player's prefix to provided value. Supports placeholders,
* as well as any supported RGB formats.
*
* @param player
* player to change prefix of
* @param prefix
* new prefix value
* @see #resetPrefix(TabPlayer)
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
void setPrefix(TabPlayer player, String prefix);
/**
* Changes player's name to provided value. Supports placeholders,
* as well as any supported RGB formats.
*
* @param player
* player to change prefix of
* @param customName
* new customName value
* @see #resetName(TabPlayer)
* @see #getCustomName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
void setName(TabPlayer player, String customName);
/**
* Changes player's suffix to provided value. Supports placeholders,
* as well as any supported RGB formats.
*
* @param player
* player to change prefix of
* @param suffix
* new suffix value
* @see #resetSuffix(TabPlayer)
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
void setSuffix(TabPlayer player, String suffix);
/**
* Resets player's prefix previously assigned using {@link #setPrefix(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset prefix of
* @see #setPrefix(TabPlayer, String)
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
void resetPrefix(TabPlayer player);
/**
* Resets player's custom name previously assigned using {@link #setName(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset name of
* @see #setName(TabPlayer, String)
* @see #getCustomName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
void resetName(TabPlayer player);
/**
* Resets player's suffix previously assigned using {@link #setSuffix(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset suffix of
* @see #setSuffix(TabPlayer, String)
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
void resetSuffix(TabPlayer player);
/**
* Returns prefix assigned using {@link #setPrefix(TabPlayer, String)} method.
* If the method was not used, {@code null} is returned.
*
* @param player
* player to get custom prefix of
* @return player's custom prefix or {@code null} if not defined
* @see #setPrefix(TabPlayer, String)
* @see #resetPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
String getCustomPrefix(TabPlayer player);
/**
* Returns custom name assigned using {@link #setName(TabPlayer, String)} method.
* If the method was not used, {@code null} is returned.
*
* @param player
* player to get custom name of
* @return player's custom name or {@code null} if not defined
* @see #setName(TabPlayer, String)
* @see #resetName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
String getCustomName(TabPlayer player);
/**
* Returns suffix assigned using {@link #setSuffix(TabPlayer, String)} method.
* If the method was not used, {@code null} is returned.
*
* @param player
* player to get custom suffix of
* @return player's custom suffix or {@code null} if not defined
* @see #setSuffix(TabPlayer, String)
* @see #resetSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
String getCustomSuffix(TabPlayer player);
/**
* Returns player's original prefix applied using plugin's internal logic.
* This value is not affected by API calls in any way.
*
* @param player
* player to get original prefix of
* @return player's original prefix
* @see #setPrefix(TabPlayer, String)
* @see #resetPrefix(TabPlayer)
* @see #getCustomPrefix(TabPlayer)
*/
String getOriginalPrefix(TabPlayer player);
/**
* Returns player's original name applied using plugin's internal logic.
* This value is not affected by API calls in any way.
*
* @param player
* player to get original name of
* @return player's original name
* @see #setName(TabPlayer, String)
* @see #resetName(TabPlayer)
* @see #getCustomName(TabPlayer)
*/
String getOriginalName(TabPlayer player);
/**
* Returns player's original suffix applied using plugin's internal logic.
* This value is not affected by API calls in any way.
*
* @param player
* player to get original suffix of
* @return player's original suffix
* @see #setSuffix(TabPlayer, String)
* @see #resetSuffix(TabPlayer)
* @see #getCustomSuffix(TabPlayer)
*/
String getOriginalSuffix(TabPlayer player);
}
@@ -19,8 +19,8 @@ public enum BarStyle {
/**
* Initializes enum constant with given bukkit name
*
* @param bukkitName
* Name of color to be returned in {@code getBukkitName()}
* @param bukkitName
* Name of color to be returned in {@code getBukkitName()}
*/
BarStyle(String bukkitName){
this.bukkitName = bukkitName;
@@ -28,7 +28,7 @@ public enum BarStyle {
/**
* Returns name of this style in Bukkit API
* @return name of this style in Bukkit API
* @return name of this style in Bukkit API
*/
public String getBukkitName() {
return bukkitName;
@@ -23,14 +23,16 @@ public interface BossBar {
* Returns name of this BossBar. If it was defined in config, returns
* the name specified in config. If it was made using the API, returns
* randomly generated ID given to this BossBar when creating.
* @return name of BossBar
*
* @return name of BossBar
*/
String getName();
/**
* Returns randomly generated uuid of this BossBar used internally to match
* the BossBar in client with packets or with Bukkit API BossBar instances.
* @return uuid of this BossBar
*
* @return uuid of this BossBar
*/
UUID getUniqueId();
@@ -43,8 +45,8 @@ public interface BossBar {
* <p>
* If specified title is equal to the current one, nothing happens.
*
* @param title
* New title to display in this BossBar
* @param title
* New title to display in this BossBar
*/
void setTitle(String title);
@@ -59,8 +61,8 @@ public interface BossBar {
* <p>
* If specified progress is equal to the current one, nothing happens.
*
* @param progress
* New progress to use in this BossBar
* @param progress
* New progress to use in this BossBar
*/
void setProgress(String progress);
@@ -68,8 +70,9 @@ public interface BossBar {
* Changes BossBar progress to specified value ranging from 0 to 100.
* <p>
* If specified progress is equal to the current one, nothing happens.
* @param progress
* New progress to use in this BossBar
*
* @param progress
* New progress to use in this BossBar
*/
void setProgress(float progress);
@@ -84,8 +87,8 @@ public interface BossBar {
* <p>
* If specified color is equal to the current one, nothing happens.
*
* @param color
* New color to use in this BossBar
* @param color
* New color to use in this BossBar
*/
void setColor(String color);
@@ -94,8 +97,8 @@ public interface BossBar {
* <p>
* If specified color is equal to the current one, nothing happens.
*
* @param color
* New color to use in this BossBar
* @param color
* New color to use in this BossBar
*/
void setColor(BarColor color);
@@ -110,8 +113,8 @@ public interface BossBar {
* <p>
* If specified style is equal to the current one, nothing happens.
*
* @param style
* New style to use in this BossBar
* @param style
* New style to use in this BossBar
*/
void setStyle(String style);
@@ -120,36 +123,40 @@ public interface BossBar {
* <p>
* If specified style is equal to the current one, nothing happens.
*
* @param style
* New style to use in this BossBar
* @param style
* New style to use in this BossBar
*/
void setStyle(BarStyle style);
/**
* Returns current title of the BossBar in raw format. If it contains placeholders,
* their raw identifiers are used in the result.
* @return title of the BossBar
*
* @return title of the BossBar
*/
String getTitle();
/**
* Returns progress of the BossBar as a string, which is either entered string
* containing placeholders or entered number converted to string
* @return entered progress as a string
*
* @return entered progress as a string
*/
String getProgress();
/**
* Returns color of the BossBar as a string, which is either entered string
* containing placeholders or entered enum value converted to string
* @return entered color as a string
*
* @return entered color as a string
*/
String getColor();
/**
* Returns style of the BossBar as a string, which is either entered string
* containing placeholders or entered enum value converted to string
* @return entered style as a string
*
* @return entered style as a string
*/
String getStyle();
@@ -158,8 +165,8 @@ public interface BossBar {
* <p>
* If the player already sees this BossBar, nothing happens.
*
* @param player
* Player to register this BossBar to
* @param player
* Player to register this BossBar to
*/
void addPlayer(TabPlayer player);
@@ -168,8 +175,8 @@ public interface BossBar {
* <p>
* If the player does not see this BossBar, nothing happens.
*
* @param player
* Player to unregister this BossBar from
* @param player
* Player to unregister this BossBar from
*/
void removePlayer(TabPlayer player);
@@ -179,15 +186,17 @@ public interface BossBar {
* The returned list can only be used to read. Writing
* will not work properly. For adding/removing players see {@link #addPlayer(TabPlayer)}
* and {@link #removePlayer(TabPlayer)}.
* @return List of players seeing this BossBar
*
* @return List of players seeing this BossBar
*/
List<TabPlayer> getPlayers();
/**
* Returns {@code true} if specified player can see this BossBar, {@code false} if not
* @param player
* Player to check for
* @return {@code true} if contains, {@code false} if not
*
* @param player
* Player to check for
* @return {@code true} if contains, {@code false} if not
*/
boolean containsPlayer(TabPlayer player);
}
@@ -4,15 +4,16 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
/**
* An interface allowing work with BossBars such as creating, sending
* and toggling.
* <p>
* Instance can be obtained using {@link me.neznamy.tab.api.TabAPI#getBossBarManager()}.
* Instance can be obtained using {@link TabAPI#getBossBarManager()}.
* This requires the BossBar feature to be enabled in config, otherwise the method will
* return null.
* return {@code null}.
*/
public interface BossBarManager {
@@ -20,18 +21,15 @@ public interface BossBarManager {
* Creates BossBar with specified parameters and registers it to BossBar manager,
* allowing to retrieve it later.
*
* @param title
* Title to display, supports placeholders
*
* @param progress
* Progress to use. Must be between 0 and 100
*
* @param color
* Color to use
*
* @param style
* Style to use
* @return The newly created BossBar
* @param title
* Title to display, supports placeholders
* @param progress
* Progress to use. Must be between 0 and 100
* @param color
* Color to use
* @param style
* Style to use
* @return The newly created BossBar
*/
BossBar createBossBar(String title, float progress, BarColor color, BarStyle style);
@@ -39,21 +37,18 @@ public interface BossBarManager {
* Creates BossBar with specified parameters and registers it to BossBar manager,
* allowing to retrieve it later.
*
* @param title
* Title to display, supports placeholders
*
* @param progress
* Progress to use. Must be a string version of a number, or
* a placeholder that returns a number
*
* @param color
* Color to use. Must be a string version of one of enum constants, or
* a placeholder that returns one of them
*
* @param style
* Style to use. Must be a string version of one of enum constants, or
* a placeholder that returns one of them
* @return The newly created BossBar
* @param title
* Title to display, supports placeholders
* @param progress
* Progress to use. Must be a string version of a number, or
* a placeholder that returns a number
* @param color
* Color to use. Must be a string version of one of enum constants, or
* a placeholder that returns one of them
* @param style
* Style to use. Must be a string version of one of enum constants, or
* a placeholder that returns one of them
* @return The newly created BossBar
*/
BossBar createBossBar(String title, String progress, String color, String style);
@@ -63,9 +58,9 @@ public interface BossBarManager {
* retrieved using {@link me.neznamy.tab.api.bossbar.BossBar#getName()}.
* Returns null if no such BossBar exists.
*
* @param name
* Name of registered BossBar
* @return BossBar with specified name
* @param name
* Name of registered BossBar
* @return BossBar with specified name
*/
BossBar getBossBar(String name);
@@ -74,9 +69,9 @@ public interface BossBarManager {
* BossBar is created and used in packets / to match which the one from Bukkit API.
* Returns null if no such BossBar exists.
*
* @param id
* UUID to return BossBar by
* @return BossBar with specified uuid
* @param id
* UUID to return BossBar by
* @return BossBar with specified uuid
*/
BossBar getBossBar(UUID id);
@@ -86,31 +81,31 @@ public interface BossBarManager {
* <p>
* Map key is BossBar's name available in {@link me.neznamy.tab.api.bossbar.BossBar#getName()},
* map value is the BossBar interface.
* @return all registered BossBars
*
* @return all registered BossBars
*/
Map<String, BossBar> getRegisteredBossBars();
/**
* Toggles BossBar for this player and sends toggle message if {@code sendToggleMessage} is true.
*
* @param player
* Player to toggle BossBar for
*
* @param sendToggleMessage
* {@code true} if toggle message should be sent, {@code false} if not
* @see #hasBossBarVisible(TabPlayer)
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
* @param player
* Player to toggle BossBar for
* @param sendToggleMessage
* {@code true} if toggle message should be sent, {@code false} if not
* @see #hasBossBarVisible(TabPlayer)
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
*/
void toggleBossBar(TabPlayer player, boolean sendToggleMessage);
/**
* Returns true if player can see BossBars, false if toggled via command or API.
*
* @param player
* Player to check toggle status of
* @return {@code true} if visible, {@code false} if disabled (toggled)
* @see #toggleBossBar(TabPlayer, boolean)
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
* @param player
* Player to check toggle status of
* @return {@code true} if visible, {@code false} if disabled (toggled)
* @see #toggleBossBar(TabPlayer, boolean)
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
*/
boolean hasBossBarVisible(TabPlayer player);
@@ -118,12 +113,12 @@ public interface BossBarManager {
* Sets BossBar visibility of player to set value. If value did not change, nothing happens.
* If visibility changed, toggle message is sent if {@code sendToggleMessage} is {@code true}.
*
* @param player
* Player to set BossBar visibility of
* @param visible
* {@code true} if BossBar should be visible, {@code false} if not
* @param sendToggleMessage
* {@code true} if toggle message should be sent if value changed, {@code false} if not
* @param player
* Player to set BossBar visibility of
* @param visible
* {@code true} if BossBar should be visible, {@code false} if not
* @param sendToggleMessage
* {@code true} if toggle message should be sent if value changed, {@code false} if not
* @see #toggleBossBar(TabPlayer, boolean)
* @see #hasBossBarVisible(TabPlayer)
*/
@@ -131,37 +126,40 @@ public interface BossBarManager {
/**
* Temporarily displays registered BossBar to player for specified amount of time in milliseconds.
* @param player
* Player to show BossBar to
* @param bossBar
* Name of registered BossBar to show
* @param duration
* In milliseconds for how long should BossBar be displayed
* @throws IllegalArgumentException
* if no BossBar with specified name exists
* @see #announceBossBar(String, int)
* @see #getAnnouncedBossBars()
*
* @param player
* Player to show BossBar to
* @param bossBar
* Name of registered BossBar to show
* @param duration
* In milliseconds for how long should BossBar be displayed
* @throws IllegalArgumentException
* if no BossBar with specified name exists
* @see #announceBossBar(String, int)
* @see #getAnnouncedBossBars()
*/
void sendBossBarTemporarily(TabPlayer player, String bossBar, int duration);
/**
* Temporarily displays registered BossBar to all players for specified amount of time in milliseconds.
* @param bossBar
* Name of registered BossBar to show
* @param duration
* In milliseconds for how long should BossBar be displayed
* @throws IllegalArgumentException
* if no BossBar with specified name exists
* @see #sendBossBarTemporarily(TabPlayer, String, int)
* @see #getAnnouncedBossBars()
*
* @param bossBar
* Name of registered BossBar to show
* @param duration
* In milliseconds for how long should BossBar be displayed
* @throws IllegalArgumentException
* if no BossBar with specified name exists
* @see #sendBossBarTemporarily(TabPlayer, String, int)
* @see #getAnnouncedBossBars()
*/
void announceBossBar(String bossBar, int duration);
/**
* Returns list of BossBars which are currently being announced.
* @return list of currently active BossBar announcements
* @see #sendBossBarTemporarily(TabPlayer, String, int)
* @see #announceBossBar(String, int)
*
* @return list of currently active BossBar announcements
* @see #sendBossBarTemporarily(TabPlayer, String, int)
* @see #announceBossBar(String, int)
*/
List<BossBar> getAnnouncedBossBars();
}
@@ -15,12 +15,13 @@ public class ChatClickable {
/**
* Constructs new instance with given action and value.
* @param action
* click event action
* @param value
* click event value
* @throws IllegalArgumentException
* if {@code action} is null or {@code value} is null
*
* @param action
* click event action
* @param value
* click event value
* @throws IllegalArgumentException
* if {@code action} is null or {@code value} is null
*/
public ChatClickable(EnumClickAction action, String value) {
Preconditions.checkNotNull(action, "hover action");
@@ -31,7 +32,8 @@ public class ChatClickable {
/**
* Returns click action defined in constructor
* @return click action
*
* @return click action
*/
public EnumClickAction getAction() {
return action;
@@ -39,7 +41,8 @@ public class ChatClickable {
/**
* Returns click action defined in constructor
* @return click action
*
* @return click action
*/
public String getValue() {
return value;
@@ -15,12 +15,13 @@ public class ChatHoverable {
/**
* Constructs new instance with given action and value.
* @param action
* hover event action
* @param value
* hover event value
* @throws IllegalArgumentException
* if {@code action} is null or {@code value} is null
*
* @param action
* hover event action
* @param value
* hover event value
* @throws IllegalArgumentException
* if {@code action} is null or {@code value} is null
*/
public ChatHoverable(EnumHoverAction action, IChatBaseComponent value) {
Preconditions.checkNotNull(action, "hover action");
@@ -31,7 +32,8 @@ public class ChatHoverable {
/**
* Returns hover action defined in constructor
* @return hover action
*
* @return hover action
*/
public EnumHoverAction getAction() {
return action;
@@ -39,7 +41,8 @@ public class ChatHoverable {
/**
* Returns hover value defined in constructor
* @return hover value
*
* @return hover value
*/
public IChatBaseComponent getValue() {
return value;
@@ -94,7 +94,8 @@ public class ChatModifier {
/**
* Returns true if bold is defined and set to true, false otherwise
* @return true if bold is defined and set to true, false otherwise
*
* @return true if bold is defined and set to true, false otherwise
*/
public boolean isBold(){
return Boolean.TRUE.equals(bold);
@@ -102,7 +103,8 @@ public class ChatModifier {
/**
* Returns true if italic is defined and set to true, false otherwise
* @return true if italic is defined and set to true, false otherwise
*
* @return true if italic is defined and set to true, false otherwise
*/
public boolean isItalic(){
return Boolean.TRUE.equals(italic);
@@ -110,7 +112,8 @@ public class ChatModifier {
/**
* Returns true if underlined is defined and set to true, false otherwise
* @return true if underlined is defined and set to true, false otherwise
*
* @return true if underlined is defined and set to true, false otherwise
*/
public boolean isUnderlined(){
return Boolean.TRUE.equals(underlined);
@@ -118,7 +121,8 @@ public class ChatModifier {
/**
* Returns true if strikethrough is defined and set to true, false otherwise
* @return true if strikethrough is defined and set to true, false otherwise
*
* @return true if strikethrough is defined and set to true, false otherwise
*/
public boolean isStrikethrough(){
return Boolean.TRUE.equals(strikethrough);
@@ -126,7 +130,8 @@ public class ChatModifier {
/**
* Returns true if obfuscation is defined and set to true, false otherwise
* @return true if obfuscation is defined and set to true, false otherwise
*
* @return true if obfuscation is defined and set to true, false otherwise
*/
public boolean isObfuscated(){
return Boolean.TRUE.equals(obfuscated);
@@ -150,7 +155,9 @@ public class ChatModifier {
/**
* Sets click action to OPEN_URL and url to given value
* @param url - url to open
*
* @param url
* url to open
*/
public void onClickOpenUrl(String url) {
Preconditions.checkNotNull(url, "url");
@@ -159,7 +166,9 @@ public class ChatModifier {
/**
* Sets click action to RUN_COMMAND and command to given value
* @param command - command to perform, might be without / to send a chat message
*
* @param command
* command to perform, might be without / to send a chat message
*/
public void onClickRunCommand(String command) {
Preconditions.checkNotNull(command, "command");
@@ -168,7 +177,9 @@ public class ChatModifier {
/**
* Sets click action to SUGGEST_COMMAND and command to given value
* @param command - command to suggest
*
* @param command
* command to suggest
*/
public void onClickSuggestCommand(String command) {
Preconditions.checkNotNull(command, "command");
@@ -177,7 +188,9 @@ public class ChatModifier {
/**
* Sets click action to CHANGE_PAGE and page id to given value
* @param newPage - id of new page
*
* @param newPage
* id of new page
*/
public void onClickChangePage(int newPage) {
if (TabAPI.getInstance().getServerVersion().getMinorVersion() < 8) throw new UnsupportedOperationException("change_page click action is not supported on <1.8");
@@ -186,7 +199,9 @@ public class ChatModifier {
/**
* Sets click action to COPY_TO_CLIPBOARD and text to provided value
* @param text - text to copy to clipboard on click
*
* @param text
* text to copy to clipboard on click
*/
public void onClickCopyToClipBoard(String text) {
Preconditions.checkNotNull(text, "text");
@@ -201,7 +216,9 @@ public class ChatModifier {
/**
* Sets hover action to SHOW_TEXT and text to given value
* @param text - text to show
*
* @param text
* text to show
*/
public void onHoverShowText(IChatBaseComponent text) {
Preconditions.checkNotNull(text, "text");
@@ -210,7 +227,9 @@ public class ChatModifier {
/**
* Sets hover action to SHOW_ITEM and item to given value
* @param serializedItem - item to show
*
* @param serializedItem
* item to show
*/
public void onHoverShowItem(String serializedItem) {
Preconditions.checkNotNull(serializedItem, "item");
@@ -219,9 +238,13 @@ public class ChatModifier {
/**
* Sets hover action to SHOW_ENTITY and entity data to given values
* @param type - entity type
* @param id - entity uuid
* @param name - entity custom name
*
* @param type
* entity type
* @param id
* entity uuid
* @param name
* entity custom name
*/
public void onHoverShowEntity(String type, UUID id, String name) {
Preconditions.checkNotNull(type, "type");
@@ -156,11 +156,11 @@ public class DeserializedChatComponent extends IChatBaseComponent {
/**
* Returns boolean value of requested key from map
*
* @param jsonObject
* map to get value from
* @param key
* name of key
* @return value from json object or null if not present
* @param jsonObject
* map to get value from
* @param key
* name of key
* @return value from json object or null if not present
*/
private static Boolean getBoolean(JSONObject jsonObject, String key) {
Preconditions.checkNotNull(jsonObject, "json object");
@@ -59,10 +59,11 @@ public enum EnumChatFormat {
/**
* Constructs new color instance with given character and hex code
* @param character
* character which the color goes by
* @param hexCode
* 6-digit hex code of the color
*
* @param character
* character which the color goes by
* @param hexCode
* 6-digit hex code of the color
*/
EnumChatFormat(char character, String hexCode) {
this.character = character;
@@ -76,8 +77,9 @@ public enum EnumChatFormat {
/**
* Constructs new magic code instance with given character
* @param character
* character representing the magic code
*
* @param character
* character representing the magic code
*/
EnumChatFormat(char character) {
this.character = character;
@@ -90,7 +92,8 @@ public enum EnumChatFormat {
/**
* Returns red value of this color code
* @return red value
*
* @return red value
*/
public short getRed() {
return red;
@@ -98,7 +101,8 @@ public enum EnumChatFormat {
/**
* Returns green value of this color code
* @return green value
*
* @return green value
*/
public short getGreen() {
return green;
@@ -106,7 +110,8 @@ public enum EnumChatFormat {
/**
* Returns blue value of this color code
* @return blue value
*
* @return blue value
*/
public short getBlue() {
return blue;
@@ -114,9 +119,10 @@ public enum EnumChatFormat {
/**
* Returns enum value based on provided character or null if character is not valid
* @param c
* color code character (0-9, a-f, k-o, r)
* @return instance from the character or null if character is not valid
*
* @param c
* color code character (0-9, a-f, k-o, r)
* @return instance from the character or null if character is not valid
*/
public static EnumChatFormat getByChar(char c) {
for (EnumChatFormat format : VALUES) {
@@ -128,9 +134,10 @@ public enum EnumChatFormat {
/**
* Returns enum value of last colors used in given string.
* If it's null, empty or does not contain color codes, WHITE is returned.
* @param string
* string to check last colors of
* @return last used color code in given string or WHITE if nothing is found
*
* @param string
* string to check last colors of
* @return last used color code in given string or WHITE if nothing is found
*/
public static EnumChatFormat lastColorsOf(String string) {
if (string == null || string.length() == 0) return EnumChatFormat.WHITE;
@@ -147,7 +154,8 @@ public enum EnumChatFormat {
/**
* Returns color char followed by color's character
* @return color char followed by color's character
*
* @return color char followed by color's character
*/
public String getFormat() {
return chatFormat;
@@ -155,7 +163,8 @@ public enum EnumChatFormat {
/**
* Returns character representing this color
* @return character representing this color
*
* @return character representing this color
*/
public char getCharacter() {
return character;
@@ -163,7 +172,8 @@ public enum EnumChatFormat {
/**
* Returns hex code of this format, null if this is a magic code
* @return hex code of this format
*
* @return hex code of this format
*/
public String getHexCode() {
return hexCode;
@@ -171,13 +181,14 @@ public enum EnumChatFormat {
/**
* Returns enum value with exact red, green and blue values or null if no match was found
* @param red
* exact red value
* @param green
* exact green value
* @param blue
* exact blue value
* @return enum value or null if no such combination exists
*
* @param red
* exact red value
* @param green
* exact green value
* @param blue
* exact blue value
* @return enum value or null if no such combination exists
*/
public static EnumChatFormat fromRGBExact(int red, int green, int blue){
for (EnumChatFormat format : VALUES) {
@@ -189,9 +200,10 @@ public enum EnumChatFormat {
/**
* Color translation method taken from bukkit, which converts '&amp;' symbol into
* the actual color character if followed by a valid color character.
* @param textToTranslate
* text to replace color symbol in
* @return colorized string from provided text
*
* @param textToTranslate
* text to replace color symbol in
* @return colorized string from provided text
*/
public static String color(String textToTranslate){
if (textToTranslate == null) return null;
@@ -208,9 +220,10 @@ public enum EnumChatFormat {
/**
* Turns back the color symbol into '&amp;' symbol in provided text.
* @param text
* text to revert colors in
* @return reverted text
*
* @param text
* text to revert colors in
* @return reverted text
*/
public static String decolor(String text) {
if (text == null) return null;
@@ -220,9 +233,10 @@ public enum EnumChatFormat {
/**
* Code taken from bukkit, which returns last color codes used in provided text.
* @param input
* text to get last colors from
* @return last colors used in provided text or empty string if nothing was found
*
* @param input
* text to get last colors from
* @return last colors used in provided text or empty string if nothing was found
*/
public static String getLastColors(String input) {
if (input == null) return "";
@@ -39,8 +39,9 @@ public class IChatBaseComponent {
/**
* Constructs a new component which is a clone of provided component
* @param component
* component to clone
*
* @param component
* component to clone
*/
public IChatBaseComponent(IChatBaseComponent component) {
Preconditions.checkNotNull(component, "component");
@@ -53,8 +54,9 @@ public class IChatBaseComponent {
/**
* Constructs new instance with given text
* @param text
* text to display
*
* @param text
* text to display
*/
public IChatBaseComponent(String text) {
this.text = text;
@@ -62,7 +64,8 @@ public class IChatBaseComponent {
/**
* Returns list of extra components. If no extra components are defined, returns empty list.
* @return list of extra components
*
* @return list of extra components
*/
public List<IChatBaseComponent> getExtra(){
if (extra == null) return Collections.emptyList();
@@ -71,11 +74,12 @@ public class IChatBaseComponent {
/**
* Sets full list of extra components to given list. Does not allow empty list.
* @param components
* components to use as extra
* @return self
* @throws IllegalArgumentException
* if {@code components} is an empty list
*
* @param components
* components to use as extra
* @return self
* @throws IllegalArgumentException
* if {@code components} is an empty list
*/
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
if (components.isEmpty()) throw new IllegalArgumentException("Unexpected empty array of components"); //exception taken from minecraft
@@ -85,8 +89,9 @@ public class IChatBaseComponent {
/**
* Appends provided component as extra component
* @param child
* component to append
*
* @param child
* component to append
*/
public void addExtra(IChatBaseComponent child) {
Preconditions.checkNotNull(child, "extra");
@@ -96,7 +101,8 @@ public class IChatBaseComponent {
/**
* Returns text of this component
* @return text of this component
*
* @return text of this component
*/
public String getText() {
return text;
@@ -104,7 +110,8 @@ public class IChatBaseComponent {
/**
* Returns chat modifier of this component
* @return chat modifier of this component
*
* @return chat modifier of this component
*/
public ChatModifier getModifier() {
return modifier;
@@ -112,10 +119,11 @@ public class IChatBaseComponent {
/**
* Sets modifier to provided value
* @param modifier
* modifier to set value to
* @throws IllegalArgumentException
* if {@code modifier} is null
*
* @param modifier
* modifier to set value to
* @throws IllegalArgumentException
* if {@code modifier} is null
*/
public void setModifier(ChatModifier modifier) {
Preconditions.checkNotNull(modifier, "modifier");
@@ -124,8 +132,9 @@ public class IChatBaseComponent {
/**
* Changes text of this component
* @param text
* text to show
*
* @param text
* text to show
*/
public void setText(String text) {
this.text = text;
@@ -133,9 +142,10 @@ public class IChatBaseComponent {
/**
* Deserializes string and returns created component. If provided string is null, returns null.
* @param json
* serialized string
* @return Deserialized component or null if input is null
*
* @param json
* serialized string
* @return Deserialized component or null if input is null
*/
public static IChatBaseComponent deserialize(String json) {
if (json == null) return null;
@@ -146,8 +156,9 @@ public class IChatBaseComponent {
* Converts the component to a string representing the serialized component.
* This method is only used internally by json library since it's missing
* protocol version field used by the method.
* @return serialized component in string form
* @see #toString(ProtocolVersion)
*
* @return serialized component in string form
* @see #toString(ProtocolVersion)
*/
@Override
public String toString() {
@@ -162,9 +173,10 @@ public class IChatBaseComponent {
/**
* Serializes this component with colors based on client version.
* If client version is &lt;1.16, HEX colors will be converted to legacy colors.
* @param clientVersion
* client version to adapt component for
* @return serialized string
*
* @param clientVersion
* client version to adapt component for
* @return serialized string
*/
public String toString(ProtocolVersion clientVersion) {
return toString(clientVersion, false);
@@ -173,11 +185,12 @@ public class IChatBaseComponent {
/**
* Serializes this component with colors based on client version.
* If client version is &lt;1.16, HEX colors will be converted to legacy colors.
* @param clientVersion
* client version to adapt component for
* @param sendTranslatableIfEmpty
* whether empty translatable should be sent if text is empty or not
* @return serialized string
*
* @param clientVersion
* client version to adapt component for
* @param sendTranslatableIfEmpty
* whether empty translatable should be sent if text is empty or not
* @return serialized string
*/
public String toString(ProtocolVersion clientVersion, boolean sendTranslatableIfEmpty) {
if (extra == null) {
@@ -211,13 +224,14 @@ public class IChatBaseComponent {
/**
* Returns organized component from colored text
* @param originalText
* text to convert
* @return organized component from colored text
*
* @param originalText
* text to convert
* @return organized component from colored text
*/
public static IChatBaseComponent fromColoredText(String originalText){
Preconditions.checkNotNull(originalText, "text");
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText), false);
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText));
List<IChatBaseComponent> components = new ArrayList<>();
StringBuilder builder = new StringBuilder();
IChatBaseComponent component = new IChatBaseComponent();
@@ -299,11 +313,12 @@ public class IChatBaseComponent {
/**
* Returns true if text contains legacy color request at defined RGB index start
* @param text
* text to check
* @param i
* current index start
* @return true if legacy color is defined, false if not
*
* @param text
* text to check
* @param i
* current index start
* @return true if legacy color is defined, false if not
*/
private static boolean containsLegacyCode(String text, int i) {
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
@@ -312,7 +327,8 @@ public class IChatBaseComponent {
/**
* Converts this component into a simple text with legacy colors (the closest match if color is set to RGB)
* @return The simple text format
*
* @return The simple text format
*/
public String toLegacyText() {
StringBuilder builder = new StringBuilder();
@@ -323,11 +339,12 @@ public class IChatBaseComponent {
/**
* Appends text to string builder, might also add color and magic codes if they are different
* from previous component in chain.
* @param builder
* builder to append text to
* @param previousFormatting
* colors and magic codes in previous component
* @return wew formatting, might be identical to previous one
*
* @param builder
* builder to append text to
* @param previousFormatting
* colors and magic codes in previous component
* @return new formatting, might be identical to previous one
*/
private String append(StringBuilder builder, String previousFormatting) {
String formatting = previousFormatting;
@@ -347,7 +364,8 @@ public class IChatBaseComponent {
/**
* Returns colors and magic codes of this component
* @return used colors and magic codes
*
* @return used colors and magic codes
*/
private String getFormatting() {
StringBuilder builder = new StringBuilder();
@@ -365,7 +383,8 @@ public class IChatBaseComponent {
/**
* Returns raw text without colors, only works correctly when component is organized
* @return raw text in this component and all child components
*
* @return raw text in this component and all child components
*/
public String toRawText() {
StringBuilder builder = new StringBuilder();
@@ -378,7 +397,8 @@ public class IChatBaseComponent {
/**
* Converts the component into flat text with used colors (including rgb) and magic codes
* @return converted text
*
* @return converted text
*/
public String toFlatText() {
StringBuilder builder = new StringBuilder();
@@ -395,9 +415,10 @@ public class IChatBaseComponent {
* Returns the most optimized component based on text. Returns null if text is null,
* organized component if RGB colors are used or simple component with only text field
* containing the whole text when no RGB colors are used
* @param text
* text to create component from
* @return The most performance-optimized component based on text
*
* @param text
* text to create component from
* @return The most performance-optimized component based on text
*/
public static IChatBaseComponent optimizedComponent(String text){
if (text == null) return null;
@@ -29,10 +29,10 @@ public class TextColor {
/**
* Constructs new instance as a clone of the provided color.
*
* @param color
* color to create a clone of
* @throws IllegalArgumentException
* if color is {@code null}
* @param color
* color to create a clone of
* @throws IllegalArgumentException
* if color is {@code null}
*/
public TextColor(TextColor color) {
Preconditions.checkNotNull(color, "color");
@@ -45,10 +45,10 @@ public class TextColor {
/**
* Constructs new instance from provided 6-digit hex code string
*
* @param hexCode
* a 6-digit combination of hex numbers as a string
* @throws IllegalArgumentException
* if hexCode is {@code null}
* @param hexCode
* a 6-digit combination of hex numbers as a string
* @throws IllegalArgumentException
* if hexCode is {@code null}
*/
public TextColor(String hexCode) {
Preconditions.checkNotNull(hexCode, "hex code");
@@ -58,12 +58,12 @@ public class TextColor {
/**
* Constructs new instance from provided 6-digit hex code and forced legacy color
*
* @param hexCode
* 6-digit combination of hex numbers as a string
* @param legacyColor
* color to use for legacy clients instead of using the closest legacy color
* @throws IllegalArgumentException
* if {@code hexCode} is {@code null} or {@code legacyColor} is {@code null}
* @param hexCode
* 6-digit combination of hex numbers as a string
* @param legacyColor
* color to use for legacy clients instead of using the closest legacy color
* @throws IllegalArgumentException
* if {@code hexCode} is {@code null} or {@code legacyColor} is {@code null}
*/
public TextColor(String hexCode, EnumChatFormat legacyColor) {
Preconditions.checkNotNull(hexCode, "hex code");
@@ -76,10 +76,10 @@ public class TextColor {
/**
* Constructs new instance from provided legacy color
*
* @param legacyColor
* legacy color to construct the instance from
* @throws IllegalArgumentException
* if {@code legacyColor} is {@code null}
* @param legacyColor
* legacy color to construct the instance from
* @throws IllegalArgumentException
* if {@code legacyColor} is {@code null}
*/
public TextColor(EnumChatFormat legacyColor) {
Preconditions.checkNotNull(legacyColor, "legacy color");
@@ -90,14 +90,14 @@ public class TextColor {
/**
* Constructs new instance with red, green and blue values
*
* @param red
* red value
* @param green
* green value
* @param blue
* blue value
* @throws IllegalArgumentException
* if {@code red}, {@code green} or {@code blue} is out of range ({@code 0-255})
* @param red
* red value
* @param green
* green value
* @param blue
* blue value
* @throws IllegalArgumentException
* if {@code red}, {@code green} or {@code blue} is out of range ({@code 0-255})
*/
public TextColor(int red, int green, int blue) {
Preconditions.checkRange(red, 0, 255, "red");
@@ -130,7 +130,8 @@ public class TextColor {
/**
* Returns {@code red} value
* @return red value
*
* @return red value
*/
public int getRed() {
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
@@ -139,7 +140,8 @@ public class TextColor {
/**
* Returns {@code green} value
* @return green value
*
* @return green value
*/
public int getGreen() {
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
@@ -148,7 +150,8 @@ public class TextColor {
/**
* Returns {@code blue} value
* @return blue value
*
* @return blue value
*/
public int getBlue() {
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
@@ -159,7 +162,8 @@ public class TextColor {
* Returns the closest legacy color of this color object.
* If the color was defined in constructor, it's returned.
* Otherwise, the closest color is calculated the then returned.
* @return closest legacy color
*
* @return closest legacy color
*/
public EnumChatFormat getLegacyColor() {
if (legacyColor == null) legacyColor = loadClosestColor();
@@ -168,7 +172,8 @@ public class TextColor {
/**
* Returns the rgb combination as a 6-digit hex code string
* @return the rgb combination as a 6-digit hex code string
*
* @return the rgb combination as a 6-digit hex code string
*/
public String getHexCode() {
if (hexCode == null) hexCode = String.format("%06X", rgb);
@@ -178,7 +183,8 @@ public class TextColor {
/**
* Converts the color into a valid color value used in color field in chat component.
* That is either 6-digit hex code prefixed with '#', or lowercase legacy color.
* @return the color serialized for use in chat component
*
* @return the color serialized for use in chat component
*/
public String toString() {
EnumChatFormat legacyEquivalent = EnumChatFormat.fromRGBExact(getRed(), getGreen(), getBlue());
@@ -191,7 +197,8 @@ public class TextColor {
/**
* Returns true if legacy color was forced with a constructor, false if not
* @return true if forced, false if not
*
* @return true if forced, false if not
*/
public boolean isLegacyColorForced() {
return legacyColorForced;
@@ -202,9 +209,10 @@ public class TextColor {
* If the entered string is null, returns null.
* If it's prefixed with '#', it's considered as a hex code.
* Otherwise, it is considered being a lowercase legacy color.
* @param string
* string from color field in chat component
* @return An instance from specified string or null if string is null
*
* @param string
* string from color field in chat component
* @return An instance from specified string or null if string is null
*/
public static TextColor fromString(String string) {
if (string == null) return null;
@@ -0,0 +1,75 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.ProtocolVersion;
import java.util.List;
/**
* A class extending IChatBaseComponent that holds the original value
* to avoid deserializing it, which is an unnecessary operation that
* slows down the performance, as well as makes code more complicated
* and open to problems.
*/
public class WrappedChatComponent extends IChatBaseComponent {
/** Original NMS component */
private final Object originalComponent;
/**
* Constructs new instance with given parameter
*
* @param originalComponent
* Original NMS component
*/
public WrappedChatComponent(Object originalComponent) {
this.originalComponent = originalComponent;
}
/**
* Returns the original NMS component
* @return the original NMS component
*/
public Object get() {
return originalComponent;
}
@Override
public String toString() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public String toString(ProtocolVersion clientVersion) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public List<IChatBaseComponent> getExtra(){
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public String getText() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public ChatModifier getModifier() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public void addExtra(IChatBaseComponent child) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public void setModifier(ChatModifier modifier) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
}
@@ -8,6 +8,7 @@ import me.neznamy.tab.api.chat.TextColor;
import me.neznamy.tab.api.chat.rgb.format.BukkitFormat;
import me.neznamy.tab.api.chat.rgb.format.CMIFormat;
import me.neznamy.tab.api.chat.rgb.format.HtmlFormat;
import me.neznamy.tab.api.chat.rgb.format.KyoriFormat;
import me.neznamy.tab.api.chat.rgb.format.RGBFormatter;
import me.neznamy.tab.api.chat.rgb.format.UnnamedFormat1;
import me.neznamy.tab.api.chat.rgb.gradient.CMIGradient;
@@ -44,7 +45,8 @@ public class RGBUtils {
new BukkitFormat(),
new CMIFormat(),
new UnnamedFormat1(),
new HtmlFormat()
new HtmlFormat(),
new KyoriFormat()
};
gradients = new GradientPattern[] {
//{#RRGGBB>}text{#RRGGBB<}
@@ -63,7 +65,8 @@ public class RGBUtils {
/**
* Returns instance of this class
* @return instance
*
* @return instance
*/
public static RGBUtils getInstance() {
return instance;
@@ -71,20 +74,16 @@ public class RGBUtils {
/**
* Applies all RGB formats and gradients to text and returns it.
* This method is called on every property initialization to convert formats
* once instead of on every refresh, if possible. Because of that, parameter
* {@code ignorePlaceholders} exists to not break raw placeholder identifiers
* @param text
* original text
* @param ignorePlaceholders
* whether placeholders should be ignored or not
* @return text where everything is converted to #RRGGBB
*
* @param text
* original text
* @return text where everything is converted to #RRGGBB
*/
public String applyFormats(String text, boolean ignorePlaceholders) {
public String applyFormats(String text) {
Preconditions.checkNotNull(text, "text");
String replaced = text;
for (GradientPattern pattern : gradients) {
replaced = pattern.applyPattern(replaced, ignorePlaceholders);
replaced = pattern.applyPattern(replaced, false);
}
for (RGBFormatter formatter : formats) {
replaced = formatter.reformat(replaced);
@@ -92,22 +91,40 @@ public class RGBUtils {
return replaced;
}
/**
* Applies all gradient formats to text and returns it. This only affects
* usage where no placeholder is used inside.
*
* @param text
* original text
* @return text where all gradients with static text are converted to #RRGGBB
*/
public String applyCleanGradients(String text) {
Preconditions.checkNotNull(text, "text");
String replaced = text;
for (GradientPattern pattern : gradients) {
replaced = pattern.applyPattern(replaced, true);
}
return replaced;
}
/**
* Converts TAB's RGB format (#RRGGBB) into bukkit one
* (&amp;x&amp;r&amp;r&amp;g&amp;g&amp;b&amp;b) for modern
* clients (1.16+), for legacy clients it will use the closest color.
* @param text
* text to convert
* @param rgbClient
* whether client accepts RGB or not
* @return converted text
*
* @param text
* text to convert
* @param rgbClient
* whether client accepts RGB or not
* @return converted text
*/
public String convertToBukkitFormat(String text, boolean rgbClient) {
if (text == null) return null;
if (!text.contains("#")) return text; //no rgb codes
if (rgbClient) {
//converting random formats to TAB one
String replaced = applyFormats(text, false);
String replaced = applyFormats(text);
for (Pattern p : new Pattern[]{tabPatternLegacy, tabPattern}) {
Matcher m = p.matcher(replaced);
while (m.find()) {
@@ -124,14 +141,15 @@ public class RGBUtils {
/**
* Converts all hex codes in given string to legacy codes
* @param text
* text to convert
* @return translated text
*
* @param text
* text to convert
* @return translated text
*/
public String convertRGBtoLegacy(String text) {
if (text == null) return null;
if (!text.contains("#")) return EnumChatFormat.color(text);
String applied = applyFormats(text, false);
String applied = applyFormats(text);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < applied.length(); i++){
char c = applied.charAt(i);
@@ -158,9 +176,10 @@ public class RGBUtils {
/**
* Returns true if entered string is a valid 6-digit combination of
* hexadecimal numbers, false if not
* @param string
* string to check
* @return {@code true} if valid, {@code false} if not
*
* @param string
* string to check
* @return {@code true} if valid, {@code false} if not
*/
public boolean isHexCode(String string) {
Preconditions.checkNotNull(string, "string");
@@ -174,11 +193,12 @@ public class RGBUtils {
/**
* Returns true if text contains legacy color request at defined RGB index start
* @param text
* text to check
* @param i
* current index start
* @return {@code true} if legacy color is defined and valid, {@code false} otherwise
*
* @param text
* text to check
* @param i
* current index start
* @return {@code true} if legacy color is defined and valid, {@code false} otherwise
*/
private static boolean containsLegacyCode(String text, int i) {
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
@@ -0,0 +1,25 @@
package me.neznamy.tab.api.chat.rgb.format;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Formatter for &lt;#RRGGBB>
*/
public class KyoriFormat implements RGBFormatter {
private final Pattern pattern = Pattern.compile("<#[0-9a-fA-F]{6}>");
@Override
public String reformat(String text) {
if (!text.contains("<#")) return text;
String replaced = text;
Matcher m = pattern.matcher(replaced);
while (m.find()) {
String hexCode = m.group();
String fixed = hexCode.substring(2, 8);
replaced = replaced.replace(hexCode, "#" + fixed);
}
return replaced;
}
}
@@ -7,8 +7,10 @@ public interface RGBFormatter {
/**
* Reformats RGB codes in provided text into #RRGGBB format
* @param text - text to format
* @return reformatted text
*
* @param text
* text to format
* @return reformatted text
*/
String reformat(String text);
}
@@ -10,17 +10,23 @@ public interface GradientPattern {
/**
* Applies gradients in provided text and returns text using only #RRGGBB
* @param text - text to be reformatted
* @return reformatted text
*
* @param text
* text to be reformatted
* @return reformatted text
*/
String applyPattern(String text, boolean ignorePlaceholders);
/**
* Returns gradient text based on start color, text and end color
* @param start - start color
* @param text - text to be reformatted
* @param end - end color
* @return reformatted text
*
* @param start
* start color
* @param text
* text to be reformatted
* @param end
* end color
* @return reformatted text
*/
default String asGradient(TextColor start, String text, TextColor end) {
//lazy support for magic codes in gradients
@@ -38,16 +38,16 @@ public abstract class ConfigurationFile {
* Constructs new instance and attempts to load specified configuration file.
* If file does not exist, default file is copied from {@code source}.
*
* @param source
* Source to copy file from if it does not exist
* @param destination
* File destination to use
* @throws IllegalArgumentException
* if {@code destination} is null
* @throws IllegalStateException
* if file does not exist and source is null
* @throws IOException
* if I/O operation with the file unexpectedly fails
* @param source
* Source to copy file from if it does not exist
* @param destination
* File destination to use
* @throws IllegalArgumentException
* if {@code destination} is null
* @throws IllegalStateException
* if file does not exist and source is null
* @throws IOException
* if I/O operation with the file unexpectedly fails
*/
protected ConfigurationFile(InputStream source, File destination) throws IOException {
Preconditions.checkNotNull(destination, "destination");
@@ -76,7 +76,8 @@ public abstract class ConfigurationFile {
/**
* Returns simple name of the file
* @return simple name of the file
*
* @return simple name of the file
*/
public String getName() {
return file.getName();
@@ -84,7 +85,8 @@ public abstract class ConfigurationFile {
/**
* Returns the root value map
* @return the root value map
*
* @return the root value map
*/
public Map<String, Object> getValues(){
return values;
@@ -99,11 +101,11 @@ public abstract class ConfigurationFile {
* {@code defaultValue} is not {@code null}, value is inserted, {@link #save()}
* called and {@code defaultValue} returned.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file
*/
public Object getObject(String path, Object defaultValue) {
Object value = values;
@@ -126,9 +128,9 @@ public abstract class ConfigurationFile {
* Returns config option with specified path. If option is not present,
* {@code null} is returned.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from configuration file or null if not present
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from configuration file or null if not present
*/
public Object getObject(String path) {
return getObject(path, null);
@@ -149,11 +151,11 @@ public abstract class ConfigurationFile {
* Returns value from map without case sensitivity of the key. Returns
* {@code null} if no such key is found.
*
* @param map
* map to get value from
* @param key
* case-insensitive key name
* @return map value from case-insensitive key
* @param map
* map to get value from
* @param key
* case-insensitive key name
* @return map value from case-insensitive key
*/
private Object getIgnoreCase(Map<Object, Object> map, String key) {
for (Entry<Object, Object> entry : map.entrySet()) {
@@ -166,9 +168,9 @@ public abstract class ConfigurationFile {
* Returns config option with specified path as {@code String}. Returns {@code null} if
* option is not present.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from file or null if not present
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from file or null if not present
*/
public String getString(String path) {
return getString(path, null);
@@ -179,11 +181,11 @@ public abstract class ConfigurationFile {
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
* and {@code defaultValue} returned.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code String}
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code String}
*/
public String getString(String path, String defaultValue) {
Object value = getObject(path, defaultValue);
@@ -195,9 +197,9 @@ public abstract class ConfigurationFile {
* Returns config option with specified path as {@code List<String>}. Returns {@code null} if
* option is not present.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from file or null if not present
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from file or null if not present
*/
public List<String> getStringList(String path) {
return getStringList(path, null);
@@ -208,11 +210,11 @@ public abstract class ConfigurationFile {
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
* and {@code defaultValue} returned.
*
* @param path
* Path of the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code List<String>}
* @param path
* Path of the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code List<String>}
*/
public List<String> getStringList(String path, List<String> defaultValue) {
Object value = getObject(path, defaultValue);
@@ -231,9 +233,9 @@ public abstract class ConfigurationFile {
* Returns config option with specified path as {@code Integer}. Returns {@code null} if
* option is not present.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from file or null if not present
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from file or null if not present
*/
public Integer getInt(String path) {
return getInt(path, null);
@@ -244,11 +246,11 @@ public abstract class ConfigurationFile {
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
* and {@code defaultValue} returned.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code Integer}
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code Integer}
*/
public Integer getInt(String path, Integer defaultValue) {
Object value = getObject(path, defaultValue);
@@ -265,11 +267,11 @@ public abstract class ConfigurationFile {
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
* and {@code defaultValue} returned.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code Boolean}
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code Boolean}
*/
public boolean getBoolean(String path, boolean defaultValue) {
Object value = getObject(path, defaultValue);
@@ -282,11 +284,11 @@ public abstract class ConfigurationFile {
* and {@code defaultValue} is not {@code null}, value is inserted, {@link #save()} called
* and {@code defaultValue} returned.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code Double}
* @param path
* Path to the option with sections separated with "{@code .}"
* @param defaultValue
* Value to be inserted and returned if option is not present
* @return value from configuration file as {@code Double}
*/
public Double getDouble(String path, double defaultValue) {
Object value = getObject(path, defaultValue);
@@ -302,9 +304,9 @@ public abstract class ConfigurationFile {
* Returns config option with specified path as {@code Map<K, V>}. If the option
* is not present or value is not a {@code Map}, new empty {@code Map} is returned.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from configuration file as {@code Map<K, V>}
* @param path
* Path to the option with sections separated with "{@code .}"
* @return value from configuration file as {@code Map<K, V>}
*/
public <K, V> Map<K, V> getConfigurationSection(String path) {
if (path == null || path.length() == 0) return (Map<K, V>) values;
@@ -320,9 +322,9 @@ public abstract class ConfigurationFile {
* Returns {@code true} if the file has option with specified path, {@code false}
* if not.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @return {@code true} if present, {@code false} if not
* @param path
* Path to the option with sections separated with "{@code .}"
* @return {@code true} if present, {@code false} if not
*/
public boolean hasConfigOption(String path) {
return getObject(path) != null;
@@ -331,10 +333,10 @@ public abstract class ConfigurationFile {
/**
* Sets value to the specified path and saves the file to disk by calling {@link #save()}.
*
* @param path
* Path to the option with sections separated with "{@code .}"
* @param value
* Value to save
* @param path
* Path to the option with sections separated with "{@code .}"
* @param value
* Value to save
*/
public void set(String path, Object value) {
set(values, path, value);
@@ -345,13 +347,13 @@ public abstract class ConfigurationFile {
* Sets value to the specified map with specified path and value. This is an internal method
* that correctly creates maps to separate sections using "{@code .}".
*
* @param map
* Map to insert value to
* @param path
* Path to the option with sections separated with "{@code .}"
* @param value
* Value to save
* @return the first argument to allow chaining
* @param map
* Map to insert value to
* @param path
* Path to the option with sections separated with "{@code .}"
* @param value
* Value to save
* @return the first argument to allow chaining
*/
private Map<String, Object> set(Map<String, Object> map, String path, Object value) {
if (path.contains(".")) {
@@ -375,11 +377,11 @@ public abstract class ConfigurationFile {
* Returns the real key name without case sensitivity from map. If not found, inserted key
* is returned.
*
* @param map
* Map to check keys of
* @param key
* Key to find
* @return The real key name
* @param map
* Map to check keys of
* @param key
* Key to find
* @return The real key name
*/
private String getRealKey(Map<?, ?> map, String key) {
for (Object mapKey : map.keySet()) {
@@ -391,8 +393,8 @@ public abstract class ConfigurationFile {
/**
* Detects header of a file (first lines of file starting with #).
*
* @throws IOException
* if I/O operation fails
* @throws IOException
* if I/O operation fails
*/
private void detectHeader() throws IOException {
header = new ArrayList<>();
@@ -409,8 +411,8 @@ public abstract class ConfigurationFile {
* Inserts header back into file. This is required after calling {@link #save()}, because
* it destroys the header.
*
* @throws IOException
* if I/O operation fails
* @throws IOException
* if I/O operation fails
*/
public void fixHeader() throws IOException {
if (header == null) return;
@@ -36,18 +36,18 @@ public class YamlConfigurationFile extends ConfigurationFile {
* Constructs new instance and attempts to load specified configuration file.
* If file does not exist, default file is copied from {@code source}.
*
* @param source
* Source to copy file from if it does not exist
* @param destination
* File destination to use
* @throws IllegalArgumentException
* if {@code destination} is null
* @throws IllegalStateException
* if file does not exist and source is null
* @throws YAMLException
* if file has invalid YAML syntax
* @throws IOException
* if I/O operation with the file unexpectedly fails
* @param source
* Source to copy file from if it does not exist
* @param destination
* File destination to use
* @throws IllegalArgumentException
* if {@code destination} is null
* @throws IllegalStateException
* if file does not exist and source is null
* @throws YAMLException
* if file has invalid YAML syntax
* @throws IOException
* if I/O operation with the file unexpectedly fails
*/
public YamlConfigurationFile(InputStream source, File destination) throws YAMLException, IOException {
super(source, destination);
@@ -11,14 +11,15 @@ public interface PlayerLoadEvent extends TabEvent {
/**
* Gets the player that was loaded.
*
* @return the player that was loaded
* @return the player that was loaded
*/
TabPlayer getPlayer();
/**
* Returns true if player was loaded because they joined,
* false if the player was loaded because tab reload command was executed
* @return true if player joined, false if plugin was reloaded
*
* @return true if player joined, false if plugin was reloaded
*/
boolean isJoin();
}
@@ -6,10 +6,6 @@ public interface Placeholder {
String getIdentifier();
boolean isTriggerMode();
void enableTriggerMode();
void enableTriggerMode(Runnable onActivation, Runnable onDisable);
void unload();
@@ -27,16 +27,21 @@ public interface PlaceholderManager {
/**
* Detects placeholders in text using %% pattern and returns list of all detected identifiers
* @param text - text to detect placeholders in
* @return list of detected identifiers
*
* @param text
* text to detect placeholders in
* @return list of detected identifiers
*/
List<String> detectPlaceholders(String text);
/**
* Adds placeholder to list of used placeholders and assigns this feature as using it,
* which will then receive refresh() if values changes
* @param identifier - placeholder identifier
* @param feature - feature using the placeholder
*
* @param identifier
* placeholder identifier
* @param feature
* feature using the placeholder
*/
void addUsedPlaceholder(String identifier, TabFeature feature);
@@ -8,11 +8,21 @@ public interface PlayerPlaceholder extends Placeholder {
/**
* Calls the placeholder request function and returns the output.
* If the placeholder thrown an exception, it is logged in {@code placeholder-errors.log}
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
* file and "ERROR" is returned.
* @param p
* player to get placeholder value for
* @return value placeholder returned or "ERROR" if it threw an error
*
* @param p
* player to get placeholder value for
* @return value placeholder returned or "ERROR" if it threw an error
*/
Object request(TabPlayer p);
/**
* Returns last known value for given player.
*
* @param player
* player to get last value for
* @return last known value for entered player
*/
String getLastValue(TabPlayer player);
}
@@ -6,5 +6,28 @@ public interface RelationalPlaceholder extends Placeholder {
void updateValue(TabPlayer viewer, TabPlayer target, Object value);
/**
* Calls the placeholder request function and returns the output.
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
* file and "ERROR" is returned.
*
* @param viewer
* player looking at output of the placeholder
* @param target
* player the placeholder is displayed on
* @return value placeholder returned or "ERROR" if it threw an error
*/
Object request(TabPlayer viewer, TabPlayer target);
/**
* Returns last known value for given players. First player is viewer,
* second player is target.
*
* @param viewer
* viewer of the placeholder
* @param target
* target who is the text displayed on
* @return last known value for entered player duo
*/
String getLastValue(TabPlayer viewer, TabPlayer target);
}
@@ -4,5 +4,19 @@ public interface ServerPlaceholder extends Placeholder {
void updateValue(Object value);
/**
* Calls the placeholder request function and returns the output.
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
* file and "ERROR" is returned.
*
* @return value placeholder returned or "ERROR" if it threw an error
*/
Object request();
/**
* Returns last known value of the placeholder.
*
* @return last known value
*/
String getLastValue();
}
@@ -40,13 +40,13 @@ public class PacketBuilder {
* Converts custom packet into platform-specific packet by calling a function from
* {@link #buildMap}.
*
* @param packet
* Packet to build
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if reflection fails
* @param packet
* Packet to build
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if reflection fails
*/
public Object build(TabPacket packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
Preconditions.checkNotNull(packet, "packet");
@@ -57,13 +57,13 @@ public class PacketBuilder {
/**
* Constructs platform-specific PacketPlayOutBoss class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
@@ -72,13 +72,13 @@ public class PacketBuilder {
/**
* Constructs platform-specific PacketPlayOutChat class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
@@ -87,13 +87,13 @@ public class PacketBuilder {
/**
* Constructs platform-specific PacketPlayOutPlayerInfo class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
@@ -102,13 +102,13 @@ public class PacketBuilder {
/**
* Constructs platform-specific PacketPlayOutPlayerListHeaderFooter class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutPlayerListHeaderFooter packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
@@ -117,13 +117,13 @@ public class PacketBuilder {
/**
* Constructs platform-specific PacketPlayOutScoreboardDisplayObjective class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutScoreboardDisplayObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
@@ -132,13 +132,13 @@ public class PacketBuilder {
/**
* Constructs platform-specific PacketPlayOutScoreboardObjective class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutScoreboardObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
@@ -147,13 +147,13 @@ public class PacketBuilder {
/**
* Constructs platform-specific PacketPlayOutScoreboardScore class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
@@ -162,13 +162,13 @@ public class PacketBuilder {
/**
* Constructs platform-specific PacketPlayOutScoreboardTeam class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
@@ -179,11 +179,12 @@ public class PacketBuilder {
* and translates RGB to legacy colors. If string is not that long, the original string is returned.
* RGB codes are converted into legacy, since cutting is only needed for &lt;1.13.
* If {@code string} is {@code null}, empty string is returned.
* @param string
* String to cut
* @param length
* Length to cut to
* @return string cut to {@code length} characters
*
* @param string
* String to cut
* @param length
* Length to cut to
* @return string cut to {@code length} characters
*/
public String cutTo(String string, int length) {
if (string == null) return "";
@@ -207,11 +208,11 @@ public class PacketBuilder {
* If {@code clientVersion} is &lt; 1.12, the text is cut to {@code length} characters if
* needed and returned.
*
* @param text
* Text to convert
* @param clientVersion
* Version of player to convert text for
* @return serialized component for 1.13+ clients, cut string for 1.12-
* @param text
* Text to convert
* @param clientVersion
* Version of player to convert text for
* @return serialized component for 1.13+ clients, cut string for 1.12-
*/
public String jsonOrCut(String text, ProtocolVersion clientVersion, int length) {
if (text == null) return null;
@@ -226,13 +227,13 @@ public class PacketBuilder {
* Converts platform-specific instance of player info packet into
* {@link PacketPlayOutPlayerInfo} object.
*
* @param packet
* platform-specific info packet
* @param clientVersion
* Version of client receiving the packet
* @return The packet converted into {@link PacketPlayOutPlayerInfo}
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* platform-specific info packet
* @param clientVersion
* Version of client receiving the packet
* @return The packet converted into {@link PacketPlayOutPlayerInfo}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public PacketPlayOutPlayerInfo readPlayerInfo(Object packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return null;
@@ -242,11 +243,11 @@ public class PacketBuilder {
* Converts platform-specific instance of objective packet into
* {@link PacketPlayOutScoreboardObjective} object.
*
* @param packet
* platform-specific objective packet
* @return The packet converted into {@link PacketPlayOutScoreboardObjective}
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* platform-specific objective packet
* @return The packet converted into {@link PacketPlayOutScoreboardObjective}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public PacketPlayOutScoreboardObjective readObjective(Object packet) throws ReflectiveOperationException {
return null;
@@ -256,11 +257,11 @@ public class PacketBuilder {
* Converts platform-specific instance of display objective packet into
* {@link PacketPlayOutScoreboardDisplayObjective} object.
*
* @param packet
* platform-specific display objective packet
* @return The packet converted into {@link PacketPlayOutScoreboardDisplayObjective}
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* platform-specific display objective packet
* @return The packet converted into {@link PacketPlayOutScoreboardDisplayObjective}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object packet) throws ReflectiveOperationException {
return null;
@@ -42,16 +42,16 @@ public class PacketPlayOutBoss implements TabPacket {
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.ADD action
*
* @param id
* BossBar uuid
* @param name
* BossBar title
* @param pct
* BossBar progress
* @param color
* BossBar color
* @param overlay
* BossBar style
* @param id
* BossBar uuid
* @param name
* BossBar title
* @param pct
* BossBar progress
* @param color
* BossBar color
* @param overlay
* BossBar style
*/
public PacketPlayOutBoss(UUID id, String name, float pct, BarColor color, BarStyle overlay) {
Preconditions.checkNotNull(id, "uuid");
@@ -70,8 +70,8 @@ public class PacketPlayOutBoss implements TabPacket {
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.REMOVE action
*
* @param id
* BossBar uuid
* @param id
* BossBar uuid
*/
public PacketPlayOutBoss(UUID id) {
Preconditions.checkNotNull(id, "uuid");
@@ -83,10 +83,10 @@ public class PacketPlayOutBoss implements TabPacket {
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PCT action
*
* @param id
* BossBar uuid
* @param pct
* BossBar progress
* @param id
* BossBar uuid
* @param pct
* BossBar progress
*/
public PacketPlayOutBoss(UUID id, float pct) {
Preconditions.checkNotNull(id, "uuid");
@@ -99,10 +99,10 @@ public class PacketPlayOutBoss implements TabPacket {
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_NAME action
*
* @param id
* BossBar uuid
* @param name
* BossBar title
* @param id
* BossBar uuid
* @param name
* BossBar title
*/
public PacketPlayOutBoss(UUID id, String name) {
Preconditions.checkNotNull(id, "uuid");
@@ -116,12 +116,12 @@ public class PacketPlayOutBoss implements TabPacket {
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_STYLE action
*
* @param id
* BossBar uuid
* @param color
* BossBar color
* @param overlay
* BossBar style
* @param id
* BossBar uuid
* @param color
* BossBar color
* @param overlay
* BossBar style
*/
public PacketPlayOutBoss(UUID id, BarColor color, BarStyle overlay) {
Preconditions.checkNotNull(id, "uuid");
@@ -137,12 +137,12 @@ public class PacketPlayOutBoss implements TabPacket {
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PROPERTIES action
*
* @param darkenScreen
* Darken screen flag
* @param playMusic
* Play music flag
* @param createWorldFog
* Create fog flag
* @param darkenScreen
* Darken screen flag
* @param playMusic
* Play music flag
* @param createWorldFog
* Create fog flag
*/
public PacketPlayOutBoss(UUID id, boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
Preconditions.checkNotNull(id, "uuid");
@@ -159,7 +159,7 @@ public class PacketPlayOutBoss implements TabPacket {
* {@link #darkenScreen} adds {@code 1}, {@link #playMusic} {@code 2} and {@link #darkenScreen} {@code 4}
* to the final value.
*
* @return the bitmask
* @return the bitmask
*/
public byte getFlags(){
byte value = 0;
@@ -177,7 +177,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #color}
* @return color
*
* @return color
*/
public BarColor getColor() {
return color;
@@ -185,7 +186,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #overlay}
* @return style
*
* @return style
*/
public BarStyle getOverlay() {
return overlay;
@@ -193,7 +195,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #name}
* @return name
*
* @return name
*/
public String getName() {
return name;
@@ -201,7 +204,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #id}
* @return id
*
* @return id
*/
public UUID getId() {
return id;
@@ -209,7 +213,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #pct}
* @return progress
*
* @return progress
*/
public float getPct() {
return pct;
@@ -217,7 +222,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #action}
* @return packet action
*
* @return packet action
*/
public Action getAction() {
return action;
@@ -225,7 +231,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #darkenScreen}
* @return darkenScreen
*
* @return darkenScreen
*/
public boolean isDarkenScreen() {
return darkenScreen;
@@ -233,7 +240,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #createWorldFog}
* @return createWorldFog
*
* @return createWorldFog
*/
public boolean isCreateWorldFog() {
return createWorldFog;
@@ -241,7 +249,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Returns {@link #playMusic}
* @return playMusic
*
* @return playMusic
*/
public boolean isPlayMusic() {
return playMusic;
@@ -250,8 +259,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Sets {@link #darkenScreen} to specified value
*
* @param darkenScreen
* Darken screen flag
* @param darkenScreen
* Darken screen flag
*/
public void setDarkenScreen(boolean darkenScreen) {
this.darkenScreen = darkenScreen;
@@ -260,8 +269,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Sets {@link #createWorldFog} to specified value
*
* @param createWorldFog
* Create fog flag
* @param createWorldFog
* Create fog flag
*/
public void setCreateWorldFog(boolean createWorldFog) {
this.createWorldFog = createWorldFog;
@@ -270,8 +279,8 @@ public class PacketPlayOutBoss implements TabPacket {
/**
* Sets {@link #playMusic} to specified value
*
* @param playMusic
* Play music flag
* @param playMusic
* Play music flag
*/
public void setPlayMusic(boolean playMusic) {
this.playMusic = playMusic;
@@ -17,10 +17,10 @@ public class PacketPlayOutChat implements TabPacket {
/**
* Constructs new instance with given parameters
*
* @param message
* Chat message to be sent
* @param type
* Message position
* @param message
* Chat message to be sent
* @param type
* Message position
*/
public PacketPlayOutChat(IChatBaseComponent message, ChatMessageType type) {
Preconditions.checkNotNull(message, "message");
@@ -36,7 +36,8 @@ public class PacketPlayOutChat implements TabPacket {
/**
* Returns {@link #message}
* @return message
*
* @return message
*/
public IChatBaseComponent getMessage() {
return message;
@@ -44,7 +45,8 @@ public class PacketPlayOutChat implements TabPacket {
/**
* Returns {@link #type}
* @return type
*
* @return type
*/
public ChatMessageType getType() {
return type;
@@ -21,10 +21,10 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Constructs new instance with given parameters.
*
* @param action
* Packet action
* @param entries
* Affected entries
* @param action
* Packet action
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, PlayerInfoData... entries) {
Preconditions.checkNotNull(action, "action");
@@ -36,10 +36,10 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Constructs new instance with given parameters.
*
* @param action
* Packet action
* @param entries
* Affected entries
* @param action
* Packet action
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
Preconditions.checkNotNull(action, "action");
@@ -55,7 +55,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Returns {@link #action}
* @return packet action
*
* @return packet action
*/
public EnumPlayerInfoAction getAction() {
return action;
@@ -63,7 +64,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Returns {@link #entries}
* @return affected entries
*
* @return affected entries
*/
public List<PlayerInfoData> getEntries() {
return entries;
@@ -99,18 +101,18 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.ADD_PLAYER action
*
* @param name
* Player's name
* @param uniqueId
* Player's uuid
* @param skin
* Player's platform-specific skin object
* @param latency
* Player's ping
* @param gameMode
* Player's GameMode
* @param displayName
* Player's display name
* @param name
* Player's name
* @param uniqueId
* Player's uuid
* @param skin
* Player's platform-specific skin object
* @param latency
* Player's ping
* @param gameMode
* Player's GameMode
* @param displayName
* Player's display name
*/
public PlayerInfoData(String name, UUID uniqueId, Skin skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName) {
Preconditions.checkNotNull(uniqueId, "uuid");
@@ -126,10 +128,10 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_GAME_MODE action
*
* @param uniqueId
* Player's uuid
* @param gameMode
* Player's GameMode
* @param uniqueId
* Player's uuid
* @param gameMode
* Player's GameMode
*/
public PlayerInfoData(UUID uniqueId, EnumGamemode gameMode) {
Preconditions.checkNotNull(uniqueId, "uuid");
@@ -141,10 +143,10 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_LATENCY action
*
* @param uniqueId
* Player's uuid
* @param latency
* Player's ping
* @param uniqueId
* Player's uuid
* @param latency
* Player's ping
*/
public PlayerInfoData(UUID uniqueId, int latency) {
Preconditions.checkNotNull(uniqueId, "uuid");
@@ -156,10 +158,10 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_DISPLAY_NAME action
*
* @param uniqueId
* Player's uuid
* @param displayName
* Player's display name
* @param uniqueId
* Player's uuid
* @param displayName
* Player's display name
*/
public PlayerInfoData(UUID uniqueId, IChatBaseComponent displayName) {
Preconditions.checkNotNull(uniqueId, "uuid");
@@ -171,8 +173,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* Constructs new instance with given parameter. Suitable for
* {@link EnumPlayerInfoAction}.REMOVE_PLAYER action
*
* @param uniqueId
* Player's uuid
* @param uniqueId
* Player's uuid
*/
public PlayerInfoData(UUID uniqueId) {
this.uniqueId = uniqueId;
@@ -186,7 +188,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Returns {@link #latency}
* @return latency
*
* @return latency
*/
public int getLatency() {
return latency;
@@ -194,8 +197,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Sets {@link #latency} to specified value
* @param latency
* Latency to use
*
* @param latency
* Latency to use
*/
public void setLatency(int latency) {
this.latency = latency;
@@ -203,7 +207,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Returns {@link #displayName}
* @return displayName
*
* @return displayName
*/
public IChatBaseComponent getDisplayName() {
return displayName;
@@ -211,8 +216,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Sets {@link #displayName} to specified value
* @param displayName
* Display name to use
*
* @param displayName
* Display name to use
*/
public void setDisplayName(IChatBaseComponent displayName) {
this.displayName = displayName;
@@ -220,7 +226,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Returns {@link #uniqueId}
* @return uniqueId
*
* @return uniqueId
*/
public UUID getUniqueId() {
return uniqueId;
@@ -228,8 +235,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Sets {@link #uniqueId} to specified value
* @param uniqueId
* UUID to use
*
* @param uniqueId
* UUID to use
*/
public void setUniqueId(UUID uniqueId) {
this.uniqueId = uniqueId;
@@ -237,7 +245,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Returns {@link #name}
* @return name
*
* @return name
*/
public String getName() {
return name;
@@ -245,8 +254,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Sets {@link #name} to specified value
* @param name
* name to use
*
* @param name
* name to use
*/
public void setName(String name) {
this.name = name;
@@ -254,7 +264,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Returns {@link #gameMode}
* @return gameMode
*
* @return gameMode
*/
public EnumGamemode getGameMode() {
return gameMode;
@@ -262,8 +273,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Sets {@link #gameMode} to specified value
* @param gameMode
* GameMode to use
*
* @param gameMode
* GameMode to use
*/
public void setGameMode(EnumGamemode gameMode) {
this.gameMode = gameMode;
@@ -271,7 +283,8 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Returns {@link #skin}
* @return skin
*
* @return skin
*/
public Skin getSkin() {
return skin;
@@ -279,8 +292,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Sets {@link #skin} to specified value
* @param skin
* Skin to use
*
* @param skin
* Skin to use
*/
public void setSkin(Skin skin) {
this.skin = skin;
@@ -18,10 +18,10 @@ public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
* Constructs new instance with given parameters. They are converted to {@link IChatBaseComponent}
* using {@link IChatBaseComponent#optimizedComponent(String)} method.
*
* @param header
* TabList header
* @param footer
* TabList footer
* @param header
* TabList header
* @param footer
* TabList footer
*/
public PacketPlayOutPlayerListHeaderFooter(String header, String footer) {
Preconditions.checkNotNull(header, "header");
@@ -33,10 +33,10 @@ public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
/**
* Constructs new instance with given parameters.
*
* @param header
* TabList header
* @param footer
* TabList footer
* @param header
* TabList header
* @param footer
* TabList footer
*/
public PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent header, IChatBaseComponent footer) {
Preconditions.checkNotNull(header, "header");
@@ -52,7 +52,8 @@ public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
/**
* Returns {@link #header}
* @return header
*
* @return header
*/
public IChatBaseComponent getHeader() {
return header;
@@ -60,7 +61,8 @@ public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
/**
* Returns {@link #footer}
* @return footer
*
* @return footer
*/
public IChatBaseComponent getFooter() {
return footer;
@@ -20,12 +20,13 @@ public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
/**
* Constructs new instance with given parameters
* @param slot
* Display slot
* @param objectiveName
* Internal name of the objective
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*
* @param slot
* Display slot
* @param objectiveName
* Internal name of the objective
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*/
public PacketPlayOutScoreboardDisplayObjective(int slot, String objectiveName) {
Preconditions.checkNotNull(objectiveName, "objective name");
@@ -41,7 +42,8 @@ public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
/**
* Returns {@link #slot}
* @return slot
*
* @return slot
*/
public int getSlot() {
return slot;
@@ -49,7 +51,8 @@ public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
/**
* Returns {@link #objectiveName}
* @return objectiveName
*
* @return objectiveName
*/
public String getObjectiveName() {
return objectiveName;
@@ -27,14 +27,16 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
/**
* Constructs new instance with given parameters.
*
* @param action
* Packet action (0 = add, 1 = remove, 2 = update title)
* @param objectiveName
* objective name, up to 16 characters long
* @param displayName - title
* @param renderType - display type
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
* @param action
* Packet action (0 = add, 1 = remove, 2 = update title)
* @param objectiveName
* objective name, up to 16 characters long
* @param displayName
* title
* @param renderType
* display type
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*/
public PacketPlayOutScoreboardObjective(int action, String objectiveName, String displayName, EnumScoreboardHealthDisplay renderType) {
Preconditions.checkRange(action, 0, 2, "action");
@@ -50,10 +52,10 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
/**
* Constructs new packet with given objective name and 1 (unregister) action.
*
* @param objectiveName
* objective name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
* @param objectiveName
* objective name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*/
public PacketPlayOutScoreboardObjective(String objectiveName) {
Preconditions.checkNotNull(objectiveName, "objective name");
@@ -72,7 +74,8 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
/**
* Returns {@link #displayName}
* @return displayName
*
* @return displayName
*/
public String getDisplayName() {
return displayName;
@@ -80,7 +83,8 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
/**
* Returns {@link #objectiveName}
* @return objectiveName
*
* @return objectiveName
*/
public String getObjectiveName() {
return objectiveName;
@@ -88,7 +92,8 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
/**
* Returns {@link #renderType}
* @return renderType
*
* @return renderType
*/
public EnumScoreboardHealthDisplay getRenderType() {
return renderType;
@@ -96,7 +101,8 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
/**
* Returns {@link #action}
* @return packet action
*
* @return packet action
*/
public int getAction() {
return action;
@@ -22,16 +22,16 @@ public class PacketPlayOutScoreboardScore implements TabPacket {
/**
* Constructs new instance with given parameters
*
* @param action
* Packet action
* @param objectiveName
* Objective name
* @param player
* Affected player
* @param score
* Player's score
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
* @param action
* Packet action
* @param objectiveName
* Objective name
* @param player
* Affected player
* @param score
* Player's score
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*/
public PacketPlayOutScoreboardScore(Action action, String objectiveName, String player, int score) {
Preconditions.checkNotNull(action, "action");
@@ -52,7 +52,8 @@ public class PacketPlayOutScoreboardScore implements TabPacket {
/**
* Returns {@link #action}
* @return packet action
*
* @return packet action
*/
public Action getAction() {
return action;
@@ -60,7 +61,8 @@ public class PacketPlayOutScoreboardScore implements TabPacket {
/**
* Returns {@link #objectiveName}
* @return objective name
*
* @return objective name
*/
public String getObjectiveName() {
return objectiveName;
@@ -68,7 +70,8 @@ public class PacketPlayOutScoreboardScore implements TabPacket {
/**
* Returns {@link #player}
* @return player
*
* @return player
*/
public String getPlayer() {
return player;
@@ -76,7 +79,8 @@ public class PacketPlayOutScoreboardScore implements TabPacket {
/**
* Returns {@link #score}
* @return score
*
* @return score
*/
public int getScore() {
return score;
@@ -72,13 +72,13 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
* Constructs new instance with given parameters. Private constructor used
* internally to validate team name and set action.
*
* @param action
* Packet action (0 = create team, 1 = remove team, 2 = update team info,
* 3 = add entries, 4 = remove entries)
* @param name
* Team name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
* @param action
* Packet action (0 = create team, 1 = remove team, 2 = update team info,
* 3 = add entries, 4 = remove entries)
* @param name
* Team name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
private PacketPlayOutScoreboardTeam(int action, String name) {
Preconditions.checkNotNull(name, "team name");
@@ -90,22 +90,22 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Constructs new instance with given parameters and 0 (CREATE) action
*
* @param team
* Team name, up to 16 characters long
* @param prefix
* Prefix of players in team
* @param suffix
* Suffix of players in team
* @param visibility
* NameTag visibility rule
* @param collision
* Collision rule
* @param players
* Affected entities
* @param options
* bitmask of team options
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
* @param team
* Team name, up to 16 characters long
* @param prefix
* Prefix of players in team
* @param suffix
* Suffix of players in team
* @param visibility
* NameTag visibility rule
* @param collision
* Collision rule
* @param players
* Affected entities
* @param options
* bitmask of team options
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
public PacketPlayOutScoreboardTeam(String team, String prefix, String suffix, String visibility, String collision, Collection<String> players, int options) {
this(0, team);
@@ -125,10 +125,10 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Constructs new instance with given parameters and 1 (REMOVE) action
*
* @param team
* Team name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
* @param team
* Team name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
public PacketPlayOutScoreboardTeam(String team) {
this(1, team);
@@ -137,20 +137,20 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Constructs new instance with given parameters and 2 (UPDATE_TEAM_INFO) action
*
* @param team
* Team name, up to 16 characters long
* @param prefix
* Prefix of players in team
* @param suffix
* Suffix of players in team
* @param visibility
* NameTag visibility rule
* @param collision
* Collision rule
* @param options
* bitmask of team options
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
* @param team
* Team name, up to 16 characters long
* @param prefix
* Prefix of players in team
* @param suffix
* Suffix of players in team
* @param visibility
* NameTag visibility rule
* @param collision
* Collision rule
* @param options
* bitmask of team options
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
public PacketPlayOutScoreboardTeam(String team, String prefix, String suffix, String visibility, String collision, int options) {
this(2, team);
@@ -170,14 +170,14 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
* Constructs new instance with given parameters and 3 (ADD_PLAYERS) if {@code add} is {@code true},
* 4 (REMOVE_PLAYERS) if {@code add} is {@code false} action.
*
* @param team
* Team name, up to 16 characters long
* @param players
* Affected entities
* @param add
* {@code true} if players should be added, {@code false} if removed from team
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
* @param team
* Team name, up to 16 characters long
* @param players
* Affected entities
* @param add
* {@code true} if players should be added, {@code false} if removed from team
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
public PacketPlayOutScoreboardTeam(String team, Collection<String> players, boolean add) {
this(add ? 3 : 4, team);
@@ -194,7 +194,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #name}
* @return team name
*
* @return team name
*/
public String getName() {
return name;
@@ -202,7 +203,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #playerPrefix}
* @return playerPrefix
*
* @return playerPrefix
*/
public String getPlayerPrefix() {
return playerPrefix;
@@ -210,7 +212,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #playerSuffix}
* @return playerSuffix
*
* @return playerSuffix
*/
public String getPlayerSuffix() {
return playerSuffix;
@@ -218,7 +221,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #nameTagVisibility}
* @return nameTagVisibility
*
* @return nameTagVisibility
*/
public String getNameTagVisibility() {
return nameTagVisibility;
@@ -226,7 +230,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #collisionRule}
* @return collisionRule
*
* @return collisionRule
*/
public String getCollisionRule() {
return collisionRule;
@@ -235,8 +240,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Sets {@link #color} to specified color
*
* @param color
* color to use
* @param color
* color to use
*/
public void setColor(EnumChatFormat color) {
this.color = color;
@@ -244,7 +249,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #color}
* @return color
*
* @return color
*/
public EnumChatFormat getColor() {
return color;
@@ -252,7 +258,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #players}
* @return players
*
* @return players
*/
public Collection<String> getPlayers() {
return players;
@@ -260,7 +267,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #options}
* @return team options
*
* @return team options
*/
public int getOptions() {
return options;
@@ -268,7 +276,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Returns {@link #action}
* @return packet action
*
* @return packet action
*/
public int getAction() {
return action;
@@ -10,25 +10,28 @@ public class Skin {
/** Skin value */
private final String value;
/** Skin signature */
/** Skin signature, can be null */
private final String signature;
/**
* Constructs new instance with given parameters
*
* @param value
* skin value
* @param signature
* skin signature
* @throws IllegalArgumentException
* if {@code value} is {@code null}
*/
public Skin(String value, String signature) {
Preconditions.checkNotNull(value, "value");
Preconditions.checkNotNull(signature, "signature");
this.value = value;
this.signature = signature;
}
/**
* Returns skin value
*
* @return skin value
*/
public String getValue() {
@@ -37,6 +40,7 @@ public class Skin {
/**
* Returns skin signature
*
* @return skin signature
*/
public String getSignature() {
@@ -45,6 +49,7 @@ public class Skin {
/**
* Returns user-friendly representation of this object
*
* @return user-friendly representation of this object
*/
@Override
@@ -10,7 +10,8 @@ public interface TabPacket {
/**
* Returns user-friendly string representation of the packet
* @return user-friendly string representation of the packet
*
* @return user-friendly string representation of the packet
*/
String toString();
}
@@ -11,7 +11,7 @@ public interface Line {
* Returns configured raw text of this line. Placeholders
* remain in raw format.
*
* @return raw text of this line
* @return raw text of this line
*/
String getText();
@@ -22,8 +22,8 @@ public interface Line {
* keep placeholders up to date. If value is identical to previous
* one, nothing happens.
*
* @param text
* Raw text to display in the line
* @param text
* Raw text to display in the line
*/
void setText(String text);
}
@@ -15,7 +15,7 @@ public interface Scoreboard {
* scoreboards from config use name they were defined with. This value is
* used internally and in /tab announce scoreboard command.
*
* @return custom name of this scoreboard
* @return custom name of this scoreboard
*/
String getName();
@@ -23,8 +23,8 @@ public interface Scoreboard {
* Returns raw title of this scoreboard. Placeholders stay in their
* raw format.
*
* @return scoreboard title
* @see #setTitle(String)
* @return scoreboard title
* @see #setTitle(String)
*/
String getTitle();
@@ -39,9 +39,9 @@ public interface Scoreboard {
* <p>
* Calling this method with same title as before will not do anything.
*
* @param title
* New title to use with placeholder support
* @see #getTitle()
* @param title
* New title to use with placeholder support
* @see #getTitle()
*/
void setTitle(String title);
@@ -50,9 +50,9 @@ public interface Scoreboard {
* in game (first line is on top). This list should only be used for reading,
* for adding/removing lines see {@link #addLine(String)} and {@link #removeLine(int)}.
*
* @return list of lines in this scoreboard
* @see #addLine(String)
* @see #removeLine(int)
* @return list of lines in this scoreboard
* @see #addLine(String)
* @see #removeLine(int)
*/
List<Line> getLines();
@@ -60,10 +60,10 @@ public interface Scoreboard {
* Adds line with specified text on the bottom of scoreboard. Supports
* placeholders, which will automatically be refreshed.
*
* @param text
* Text to display
* @see #getLines()
* @see #removeLine(int)
* @param text
* Text to display
* @see #getLines()
* @see #removeLine(int)
*/
void addLine(String text);
@@ -71,12 +71,12 @@ public interface Scoreboard {
* Removes line with specified index. Index starts at {@code 0} and ends at
* {@link #getLines()}.size()-1.
*
* @param index
* Index of line to remove, starting at 0
* @throws IndexOutOfBoundsException
* if the index is out of range (index &lt; 0 || index >= {@link #getLines()}.size())
* @see #getLines()
* @see #addLine(String)
* @param index
* Index of line to remove, starting at 0
* @throws IndexOutOfBoundsException
* if the index is out of range (index &lt; 0 || index >= {@link #getLines()}.size())
* @see #getLines()
* @see #addLine(String)
*/
void removeLine(int index);
@@ -29,15 +29,15 @@ public interface ScoreboardManager {
* Client can only see up to 15 lines at a time, however you can define
* more, if some of them only consist of placeholder that might be empty.
*
* @param name
* Internal name of the scoreboard
* @param title
* The scoreboard title
* @param lines
* Lines of text in scoreboard (supports placeholders)
* @return The new scoreboard with given parameters
* @throws NullPointerException
* if {@code name} or {@code lines} is {@code null}
* @param name
* Internal name of the scoreboard
* @param title
* The scoreboard title
* @param lines
* Lines of text in scoreboard (supports placeholders)
* @return The new scoreboard with given parameters
* @throws NullPointerException
* if {@code name} or {@code lines} is {@code null}
*/
Scoreboard createScoreboard(String name, String title, List<String> lines);
@@ -47,7 +47,7 @@ public interface ScoreboardManager {
* or using {@link #createScoreboard(String, String, List)}.
* Map value is reference to the actual scoreboard.
*
* @return map of registered scoreboards
* @return map of registered scoreboards
*/
Map<String, Scoreboard> getRegisteredScoreboards();
@@ -57,14 +57,14 @@ public interface ScoreboardManager {
* {@link #resetScoreboard(TabPlayer)}. If the player already sees
* this scoreboard, nothing will happen.
*
* @param player
* Player to send scoreboard to
* @param scoreboard
* Scoreboard to display
* @throws NullPointerException
* if {@code player} or {@code scoreboard} is {@code null}
* @see #hasCustomScoreboard(TabPlayer)
* @see #resetScoreboard(TabPlayer)
* @param player
* Player to send scoreboard to
* @param scoreboard
* Scoreboard to display
* @throws NullPointerException
* if {@code player} or {@code scoreboard} is {@code null}
* @see #hasCustomScoreboard(TabPlayer)
* @see #resetScoreboard(TabPlayer)
*/
void showScoreboard(TabPlayer player, Scoreboard scoreboard);
@@ -72,12 +72,12 @@ public interface ScoreboardManager {
* Returns {@code true} if player has custom scoreboard set using
* {@link #showScoreboard(TabPlayer, Scoreboard)}, {@code false} if not.
*
* @param player
* Player to check
* @return {@code true} if player has custom scoreboard set using API,
* {@code false} if not
* @see #showScoreboard(TabPlayer, Scoreboard)
* @see #resetScoreboard(TabPlayer)
* @param player
* Player to check
* @return {@code true} if player has custom scoreboard set using API,
* {@code false} if not
* @see #showScoreboard(TabPlayer, Scoreboard)
* @see #resetScoreboard(TabPlayer)
*/
boolean hasCustomScoreboard(TabPlayer player);
@@ -86,10 +86,10 @@ public interface ScoreboardManager {
* and re-enables internal display logic with conditions. If player does not
* have any forced scoreboard, nothing happens.
*
* @param player
* Player to hide custom scoreboard from
* @see #showScoreboard(TabPlayer, Scoreboard)
* @see #hasCustomScoreboard(TabPlayer)
* @param player
* Player to hide custom scoreboard from
* @see #showScoreboard(TabPlayer, Scoreboard)
* @see #hasCustomScoreboard(TabPlayer)
*/
void resetScoreboard(TabPlayer player);
@@ -99,11 +99,11 @@ public interface ScoreboardManager {
* {@link #toggleScoreboard(TabPlayer, boolean)} or
* {@link #setScoreboardVisible(TabPlayer, boolean, boolean)}.
*
* @param player
* Player to get visibility status of
* @return {@code true} if visible, {@code false} if disabled
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
* @see #toggleScoreboard(TabPlayer, boolean)
* @param player
* Player to get visibility status of
* @return {@code true} if visible, {@code false} if disabled
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
* @see #toggleScoreboard(TabPlayer, boolean)
*/
boolean hasScoreboardVisible(TabPlayer player);
@@ -113,14 +113,14 @@ public interface ScoreboardManager {
* visibility, scoreboard is toggled. If {@code sendToggleMessage} is {@code true},
* toggle message defined in configuration is sent.
*
* @param player
* Player to set visibility for
* @param visible
* New visibility status
* @param sendToggleMessage
* whether to send toggle message if status changed or not
* @see #toggleScoreboard(TabPlayer, boolean)
* @see #hasScoreboardVisible(TabPlayer)
* @param player
* Player to set visibility for
* @param visible
* New visibility status
* @param sendToggleMessage
* whether to send toggle message if status changed or not
* @see #toggleScoreboard(TabPlayer, boolean)
* @see #hasScoreboardVisible(TabPlayer)
*/
void setScoreboardVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
@@ -129,12 +129,12 @@ public interface ScoreboardManager {
* hides it. If hidden, shows it. If {@code sendToggleMessage} is {@code true},
* toggle message defined in configuration is sent.
*
* @param player
* Player to toggle scoreboard for
* @param sendToggleMessage
* Whether to send toggle message or not
* @see #hasScoreboardVisible(TabPlayer)
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
* @param player
* Player to toggle scoreboard for
* @param sendToggleMessage
* Whether to send toggle message or not
* @see #hasScoreboardVisible(TabPlayer)
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
*/
void toggleScoreboard(TabPlayer player, boolean sendToggleMessage);
@@ -143,13 +143,13 @@ public interface ScoreboardManager {
* time (in milliseconds). Scoreboard name is either defined in config, or
* through API in {@link #createScoreboard(String, String, List)}.
*
* @param scoreboard
* Scoreboard from config or registered via API
* @param duration
* Duration in milliseconds
* @throws IllegalArgumentException
* if no scoreboard was found with such name or {@code duration}
* is &lt; 0.
* @param scoreboard
* Scoreboard from config or registered via API
* @param duration
* Duration in milliseconds
* @throws IllegalArgumentException
* if no scoreboard was found with such name or {@code duration}
* is &lt; 0.
*/
void announceScoreboard(String scoreboard, int duration);
@@ -157,9 +157,10 @@ public interface ScoreboardManager {
* Returns player's currently displayed scoreboard. This can be either with
* configuration, overridden with commands or the API. Will return {@code null}
* if player does not see any scoreboard due to not meeting any display condition.
* @param player
* player to get active scoreboard of
* @return player's active scoreboard or {@code null} if player has no scoreboard
*
* @param player
* player to get active scoreboard of
* @return player's active scoreboard or {@code null} if player has no scoreboard
*/
Scoreboard getActiveScoreboard(TabPlayer player);
}
@@ -8,32 +8,47 @@ public interface ThreadManager {
/**
* Starts a task in new thread and measures how long it took to process
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
*
* @param feature
* feature to add cpu usage to
* @param type
* usage type to add cpu usage to
* @param task
* the task
*/
Future<Void> runMeasuredTask(TabFeature feature, String type, Runnable task);
/**
* Starts a task in new thread and measures how long it took to process
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
*
* @param feature
* feature to add cpu usage to
* @param type
* usage type to add cpu usage to
* @param task
* the task
*/
Future<Void> runMeasuredTask(String feature, String type, Runnable task);
/**
* Runs task in a new thread
* @param task - the task
*
* @param task
* the task
*/
Future<Void> runTask(Runnable task);
/**
* Starts a new task with defined repeat interval that measures cpu usage
* @param intervalMilliseconds - task interval
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
*
* @param intervalMilliseconds
* task interval
* @param feature
* feature to add cpu usage to
* @param type
* usage type to add cpu usage to
* @param task
* the task
*/
RepeatingTask startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task);
@@ -41,13 +56,16 @@ public interface ThreadManager {
/**
* Runs task with a delay and measures how long it took to process
* @param delayMilliseconds - how long to run the task after
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
* @return future allowing to cancel the task
*
* @param delayMilliseconds
* how long to run the task after
* @param feature
* feature to add cpu usage to
* @param type
* usage type to add cpu usage to
* @param task
* the task
* @return future allowing to cancel the task
*/
Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task);
Future<?> runTaskLater(int delayMilliseconds, Runnable task);
}
@@ -6,92 +6,138 @@ public interface TeamManager {
/**
* Makes player's NameTag globally invisible
* @see #showNametag(TabPlayer)
* @see #hasHiddenNametag(TabPlayer)
*
* @param player
* player to hide nametag of
* @see #showNametag(TabPlayer)
* @see #hasHiddenNametag(TabPlayer)
*/
void hideNametag(TabPlayer player);
/**
* Hides player's NameTag for specified player until it's shown again
* @param viewer - player to hide NameTag for
*
* @param player
* player to hide nametag of
* @param viewer
* player to hide NameTag for
*/
void hideNametag(TabPlayer player, TabPlayer viewer);
/**
* Makes player's NameTag visible again
* @see #hideNametag(TabPlayer)
* @see #hasHiddenNametag(TabPlayer)
*
* @param player
* player to show nametag of
* @see #hideNametag(TabPlayer)
* @see #hasHiddenNametag(TabPlayer)
*/
void showNametag(TabPlayer player);
/**
* Shows player's NameTag for specified viewer if it was hidden before
* @param viewer - player to show NameTag back for
*
* @param player
* player to show nametag of
* @param viewer
* player to show NameTag back for
*/
void showNametag(TabPlayer player, TabPlayer viewer);
/**
* Return whether player has hidden NameTag or not
* @return Whether player has hidden NameTag or not
* @see #hideNametag(TabPlayer)
* @see #showNametag(TabPlayer)
*
* @param player
* player to check nametag visibility status of
* @return Whether player has hidden NameTag or not
* @see #hideNametag(TabPlayer)
* @see #showNametag(TabPlayer)
*/
boolean hasHiddenNametag(TabPlayer player);
/**
* Returns true if NameTag is hidden for specified viewer, false if not
* @param viewer - player to check visibility status for
* @return true if hidden, false if not
*
* @param player
* player to check visibility status of
* @param viewer
* player to check visibility status for
* @return true if hidden, false if not
*/
boolean hasHiddenNametag(TabPlayer player, TabPlayer viewer);
/**
* Unregisters player's team and no longer handles it, as well as disables anti-override for teams.
* This can be resumed using resumeTeamHandling(). If team handling was paused already, nothing happens.
*
* @param player
* player to pause team handling of
*/
void pauseTeamHandling(TabPlayer player);
/**
* Resumes team handling if it was before paused using pauseTeamHandling(), if not, nothing happens
*
* @param player
* player to resume team handling of
*/
void resumeTeamHandling(TabPlayer player);
/**
* Returns true if team handling is paused for this player using pauseTeamHandling(), false if not, or
* it was resumed already using resumeTeamHandling
* @return true if paused, false if not
*
* @param player
* player to check handling status of
* @return true if paused, false if not
*/
boolean hasTeamHandlingPaused(TabPlayer player);
/**
* Forces new team name for the player until this method is called again with null argument and
* performs all actions to change player's team name
* @param teamName - forced team name
*
* @param player
* player to set team name of
* @param teamName
* forced team name
*/
void forceTeamName(TabPlayer player, String teamName);
/**
* Returns forced team name of player or null if not forced
* @return forced team name of player or null if not forced
*
* @param player
* player to check forced team name of
* @return forced team name of player or null if not forced
*/
String getForcedTeamName(TabPlayer player);
/**
* Forces collision rule for the player. Setting it to null will remove forced value
* @param collision - forced collision rule
*
* @param player
* player to set collision rule of
* @param collision
* forced collision rule
*/
void setCollisionRule(TabPlayer player, Boolean collision);
/**
* Returns forced collision rule or null if collision is not forced using setCollisionRule
* @return forced value or null if not forced
*
* @param player
* player to get forced collision of
* @return forced value or null if not forced
*/
Boolean getCollisionRule(TabPlayer player);
/**
* Sends update team's properties packet of player's team to everyone
* @param player - player to update team data of
*
* @param player
* player to update team data of
*/
void updateTeamData(TabPlayer player);
@@ -116,10 +162,10 @@ public interface TeamManager {
* On first call, nametags of all players will become invisible for specified
* player. On second call, they will become visible again.
*
* @param player
* player to toggle nametag visibility view for
* @param sendToggleMessage
* {@code true} if configured toggle message should be sent, {@code false} if not
* @param player
* player to toggle nametag visibility view for
* @param sendToggleMessage
* {@code true} if configured toggle message should be sent, {@code false} if not
*/
void toggleNameTagVisibilityView(TabPlayer player, boolean sendToggleMessage);
@@ -128,9 +174,9 @@ public interface TeamManager {
* {@link #toggleNameTagVisibilityView(TabPlayer, boolean)} or using a command,
* {@code false} if not.
*
* @param player
* player to check
* @return {@code true} if hidden, {@code false} if not
* @param player
* player to check
* @return {@code true} if hidden, {@code false} if not
*/
boolean hasHiddenNameTagVisibilityView(TabPlayer player);
}
@@ -5,12 +5,12 @@ package me.neznamy.tab.api.util;
* Used in {@link me.neznamy.tab.api.protocol.PacketBuilder} to
* call build method for packet classes, which may throw an
* exception due to reflection.
* @param <A>
* First function argument
* @param <B>
* Second function argument
* @param <C>
* Function return type
* @param <A>
* First function argument
* @param <B>
* Second function argument
* @param <C>
* Function return type
*/
public interface BiFunctionWithException<A, B, C> {
@@ -1,5 +1,7 @@
package me.neznamy.tab.api.util;
import me.neznamy.tab.api.TabPlayer;
public class Preconditions {
private Preconditions(){}
@@ -17,4 +19,8 @@ public class Preconditions {
if (number.doubleValue() < min.doubleValue() || number.doubleValue() > max.doubleValue())
throw new IllegalArgumentException(variable + " index out of range (" + min + " - " + max + ")");
}
public static void checkLoaded(TabPlayer player) {
if (!player.isLoaded()) throw new IllegalStateException("Player is not loaded yet. Try again later.");
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ val platforms = setOf(
projects.bukkit,
projects.bungeecord,
// projects.krypton,
projects.velocity
// projects.velocity
).map { it.dependencyProject }
val special = setOf(
+3 -1
View File
@@ -7,5 +7,7 @@ dependencies {
compileOnly(libs.via)
compileOnly(libs.authlib)
compileOnly(libs.libsDisguises)
compileOnly(libs.essentials)
compileOnly(libs.essentials) {
exclude("org.spigotmc", "spigot-api")
}
}
@@ -15,15 +15,24 @@ import me.neznamy.tab.shared.TAB;
*/
public class BukkitEventListener implements Listener {
/** Platform instance */
private final BukkitPlatform platform;
/**
* Constructs new instance with given parameter
*
* @param platform
* Platform instance
*/
public BukkitEventListener(BukkitPlatform platform) {
this.platform = platform;
}
/**
* Listener to PlayerQuitEvent to remove player data and forward the event to features
* @param e quit event
*
* @param e
* quit event
*/
@EventHandler(priority = EventPriority.HIGHEST)
public void onQuit(PlayerQuitEvent e){
@@ -33,7 +42,9 @@ public class BukkitEventListener implements Listener {
/**
* Listener to PlayerJoinEvent to create player data and forward the event to features
* @param e join event
*
* @param e
* join event
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent e) {
@@ -43,7 +54,9 @@ public class BukkitEventListener implements Listener {
/**
* Listener to PlayerChangedWorldEvent to forward the event to features
* @param e world changed event
*
* @param e
* world changed event
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldChange(PlayerChangedWorldEvent e){
@@ -53,7 +66,9 @@ public class BukkitEventListener implements Listener {
/**
* Listener to PlayerChangedWorldEvent to forward the event to features
* @param e command preprocess event
*
* @param e
* command preprocess event
*/
@EventHandler
public void onCommand(PlayerCommandPreprocessEvent e) {
@@ -6,26 +6,21 @@ import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
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.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.chat.ChatClickable.EnumClickAction;
import me.neznamy.tab.api.chat.ChatComponentEntity;
import me.neznamy.tab.api.chat.ChatHoverable.EnumHoverAction;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.chat.TextColor;
import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
@@ -40,17 +35,17 @@ import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitPacketBuilder extends PacketBuilder {
//nms storage
/** NMS data storage */
private final NMSStorage nms = NMSStorage.getInstance();
//entity type ids
/** Map of entity type ids for entities used by the plugin */
private final EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
/** Component cache for better performance (1.16+ players) */
private final Map<IChatBaseComponent, Object> componentCacheModern = new HashMap<>();
private final Map<IChatBaseComponent, Object> componentCacheLegacy = new HashMap<>();
private Object emptyScoreboard;
private Object dummyEntity;
/** Component cache for better performance (1.15- players) */
private final Map<IChatBaseComponent, Object> componentCacheLegacy = new HashMap<>();
/**
* Constructs new instance
@@ -65,18 +60,6 @@ public class BukkitPacketBuilder extends PacketBuilder {
entityIds.put(EntityType.ARMOR_STAND, 30);
}
}
try {
emptyScoreboard = nms.newScoreboard.newInstance();
} catch (ReflectiveOperationException e) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Failed to create instance of \"Scoreboard\""));
}
if (nms.getMinorVersion() >= 8) {
try {
dummyEntity = nms.newEntityArmorStand.newInstance(nms.World_getHandle.invoke(Bukkit.getWorlds().get(0)), 0, 0, 0);
} catch (ReflectiveOperationException e) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Failed to create instance of \"EntityArmorStand\""));
}
}
buildMap.put(PacketPlayOutEntityMetadata.class, (packet, version) -> build((PacketPlayOutEntityMetadata)packet));
buildMap.put(PacketPlayOutEntityTeleport.class, (packet, version) -> build((PacketPlayOutEntityTeleport)packet));
buildMap.put(PacketPlayOutEntityDestroy.class, (packet, version) -> build((PacketPlayOutEntityDestroy)packet));
@@ -108,7 +91,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (nms.getMinorVersion() == 7) {
return nms.newPacketPlayOutChat.newInstance(component);
}
throw new IllegalStateException("Not supported on <1.7");
return packet;
}
@Override
@@ -178,7 +161,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (packet.getAction() == PacketPlayOutScoreboardScore.Action.REMOVE) {
return nms.newPacketPlayOutScoreboardScore_String.newInstance(packet.getPlayer());
}
Object score = nms.newScoreboardScore.newInstance(emptyScoreboard, newScoreboardObjective(packet.getObjectiveName()), packet.getPlayer());
Object score = nms.newScoreboardScore.newInstance(nms.emptyScoreboard, newScoreboardObjective(packet.getObjectiveName()), packet.getPlayer());
nms.ScoreboardScore_setScore.invoke(score, packet.getScore());
if (nms.getMinorVersion() >= 8) {
return nms.newPacketPlayOutScoreboardScore.newInstance(score);
@@ -189,7 +172,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
@Override
public Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.PacketPlayOutScoreboardTeam == null) return null; //fabric
Object team = nms.newScoreboardTeam.newInstance(emptyScoreboard, packet.getName());
Object team = nms.newScoreboardTeam.newInstance(nms.emptyScoreboard, packet.getName());
String prefix = packet.getPlayerPrefix();
String suffix = packet.getPlayerSuffix();
if (clientVersion.getMinorVersion() < 13) {
@@ -224,10 +207,13 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Builds entity destroy packet with given parameter
* @return destroy packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* Builds entity destroy packet from custom packet class
*
* @param packet
* Destroy packet
* @return NMS destroy packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityDestroy packet) throws ReflectiveOperationException {
try {
@@ -238,21 +224,32 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
}
/**
* Builds entity metadata packet from custom packet class
*
* @param packet
* Metadata packet
* @return NMS metadata packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityMetadata packet) throws ReflectiveOperationException {
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
}
/**
* Builds entity spawn packet with given parameters
* Builds entity spawn packet from custom packet class
*
* @return entity spawn packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* @param packet
* Spawn packet
* @return NMS spawn packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutSpawnEntityLiving packet) throws ReflectiveOperationException {
Object nmsPacket;
if (nms.getMinorVersion() >= 17) {
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance(dummyEntity);
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance(nms.dummyEntity);
} else {
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance();
}
@@ -277,15 +274,18 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Builds entity teleport packet with given parameters
* @return entity teleport packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
* Builds entity teleport packet from custom packet class
*
* @param packet
* Teleport packet
* @return NMS teleport packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityTeleport packet) throws ReflectiveOperationException {
Object nmsPacket;
if (nms.getMinorVersion() >= 17) {
nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance(dummyEntity);
nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance(nms.dummyEntity);
} else {
nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance();
}
@@ -303,7 +303,23 @@ public class BukkitPacketBuilder extends PacketBuilder {
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_PITCH, (byte) (packet.getLocation().getPitch()/360*256));
return nmsPacket;
}
/**
* Writes data into NMS team from custom team packet. Used on 1.13+ servers.
*
* @param packet
* Packet to read from
* @param clientVersion
* Version of player
* @param team
* Team to write to
* @param prefix
* Prefix to use
* @param suffix
* Suffix to use
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private void createTeamModern(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion, Object team, String prefix, String suffix) throws ReflectiveOperationException {
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));
@@ -313,6 +329,20 @@ public class BukkitPacketBuilder extends PacketBuilder {
nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
}
/**
* Writes data into NMS team from custom team packet. Used on 1.12- servers.
*
* @param packet
* Packet to read from
* @param team
* Team to write to
* @param prefix
* Prefix to use
* @param suffix
* Suffix to use
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
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);
@@ -330,7 +360,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
IChatBaseComponent listName = nmsComponent == null ? null : fromNMSComponent(nmsComponent);
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();
@@ -360,11 +390,14 @@ public class BukkitPacketBuilder extends PacketBuilder {
/**
* Builds entity BossBar packet
* @param packet - packet to build
* @param clientVersion - client version
* @return entity BossBar packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*
* @param packet
* packet to build
* @param clientVersion
* client version
* @return entity BossBar packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private Object buildBossPacketEntity(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (packet.getAction() == Action.UPDATE_STYLE) return null; //nothing to do here
@@ -392,8 +425,10 @@ public class BukkitPacketBuilder extends PacketBuilder {
/**
* A method taken from minecraft code used to convert double to int
* @param paramDouble double value
* @return int value
*
* @param paramDouble
* double value
* @return int value
*/
private int floor(double paramDouble){
int i = (int)paramDouble;
@@ -401,95 +436,25 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Converts minecraft IChatBaseComponent into TAB's component class. Currently, does not support show_item hover event on 1.16+.
* @param component - component to convert
* @return converted component
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private IChatBaseComponent fromNMSComponent(Object component) throws ReflectiveOperationException {
if (!nms.ChatComponentText.isInstance(component)) return null; //paper
IChatBaseComponent chat = new IChatBaseComponent((String) nms.ChatComponentText_text.get(component));
Object modifier = nms.ChatBaseComponent_modifier.get(component);
if (modifier != null) {
chat.getModifier().setColor(fromNMSColor(nms.ChatModifier_color.get(modifier)));
chat.getModifier().setBold((Boolean) nms.ChatModifier_bold.get(modifier));
chat.getModifier().setItalic((Boolean) nms.ChatModifier_italic.get(modifier));
chat.getModifier().setObfuscated((Boolean) nms.ChatModifier_obfuscated.get(modifier));
chat.getModifier().setStrikethrough((Boolean) nms.ChatModifier_strikethrough.get(modifier));
chat.getModifier().setUnderlined((Boolean) nms.ChatModifier_underlined.get(modifier));
Object clickEvent = nms.ChatModifier_clickEvent.get(modifier);
if (clickEvent != null) {
chat.getModifier().onClick(EnumClickAction.valueOf(nms.ChatClickable_action.get(clickEvent).toString().toUpperCase()), (String) nms.ChatClickable_value.get(clickEvent));
}
Object hoverEvent = nms.ChatModifier_hoverEvent.get(modifier);
if (hoverEvent != null) {
EnumHoverAction action;
IChatBaseComponent value;
if (nms.getMinorVersion() >= 16) {
//does not support show_item on 1.16+
JsonObject json = (JsonObject) nms.ChatHoverable_serialize.invoke(hoverEvent);
action = EnumHoverAction.valueOf(json.get("action").getAsString().toUpperCase());
value = IChatBaseComponent.deserialize(json.get("contents").getAsJsonObject().toString());
} else {
action = EnumHoverAction.valueOf(nms.ChatHoverable_getAction.invoke(hoverEvent).toString().toUpperCase());
value = fromNMSComponent(nms.ChatHoverable_getValue.invoke(hoverEvent));
}
chat.getModifier().onHover(action, value);
}
}
for (Object extra : (List<Object>) nms.ChatBaseComponent_extra.get(component)) {
chat.addExtra(fromNMSComponent(extra));
}
return chat;
}
private TextColor fromNMSColor(Object color) throws ReflectiveOperationException {
if (color == null) return null;
if (nms.getMinorVersion() >= 16) {
String name = (String) nms.ChatHexColor_name.get(color);
if (name != null) {
//legacy code
return new TextColor(EnumChatFormat.valueOf(name.toUpperCase(Locale.US)));
} else {
int rgb = (int) nms.ChatHexColor_rgb.get(color);
return new TextColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);
}
} else {
return new TextColor(EnumChatFormat.valueOf(((Enum)color).name()));
}
}
/**
* Converts TAB's IChatBaseComponent into minecraft's component. Currently, does not support hover event.
* @param component - component to convert
* @param clientVersion - client version used to decide RGB conversion
* @return converted component
* @throws ReflectiveOperationException
* if thrown by reflective operation
* Converts TAB's IChatBaseComponent into minecraft's component.
*
* @param component
* component to convert
* @param clientVersion
* client version used to decide RGB conversion
* @return converted component
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object toNMSComponent(IChatBaseComponent component, ProtocolVersion clientVersion) throws ReflectiveOperationException {
Object obj;
if (clientVersion.getMinorVersion() >= 16) {
if (componentCacheModern.containsKey(component)) return componentCacheModern.get(component);
obj = toNMSComponent0(component, clientVersion);
if (componentCacheModern.size() > 10000) componentCacheModern.clear();
componentCacheModern.put(component, obj);
} else {
if (componentCacheLegacy.containsKey(component)) return componentCacheLegacy.get(component);
obj = toNMSComponent0(component, clientVersion);
if (componentCacheLegacy.size() > 10000) componentCacheLegacy.clear();
componentCacheLegacy.put(component, obj);
}
return obj;
}
//separate method to prevent extras counting cpu again due to recursion and finally showing higher usage than real
private Object toNMSComponent0(IChatBaseComponent component, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (component == null) return null;
if (component instanceof WrappedChatComponent) return ((WrappedChatComponent) component).get();
Map<IChatBaseComponent, Object> cache = clientVersion.getMinorVersion() >= 16 ? componentCacheModern : componentCacheLegacy;
if (cache.containsKey(component)) return cache.get(component);
if (cache.size() > 10000) cache.clear();
Object chat = nms.newChatComponentText.newInstance(component.getText());
Object modifier;
Object clickEvent = component.getModifier().getClickEvent() == null ? null : nms.newChatClickable.newInstance(Enum.valueOf((Class<Enum>) nms.EnumClickAction,
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;
@@ -504,17 +469,17 @@ public class BukkitPacketBuilder extends PacketBuilder {
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, toNMSComponent0(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, toNMSComponent0(component.getModifier().getHoverEvent().getValue(), clientVersion));
break;
default:
break;
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(
@@ -535,17 +500,27 @@ public class BukkitPacketBuilder extends PacketBuilder {
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()), toNMSComponent0(component.getModifier().getHoverEvent().getValue(), clientVersion)));
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, toNMSComponent0(extra, clientVersion));
nms.ChatComponentText_addSibling.invoke(chat, toNMSComponent(extra, clientVersion));
}
cache.put(component, chat);
return chat;
}
/**
* Creates a new Scoreboard Objective with given name.
*
* @param objectiveName
* Objective name
* @return NMS Objective
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private Object newScoreboardObjective(String objectiveName) throws ReflectiveOperationException {
if (nms.getMinorVersion() >= 13) {
return nms.newScoreboardObjective.newInstance(null, objectiveName, null, nms.newChatComponentText.newInstance(""), null);
@@ -9,16 +9,18 @@ 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.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PipelineInjector;
import org.jetbrains.annotations.NotNull;
/**
* Pipeline injection for bukkit
*/
public class BukkitPipelineInjector extends PipelineInjector {
//nms storage
/** NMS data storage */
private final NMSStorage nms = NMSStorage.getInstance();
/**
@@ -38,15 +40,16 @@ public class BukkitPipelineInjector extends PipelineInjector {
*/
public class BukkitChannelDuplexHandler extends ChannelDuplexHandler {
//injected player
/** Injected player */
private final TabPlayer player;
/**
* Constructs new instance with given player
* @param player - player to inject
*
* @param player
* player to inject
*/
public BukkitChannelDuplexHandler(TabPlayer player) {
Preconditions.checkNotNull(player, "player");
this.player = player;
}
@@ -68,7 +71,9 @@ public class BukkitPipelineInjector extends PipelineInjector {
return;
}
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam != null && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
long time = System.nanoTime();
modifyPlayers(packet);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
super.write(context, packet, channelPromise);
return;
}
@@ -91,13 +96,16 @@ public class BukkitPipelineInjector extends PipelineInjector {
/**
* Removes all real players from team if packet does not come from TAB and reports this to override log
* @param packetPlayOutScoreboardTeam - team packet
* @throws ReflectiveOperationException
* nmsGameMode
*
* @param packetPlayOutScoreboardTeam
* team packet
* @throws ReflectiveOperationException
* nmsGameMode
*/
@SuppressWarnings("unchecked")
private void modifyPlayers(Object packetPlayOutScoreboardTeam) throws ReflectiveOperationException {
long time = System.nanoTime();
int action = nms.PacketPlayOutScoreboardTeam_ACTION.getInt(packetPlayOutScoreboardTeam);
if (action == 1 || action == 2 || action == 4) return;
Collection<String> players = (Collection<String>) nms.PacketPlayOutScoreboardTeam_PLAYERS.get(packetPlayOutScoreboardTeam);
String teamName = (String) nms.PacketPlayOutScoreboardTeam_NAME.get(packetPlayOutScoreboardTeam);
if (players == null) return;
@@ -117,9 +125,17 @@ public class BukkitPipelineInjector extends PipelineInjector {
}
}
nms.setField(packetPlayOutScoreboardTeam, nms.PacketPlayOutScoreboardTeam_PLAYERS, newList);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
/**
* Returns player with matching game profile name. This is different from
* real name when a nick plugin changing names of players is used. If no
* player was found, returns {@code null}.
*
* @param name
* Game profile name
* @return Player with matching game profile name
*/
private TabPlayer getPlayer(String name) {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (p.getNickname().equals(name)) return p;
@@ -5,6 +5,7 @@ import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.util.Locale;
import me.neznamy.tab.shared.TabConstants;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@@ -13,7 +14,6 @@ import org.bukkit.plugin.RegisteredServiceProvider;
import com.earth2me.essentials.Essentials;
import me.neznamy.tab.api.placeholder.PlaceholderManager;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
import net.milkbowl.vault.chat.Chat;
@@ -25,26 +25,22 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
/** Number formatter for 2 decimal places */
private final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
private Object chat;
private final Plugin essentials = Bukkit.getPluginManager().getPlugin("Essentials");
private Object chat; // Object because Fabric
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
private Object server;
private Field recentTps;
private boolean paperTps;
private boolean paperMspt;
private Method playerIsAfk;
private Method paperTps;
private Method paperMspt;
private Method purpurIsAfk;
/**
* Constructs new instance with given parameter
*/
public BukkitPlaceholderRegistry() {
numberFormat.setMaximumFractionDigits(2);
try {
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
RegisteredServiceProvider<?> rspChat = Bukkit.getServicesManager().getRegistration(Class.forName("net.milkbowl.vault.chat.Chat"));
if (rspChat != null) chat = rspChat.getProvider();
}
} catch (ClassNotFoundException e) {
//modded server without vault
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
RegisteredServiceProvider<?> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
if (rspChat != null) chat = rspChat.getProvider();
}
try {
server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
@@ -52,70 +48,50 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
} catch (ReflectiveOperationException e) {
//not spigot
}
try {
Bukkit.class.getMethod("getTPS");
paperTps = true;
} catch (NoSuchMethodException e) {
//not paper
}
try {
Bukkit.class.getMethod("getAverageTickTime");
paperMspt = true;
} catch (NoSuchMethodException e) {
//not paper
}
try {
playerIsAfk = Player.class.getMethod("isAfk");
} catch (NoSuchMethodException e) {
//not purpur
}
try { paperTps = Bukkit.class.getMethod("getTPS"); } catch (NoSuchMethodException ignored) {}
try { paperMspt = Bukkit.class.getMethod("getAverageTickTime"); } catch (NoSuchMethodException ignored) {}
try { purpurIsAfk = Player.class.getMethod("isAfk"); } catch (NoSuchMethodException ignored) {}
}
@SuppressWarnings("deprecation")
@Override
public void registerPlaceholders(PlaceholderManager manager) {
super.registerPlaceholders(manager);
manager.registerPlayerPlaceholder("%displayname%", 500, p -> ((Player) p.getPlayer()).getDisplayName());
if (paperTps) {
manager.registerServerPlaceholder("%tps%", 1000, () -> formatTPS(Bukkit.getTPS()[0]));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((Player) p.getPlayer()).getDisplayName());
if (paperTps != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> formatTPS(Bukkit.getTPS()[0]));
} else if (recentTps != null) {
manager.registerServerPlaceholder("%tps%", 1000, () -> {
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> {
try {
return formatTPS(((double[]) recentTps.get(server))[0]);
} catch (IllegalAccessException e) {
return "-1";
return -1;
}
});
} else {
manager.registerServerPlaceholder("%tps%", -1, () -> "-1").enableTriggerMode();
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, -1, () -> -1);
}
if (paperMspt) {
manager.registerServerPlaceholder("%mspt%", 1000, () -> numberFormat.format(Bukkit.getAverageTickTime()));
if (paperMspt != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.MSPT, 1000, () -> numberFormat.format(Bukkit.getAverageTickTime()));
}
manager.registerPlayerPlaceholder("%afk%", 500, p -> {
manager.registerPlayerPlaceholder(TabConstants.Placeholder.AFK, 500, p -> {
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
if (playerIsAfk == null) return false;
try {
return playerIsAfk.invoke(p.getPlayer());
} catch (ReflectiveOperationException exception) {
TAB.getInstance().getErrorManager().printError("Failed to get AFK status of " + p.getName() + " using Purpur", exception);
}
return false;
return purpurIsAfk != null && ((Player)p.getPlayer()).isAfk();
});
manager.registerPlayerPlaceholder("%essentialsnick%", 1000, p -> {
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, 1000, p -> {
String nickname = null;
if (essentials != null)
nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
return nickname == null || nickname.length() == 0 ? p.getName() : nickname;
});
if (chat != null) {
manager.registerPlayerPlaceholder("%vault-prefix%", 1000, p -> ((Chat) chat).getPlayerPrefix((Player) p.getPlayer()));
manager.registerPlayerPlaceholder("%vault-suffix%", 1000, p -> ((Chat) chat).getPlayerSuffix((Player) p.getPlayer()));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, 1000, p -> ((Chat) chat).getPlayerPrefix((Player) p.getPlayer()));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, 1000, p -> ((Chat) chat).getPlayerSuffix((Player) p.getPlayer()));
} else {
manager.registerServerPlaceholder("%vault-prefix%", -1, () -> "").enableTriggerMode();
manager.registerServerPlaceholder("%vault-suffix%", -1, () -> "").enableTriggerMode();
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, -1, () -> "");
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, -1, () -> "");
}
manager.registerPlayerPlaceholder("%health%", 100, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.HEALTH, 100, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
}
private String formatTPS(double tps) {
@@ -4,7 +4,7 @@ import com.earth2me.essentials.Essentials;
import com.viaversion.viaversion.api.Via;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.util.Preconditions;
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.platforms.bukkit.features.BukkitTabExpansion;
@@ -41,31 +41,31 @@ public class BukkitPlatform extends Platform {
private final JavaPlugin plugin;
/** Variables checking presence of other plugins to hook into */
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled("LibsDisguises");
private final Plugin essentials = Bukkit.getPluginManager().getPlugin("Essentials");
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("ProtocolSupport");
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
/**
* Constructs new instance with given plugin parameter
* @param plugin
* plugin instance
*
* @param plugin
* plugin instance
*/
public BukkitPlatform(JavaPlugin plugin) {
super(new BukkitPacketBuilder());
Preconditions.checkNotNull(plugin, "plugin");
this.plugin = plugin;
}
@Override
public PermissionPlugin detectPermissionPlugin() {
if (Bukkit.getPluginManager().isPluginEnabled("LuckPerms")) {
return new LuckPerms(getPluginVersion("LuckPerms"));
} else if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LUCKPERMS)) {
return new LuckPerms(getPluginVersion(TabConstants.Plugin.LUCKPERMS));
} else if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
RegisteredServiceProvider<Permission> provider = Bukkit.getServicesManager().getRegistration(Permission.class);
if (provider == null) return new None();
return new Vault(provider.getProvider(), getPluginVersion("Vault"));
return new Vault(provider.getProvider(), getPluginVersion(TabConstants.Plugin.VAULT));
} else {
return new None();
}
@@ -73,22 +73,16 @@ public class BukkitPlatform extends Platform {
@Override
public void loadFeatures() {
if (Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VIAVERSION)) {
try {
Class.forName("com.viaversion.viaversion.api.Via");
viaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion");
viaVersion = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.VIAVERSION);
} catch (ClassNotFoundException e) {
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion("ViaVersion") + ") was detected.", true);
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion(TabConstants.Plugin.VIAVERSION) + ") was detected.", true);
TAB.getInstance().sendConsoleMessage("&cTAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
TAB.getInstance().sendConsoleMessage("&cThis might cause problems, such as limitations still being present for latest MC clients as well as RGB not working.", true);
}
}
if (Bukkit.getPluginManager().isPluginEnabled("Tablisknu")) {
TAB.getInstance().sendConsoleMessage("&cDetected plugin \"Tablisknu\", which causes TAB to not work properly. Consider removing the plugin.", true);
}
if (Bukkit.getPluginManager().isPluginEnabled("SkBee")) {
TAB.getInstance().sendConsoleMessage("&cDetected plugin \"SkBee\", which causes TAB's scoreboard to not show. Consider removing the plugin.", true);
}
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
@@ -124,14 +118,14 @@ public class BukkitPlatform extends Platform {
@Override
public String getPluginVersion(String plugin) {
Preconditions.checkNotNull(plugin, "plugin");
Plugin pl = Bukkit.getPluginManager().getPlugin(plugin);
return pl == null ? null : pl.getDescription().getVersion();
}
/**
* Returns online players from Bukkit API
* @return online players from Bukkit API
*
* @return online players from Bukkit API
*/
@SuppressWarnings("unchecked")
private Player[] getOnlinePlayers() {
@@ -152,7 +146,6 @@ public class BukkitPlatform extends Platform {
@Override
public void registerUnknownPlaceholder(String identifier) {
Preconditions.checkNotNull(identifier, "identifier");
PlaceholderManagerImpl pl = TAB.getInstance().getPlaceholderManager();
if (identifier.startsWith("%rel_")) {
//relational placeholder
@@ -201,7 +194,8 @@ public class BukkitPlatform extends Platform {
/**
* Returns status of LibsDisguises plugin presence
* @return {@code true} if plugin is enabled, {@code false} if not
*
* @return {@code true} if plugin is enabled, {@code false} if not
*/
public boolean isLibsDisguisesEnabled() {
return libsDisguises;
@@ -211,8 +205,9 @@ public class BukkitPlatform extends Platform {
* Sets LibsDisguises presence status to provided value. This is used
* to disable LibsDisguises hook in case the plugin is not correctly loaded
* for any reason to avoid error spam in the hook.
* @param enabled
* New status of LibsDisguises presence
*
* @param enabled
* New status of LibsDisguises presence
*/
public void setLibsDisguisesEnabled(boolean enabled) {
libsDisguises = enabled;
@@ -220,17 +215,13 @@ public class BukkitPlatform extends Platform {
/**
* Returns Essentials' main class if the plugin is installed, {@code null} if not
* @return Essentials instance or {@code null} if plugin is not installed
*
* @return Essentials instance or {@code null} if plugin is not installed
*/
public Essentials getEssentials() {
return (Essentials) essentials;
}
@Override
public boolean isProxy() {
return false;
}
@Override
public String getConfigName() {
return "bukkitconfig.yml";
@@ -238,9 +229,10 @@ public class BukkitPlatform extends Platform {
/**
* Gets protocol version of requested player and returns it.
* @param player
* Player to get protocol version of
* @return protocol version of the player
*
* @param player
* Player to get protocol version of
* @return protocol version of the player
*/
public int getProtocolVersion(Player player) {
if (protocolSupport){
@@ -256,15 +248,16 @@ public class BukkitPlatform extends Platform {
/**
* Returns protocol version of requested player using ProtocolSupport
* @param player
* Player to get protocol version of
* @return protocol version of the player using ProtocolSupport
*
* @param player
* Player to get protocol version of
* @return protocol version of the player using ProtocolSupport
*/
private int getProtocolVersionPS(Player player){
try {
Object protocolVersion = Class.forName("protocolsupport.api.ProtocolSupportAPI").getMethod("getProtocolVersion", Player.class).invoke(null, player);
int version = (int) protocolVersion.getClass().getMethod("getId").invoke(protocolVersion);
TAB.getInstance().debug("ProtocolSupport returned protocol version " + version + " for " + player.getName() + "(online=" + player.isOnline() + ")");
TAB.getInstance().debug("ProtocolSupport returned protocol version " + version + " for " + player.getName() + " (online=" + player.isOnline() + ")");
return version;
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ProtocolSupport", player.getName()), e);
@@ -274,9 +267,10 @@ public class BukkitPlatform extends Platform {
/**
* Returns protocol version of requested player using ViaVersion
* @param player
* Player to get protocol version of
* @return protocol version of the player using ViaVersion
*
* @param player
* Player to get protocol version of
* @return protocol version of the player using ViaVersion
*/
private int getProtocolVersionVia(Player player, int retryLevel){
try {
@@ -290,7 +284,7 @@ public class BukkitPlatform extends Platform {
Thread.sleep(5);
return getProtocolVersionVia(player, retryLevel + 1);
}
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + player.getName() + "(online=" + player.isOnline() + ")");
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + player.getName() + " (online=" + player.isOnline() + ")");
return version;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -300,4 +294,18 @@ public class BukkitPlatform extends Platform {
return TAB.getInstance().getServerVersion().getNetworkId();
}
}
/**
* Sends console message using ConsoleCommandSender, due to
* Paper not translating colors correctly in Logger messages
*
* @param message
* Message to send
* @param translateColors
* Whether color codes should be translated or not
*/
@Override
public void sendConsoleMessage(String message, boolean translateColors) {
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ? EnumChatFormat.color(message) : message));
}
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.platforms.bukkit;
import java.util.*;
import com.mojang.authlib.properties.Property;
import me.neznamy.tab.api.protocol.PacketPlayOutChat;
import me.neznamy.tab.api.protocol.Skin;
import me.neznamy.tab.api.util.Preconditions;
import org.bukkit.Bukkit;
@@ -48,20 +49,19 @@ public class BukkitTabPlayer extends ITabPlayer {
/**
* Constructs new instance with given bukkit player and protocol version
* @param p
* bukkit player
* @param protocolVersion
* Player's protocol network id
*
* @param p
* bukkit player
* @param protocolVersion
* Player's protocol network id
*/
public BukkitTabPlayer(Player p, int protocolVersion){
super(p, p.getUniqueId(), p.getName(), "N/A", p.getWorld().getName(), protocolVersion);
try {
handle = NMSStorage.getInstance().getHandle.invoke(player);
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
if (NMSStorage.getInstance().CHANNEL != null)
channel = (Channel) NMSStorage.getInstance().CHANNEL.get(NMSStorage.getInstance().NETWORK_MANAGER.get(playerConnection));
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get playerConnection or channel of " + p.getName(), e);
TAB.getInstance().getErrorManager().printError("Failed to get playerConnection of " + p.getName(), e);
}
}
@@ -96,6 +96,8 @@ public class BukkitTabPlayer extends ITabPlayer {
} else {
handleVia((PacketPlayOutBoss) nmsPacket);
}
} else if (nmsPacket instanceof PacketPlayOutChat) {
getPlayer().sendMessage(((PacketPlayOutChat) nmsPacket).getMessage().toLegacyText());
} else {
NMSStorage.getInstance().sendPacket.invoke(playerConnection, nmsPacket);
}
@@ -109,8 +111,9 @@ public class BukkitTabPlayer extends ITabPlayer {
* Handles PacketPlayOutBoss packet send request using Bukkit API,
* since the API offers everything we need and makes us not need to
* deal with NMS code at all.
* @param packet
* packet request to handle using Bukkit API
*
* @param packet
* packet request to handle using Bukkit API
*/
private void handle(PacketPlayOutBoss packet) {
BossBar bar = bossBars.get(packet.getId());
@@ -157,8 +160,9 @@ public class BukkitTabPlayer extends ITabPlayer {
/**
* Handles PacketPlayOutBoss packet request for 1.9+ clients on
* 1.8 servers using ViaVersion API instead of using Wither.
* @param packet
* packet request to handle using ViaVersion API
*
* @param packet
* packet request to handle using ViaVersion API
*/
private void handleVia(PacketPlayOutBoss packet) {
com.viaversion.viaversion.api.legacy.bossbar.BossBar bar;
@@ -202,12 +206,13 @@ public class BukkitTabPlayer extends ITabPlayer {
/**
* Sets BossBar flag to requested target value.
* @param bar
* BossBar to set flag of
* @param targetValue
* Target value of the flag
* @param flag
* Flag to set value of
*
* @param bar
* BossBar to set flag of
* @param targetValue
* Target value of the flag
* @param flag
* Flag to set value of
*/
private void processFlag(BossBar bar, boolean targetValue, BarFlag flag) {
if (targetValue && !bar.hasFlag(flag)) bar.addFlag(flag);
@@ -216,12 +221,13 @@ public class BukkitTabPlayer extends ITabPlayer {
/**
* Sets BossBar flag to requested target value.
* @param bar
* BossBar to set flag of
* @param targetValue
* Target value of the flag
* @param flag
* Flag to set value of
*
* @param bar
* BossBar to set flag of
* @param targetValue
* Target value of the flag
* @param flag
* Flag to set value of
*/
private void processFlagVia(com.viaversion.viaversion.api.legacy.bossbar.BossBar bar, boolean targetValue, BossFlag flag) {
if (targetValue && !bar.hasFlag(flag)) bar.addFlag(flag);
@@ -283,4 +289,15 @@ public class BukkitTabPlayer extends ITabPlayer {
public int getGamemode() {
return getPlayer().getGameMode().getValue();
}
@Override
public Channel getChannel() {
try {
if (NMSStorage.getInstance().CHANNEL != null)
return (Channel) NMSStorage.getInstance().CHANNEL.get(NMSStorage.getInstance().NETWORK_MANAGER.get(playerConnection));
} catch (IllegalAccessException e) {
TAB.getInstance().getErrorManager().printError("Failed to get channel of " + getName(), e);
}
return null;
}
}
@@ -26,24 +26,25 @@ public class Main extends JavaPlugin {
@Override
public void onEnable(){
getLogger().info(EnumChatFormat.color("&7Server version: " + Bukkit.getBukkitVersion().split("-")[0] + " (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")"));
String version = Bukkit.getBukkitVersion().split("-")[0];
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Server version: " + version + " (" + serverPackage + ")"));
if (!isVersionSupported()){
Bukkit.getPluginManager().disablePlugin(this);
return;
}
BukkitPlatform platform = new BukkitPlatform(this);
TAB.setInstance(new TAB(platform, ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]),
Bukkit.getBukkitVersion().split("-")[0] + " (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")", getDataFolder(), getLogger()));
TAB.setInstance(new TAB(platform, ProtocolVersion.fromFriendlyName(version), version + " (" + serverPackage + ")", getDataFolder(), getLogger()));
if (TAB.getInstance().getServerVersion() == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
getLogger().info(EnumChatFormat.color("&cUnknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Unknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
}
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(platform), this);
TAB.getInstance().load();
Metrics metrics = new Metrics(this, 5304);
metrics.addCustomChart(new SimplePie("unlimited_nametag_mode_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("placeholderapi", () -> Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI") ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("permission_system", () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie("server_version", () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.UNLIMITED_NAME_TAG_MODE_ENABLED, () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PLACEHOLDER_API, () -> Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM, () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.SERVER_VERSION, () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
PluginCommand cmd = Bukkit.getPluginCommand("tab");
if (cmd == null) return;
TABCommand command = new TABCommand();
@@ -53,7 +54,7 @@ public class Main extends JavaPlugin {
@Override
public void onDisable() {
//null check due to L31 return making L34 not run
//null check due to L34 return making L37 not run
if (TAB.getInstance() != null) TAB.getInstance().unload();
}
@@ -61,7 +62,8 @@ public class Main extends JavaPlugin {
* Initializes all used NMS classes, constructors, fields and methods.
* Returns {@code true} if everything went successfully and version is marked as compatible,
* {@code false} if anything went wrong or version is not marked as compatible.
* @return {@code true} if server version is compatible, {@code false} if not
*
* @return {@code true} if server version is compatible, {@code false} if not
*/
private boolean isVersionSupported(){
List<String> supportedVersions = Arrays.asList(
@@ -74,16 +76,17 @@ public class Main extends JavaPlugin {
long time = System.currentTimeMillis();
NMSStorage.setInstance(new NMSStorage());
if (supportedVersions.contains(serverPackage)) {
getLogger().info(EnumChatFormat.color("&7Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
return true;
} else {
getLogger().info(EnumChatFormat.color("&cNo 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.x - 1.18.2. Disabling just to stay safe."));
}
} catch (Exception ex) {
if (supportedVersions.contains(serverPackage)) {
getLogger().log(Level.SEVERE, EnumChatFormat.color("&cYour server version is marked as compatible, but a compatibility issue was found. Please report the error below (include your server version & fork too)"), ex);
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report the error below (include your server version & fork too)"));
getLogger().log(Level.SEVERE, "", ex);
} else {
getLogger().info(EnumChatFormat.color("&cYour 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.x - 1.18.2. Disabling."));
}
}
return false;
@@ -92,7 +95,7 @@ public class Main extends JavaPlugin {
/**
* Command handler for /tab command
*/
public static class TABCommand implements CommandExecutor, TabCompleter {
private static class TABCommand implements CommandExecutor, TabCompleter {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
@@ -26,11 +26,6 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
return true;
}
@Override
public boolean canRegister(){
return true;
}
@Override
public @NotNull String getAuthor(){
return "NEZNAMY";
@@ -48,48 +43,22 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
@Override
public String onPlaceholderRequest(Player player, @NotNull String identifier){
if (player == null) return "";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (identifier.startsWith("replace_")) {
String placeholder = "%" + identifier.substring(8) + "%";
String output = PlaceholderAPI.setPlaceholders(player, placeholder);
return TAB.getInstance().getPlaceholderManager().findReplacement(placeholder, output);
}
return values.computeIfAbsent(p, pl -> new HashMap<>()).get(identifier);
if (identifier.startsWith("placeholder_")) {
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", TAB.getInstance().getPlaceholderManager());
}
if (player == null) return "<Player cannot be null>";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
return values.get(p).get(identifier);
}
@Override
public void setScoreboardVisible(TabPlayer player, boolean visible) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("scoreboard_visible", visible ? "Enabled" : "Disabled");
}
@Override
public void setScoreboardName(TabPlayer player, String name) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("scoreboard_name", name);
}
@Override
public void setBossBarVisible(TabPlayer player, boolean visible) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("bossbar_visible", visible ? "Enabled" : "Disabled");
}
@Override
public void setNameTagPreview(TabPlayer player, boolean previewing) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("ntpreview", previewing ? "Enabled" : "Disabled");
}
@Override
public void setPlaceholderValue(TabPlayer player, String placeholder, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put("placeholder_" + placeholder.substring(1, placeholder.length()-1), value);
}
@Override
public void setPropertyValue(TabPlayer player, String property, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(property, value);
}
@Override
public void setRawPropertyValue(TabPlayer player, String property, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(property + "_raw", value);
public void setValue(TabPlayer player, String key, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
}
}
@@ -34,7 +34,9 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
/**
* Constructs new instance with given parameters and loads config options
* @param plugin - plugin instance
*
* @param plugin
* plugin instance
*/
public PerWorldPlayerList(JavaPlugin plugin) {
super("Per world PlayerList", null);
@@ -50,7 +50,8 @@ public class PetFix extends TabFeature {
/**
* Returns position of pet owner field based on server version
* @return position of pet owner field based on server version
*
* @return position of pet owner field based on server version
*/
private int getPetOwnerPosition() {
if (nms.getMinorVersion() >= 17) {
@@ -72,21 +73,20 @@ public class PetFix extends TabFeature {
}
/**
* Cancels a packet if previous one arrived with no delay to prevent double toggle on 1.16
* @throws ReflectiveOperationException
* if thrown by reflective operation
* Cancels a packet if previous one arrived with no delay to prevent double toggle since 1.16
*
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@Override
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
if (nms.PacketPlayInUseEntity.isInstance(packet)) {
if (lastInteractFix.containsKey(sender) && (System.currentTimeMillis() - lastInteractFix.get(sender) < INTERACT_COOLDOWN)) {
if (nms.PacketPlayInUseEntity.isInstance(packet) && isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
if (System.currentTimeMillis() - lastInteractFix.getOrDefault(sender, 0L) < INTERACT_COOLDOWN) {
//last interact packet was sent right now, cancelling to prevent double-toggle due to this feature enabled
return true;
}
if (isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
//this is the first packet, saving player so the next packet can be cancelled
lastInteractFix.put(sender, System.currentTimeMillis());
}
//this is the first packet, saving player so the next packet can be cancelled
lastInteractFix.put(sender, System.currentTimeMillis());
}
return false;
}
@@ -101,8 +101,9 @@ public class PetFix extends TabFeature {
/**
* Removes pet owner field from DataWatcher
* @throws ReflectiveOperationException
* if thrown by reflective operation
*
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@SuppressWarnings("unchecked")
@Override
@@ -116,7 +117,7 @@ public class PetFix extends TabFeature {
if (item == null) continue;
if (nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item)) == petOwnerPosition) {
Object value = nms.DataWatcherItem_VALUE.get(item);
if (value instanceof java.util.Optional || value instanceof com.google.common.base.Optional) {
if (value instanceof Optional || value instanceof com.google.common.base.Optional) {
removedEntry = item;
}
}
@@ -130,7 +131,7 @@ public class PetFix extends TabFeature {
//<1.15
DataWatcher watcher = DataWatcher.fromNMS(nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.get(packet));
DataWatcherItem petOwner = watcher.getItem(petOwnerPosition);
if (petOwner != null && (petOwner.getValue() instanceof java.util.Optional || petOwner.getValue() instanceof com.google.common.base.Optional)) {
if (petOwner != null && (petOwner.getValue() instanceof Optional || petOwner.getValue() instanceof com.google.common.base.Optional)) {
watcher.removeValue(petOwnerPosition);
nms.setField(packet, nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER, watcher.toNMS());
}
@@ -1,6 +1,5 @@
package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.api.util.Preconditions;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -27,10 +26,11 @@ public class WitherBossBar extends BossBarManagerImpl implements Listener {
/**
* Constructs a new instance of the class
* @param plugin - plugin instance
*
* @param plugin
* plugin instance
*/
public WitherBossBar(JavaPlugin plugin) {
Preconditions.checkNotNull(plugin, "plugin");
Bukkit.getPluginManager().registerEvents(this, plugin);
//when MC is on fullscreen, BossBar disappears after 1 second of not being seen
//when in a small window, it's about 100ms
@@ -172,8 +172,10 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns teleport packet for specified viewer
* @param viewer - player to get location for
* @return teleport packet
*
* @param viewer
* player to get location for
* @return teleport packet
*/
public PacketPlayOutEntityTeleport getTeleportPacket(TabPlayer viewer) {
return new PacketPlayOutEntityTeleport(entityId, getArmorStandLocationFor(viewer));
@@ -190,7 +192,8 @@ 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 true if armor stand should be visible, false if not
*/
public boolean getVisibility() {
if (manager.isArmorStandsAlwaysVisible()) return true;
@@ -200,7 +203,8 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns general location where armor stand should be at time of calling
* @return Location where armor stand should be for everyone
*
* @return Location where armor stand should be for everyone
*/
public Location getLocation() {
double x = player.getLocation().getX();
@@ -220,7 +224,8 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns Y where player is based on player's vehicle due to bukkit API bug
* @return correct player's Y
*
* @return correct player's Y
*/
protected double getY() {
//1.14+ server sided bug
@@ -253,9 +258,12 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Creates data watcher with specified display name for viewer
* @param displayName - armor stand name
* @param viewer - player to apply checks against
* @return DataWatcher for viewer
*
* @param displayName
* armor stand name
* @param viewer
* player to apply checks against
* @return DataWatcher for viewer
*/
public DataWatcher createDataWatcher(String displayName, TabPlayer viewer) {
DataWatcher datawatcher = new DataWatcher();
@@ -280,8 +288,10 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns true if display name is in fact empty, for example only containing color codes
* @param displayName - string to check
* @return true if it's empty, false if not
*
* @param displayName
* string to check
* @return true if it's empty, false if not
*/
private boolean isNameVisiblyEmpty(String displayName) {
if (displayName.length() == 0) return true;
@@ -293,8 +303,10 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns list of packets to send to make armor stand spawn with metadata
* @param viewer - viewer to apply relational placeholders for
* @return List of packets that spawn the armor stand
*
* @param viewer
* viewer to apply relational placeholders for
* @return List of packets that spawn the armor stand
*/
public TabPacket[] getSpawnPackets(TabPlayer viewer) {
visible = getVisibility();
@@ -313,8 +325,10 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns location where armor stand should be for specified viewer
* @param viewer - player to get location for
* @return location of armor stand
*
* @param viewer
* player to get location for
* @return location of armor stand
*/
public Location getArmorStandLocationFor(TabPlayer viewer) {
return viewer.getVersion().getMinorVersion() == 8 && !manager.isMarkerFor18x() ? getLocation().clone().add(0,-2,0) : getLocation();
@@ -323,12 +337,8 @@ public class BukkitArmorStand implements ArmorStand {
@Override
public void respawn(TabPlayer viewer) {
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
Runnable spawn = () -> spawn(viewer);
if (viewer.getVersion().getMinorVersion() == 8) {
//1.8.0 client sided bug
TAB.getInstance().getCPUManager().runTaskLater(50, manager, TabConstants.CpuUsageCategory.V1_8_0_BUG_COMPENSATION, spawn);
} else {
spawn.run();
}
// 1.8.0 will not see entity that respawned in the same tick
// creating new delayed task every time someone sneaks can be abused and cause OOM
spawn(viewer);
}
}
@@ -46,12 +46,21 @@ public class BukkitArmorStandManager implements ArmorStandManager {
/**
* Teleports armor stands to player's current location for specified viewer
* @param viewer - player to teleport armor stands for
*
* @param viewer
* player to teleport armor stands for
*/
public void teleport(TabPlayer viewer) {
for (ArmorStand a : armorStandArray) a.teleport(viewer);
}
/**
* Teleports armor stands to player's current location for all nearby players
*/
public void teleport() {
for (ArmorStand a : armorStandArray) a.teleport();
}
/**
* Returns array of nearby players
* @return array of nearby players
@@ -62,9 +71,10 @@ public class BukkitArmorStandManager implements ArmorStandManager {
/**
* Returns {@code true} if requested player is nearby, {@code false} if not
* @param viewer
* Player to check for
* @return {@code true} if player nearby, {@code false} if not
*
* @param viewer
* Player to check for
* @return {@code true} if player nearby, {@code false} if not
*/
public boolean isNearby(TabPlayer viewer) {
return nearbyPlayers.contains(viewer);
@@ -72,8 +82,10 @@ public class BukkitArmorStandManager implements ArmorStandManager {
/**
* Returns true if manager contains armor stand with specified entity id, false if not
* @param entityId - entity id
* @return true if armor stand with specified entity id exists, false if not
*
* @param entityId
* entity id
* @return true if armor stand with specified entity id exists, false if not
*/
public boolean hasArmorStandWithID(int entityId) {
for (ArmorStand a : armorStandArray) {
@@ -86,19 +98,14 @@ public class BukkitArmorStandManager implements ArmorStandManager {
/**
* Sets sneak value of armor stands to specified value
* @param sneaking - new sneaking status
*
* @param sneaking
* new sneaking status
*/
public void sneak(boolean sneaking) {
for (ArmorStand a : armorStandArray) a.sneak(sneaking);
}
/**
* Teleports armor stands to player's current location for all nearby players
*/
public void teleport() {
for (ArmorStand a : armorStandArray) a.teleport();
}
/**
* Performs respawn operation on all armor stands to skip teleport animation
*/
@@ -112,7 +119,9 @@ public class BukkitArmorStandManager implements ArmorStandManager {
/**
* Spawns all armor stands for specified viewer and adds them into nearby players
* @param viewer - player to spawn armor stands for
*
* @param viewer
* player to spawn armor stands for
*/
public void spawn(TabPlayer viewer) {
nearbyPlayers.add(viewer);
@@ -121,15 +130,6 @@ public class BukkitArmorStandManager implements ArmorStandManager {
for (ArmorStand a : armorStandArray) a.spawn(viewer);
}
/**
* Sends destroy packet of all armor stands to specified player and removes them from nearby players list
* @param viewer - player to destroy armor stands for
*/
public void destroy(TabPlayer viewer) {
for (ArmorStand a : armorStandArray) a.destroy(viewer);
unregisterPlayer(viewer);
}
/**
* Fixes heights of all armor stands due to dynamic lines
*/
@@ -147,10 +147,10 @@ public class BukkitArmorStandManager implements ArmorStandManager {
/**
* Adds armor stand to list and registers it to all nearby players
*
* @param name
* Unique identifier of the text line
* @param as
* Armor stand to add
* @param name
* Unique identifier of the text line
* @param as
* Armor stand to add
*/
public void addArmorStand(String name, ArmorStand as) {
armorStands.put(name, as);
@@ -158,33 +158,11 @@ public class BukkitArmorStandManager implements ArmorStandManager {
for (TabPlayer p : nearbyPlayerArray) as.spawn(p);
}
/**
* Gets armor stand by name
*
* @param name
* Name to get armor stand by
* @return Armor stand with given name or null if not found
*/
public ArmorStand getArmorStand(String name) {
return armorStands.get(name);
}
/**
* Removes armor stand by given name if exists
*
* @param name
* Name of line to remove
*/
public void removeArmorStand(String name) {
if (!armorStands.containsKey(name)) return;
armorStands.get(name).destroy();
armorStands.remove(name);
armorStandArray = armorStands.values().toArray(new ArmorStand[0]);
}
/**
* Removes specified player from list of nearby players
* @param viewer - player to remove
*
* @param viewer
* player to remove
*/
public void unregisterPlayer(TabPlayer viewer) {
if (nearbyPlayers.remove(viewer)) nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
@@ -194,6 +172,17 @@ public class BukkitArmorStandManager implements ArmorStandManager {
for (ArmorStand a : armorStandArray) a.updateVisibility(force);
}
/**
* Sends destroy packet of all armor stands to specified player and removes them from nearby players list
*
* @param viewer
* player to destroy armor stands for
*/
public void destroy(TabPlayer viewer) {
for (ArmorStand a : armorStandArray) a.destroy(viewer);
unregisterPlayer(viewer);
}
@Override
public void destroy() {
for (ArmorStand a : armorStandArray) a.destroy();
@@ -22,7 +22,9 @@ public class BukkitNameTagX extends NameTagX {
/**
* Constructs new instance with given parameters and loads config options
* @param plugin - plugin instance
*
* @param plugin
* plugin instance
*/
public BukkitNameTagX(JavaPlugin plugin) {
super(BukkitArmorStandManager::new);
@@ -77,9 +79,8 @@ public class BukkitNameTagX extends NameTagX {
private void spawnArmorStands(TabPlayer viewer, TabPlayer target) {
if (target == viewer || isPlayerDisabled(target)) return;
if (((Player) viewer.getPlayer()).getWorld() != ((Player) target.getPlayer()).getWorld()) return;
if (getDistance(viewer, target) <= 48) {
if (((Player)viewer.getPlayer()).canSee((Player)target.getPlayer()) && !target.isVanished()) getArmorStandManager(target).spawn(viewer);
}
if (getDistance(viewer, target) <= 48 && ((Player)viewer.getPlayer()).canSee((Player)target.getPlayer()) && !target.isVanished())
getArmorStandManager(target).spawn(viewer);
}
@Override
@@ -101,7 +102,6 @@ public class BukkitNameTagX extends NameTagX {
@Override
public void pauseArmorStands(TabPlayer player) {
if (isPlayerDisabled(player)) return;
getArmorStandManager(player).destroy();
}
@@ -118,8 +118,8 @@ public class BukkitNameTagX extends NameTagX {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
getArmorStandManager(all).unregisterPlayer(disconnectedPlayer);
}
getArmorStandManager(disconnectedPlayer).destroy();
TAB.getInstance().getCPUManager().runTaskLater(500, this, TabConstants.CpuUsageCategory.PLAYER_QUIT, () -> getArmorStandManager(disconnectedPlayer).destroy());
armorStandManagerMap.get(disconnectedPlayer).destroy();
armorStandManagerMap.remove(disconnectedPlayer); // WeakHashMap doesn't clear this due to value referencing the key
}
@Override
@@ -134,13 +134,22 @@ public class BukkitNameTagX extends NameTagX {
if (isPreviewingNametag(p)) {
getArmorStandManager(p).spawn(p);
}
//for some reason this is needed for some users
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getWorld().equals(from)) {
getArmorStandManager(p).destroy(viewer);
}
}
}
/**
* Returns flat distance between two players ignoring Y value
* @param player1 - first player
* @param player2 - second player
* @return flat distance in blocks
*
* @param player1
* first player
* @param player2
* second player
* @return flat distance in blocks
*/
private double getDistance(TabPlayer player1, TabPlayer player2) {
Location loc1 = ((Player) player1.getPlayer()).getLocation();
@@ -156,6 +165,4 @@ public class BukkitNameTagX extends NameTagX {
public VehicleRefresher getVehicleManager() {
return vehicleManager;
}
}
@@ -20,7 +20,9 @@ public class EventListener implements Listener {
/**
* Constructs new instance with given parameters
* @param feature - NameTag feature handler
*
* @param feature
* NameTag feature handler
*/
public EventListener(BukkitNameTagX feature) {
this.feature = feature;
@@ -28,7 +30,9 @@ public class EventListener implements Listener {
/**
* Sneak event listener to de-spawn and spawn armor stands to skip animation
* @param e - sneak event
*
* @param e
* sneak event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSneak(PlayerToggleSneakEvent e) {
@@ -39,7 +43,9 @@ public class EventListener implements Listener {
/**
* Respawning armor stands as respawn screen destroys all entities in client
* @param e - respawn event
*
* @param e
* respawn event
*/
@EventHandler
public void onRespawn(PlayerRespawnEvent e) {
@@ -30,7 +30,9 @@ public class PacketListener extends TabFeature {
/**
* Constructs new instance with given parameters and loads config options
* @param nameTagX - main feature
*
* @param nameTagX
* main feature
*/
public PacketListener(BukkitNameTagX nameTagX) {
super(nameTagX.getFeatureName(), null);
@@ -100,15 +102,18 @@ public class PacketListener extends TabFeature {
/**
* Processes entity move packet
* @param receiver - packet receiver
* @param entityId - entity that moved
*
* @param receiver
* packet receiver
* @param entityId
* entity that moved
*/
private void onEntityMove(TabPlayer receiver, int entityId) {
TabPlayer pl = entityIdMap.get(entityId);
List<Entity> vehicleList;
if (pl != null) {
//player moved
if (nameTagX.isPlayerDisabled(pl)) return;
if (nameTagX.isPlayerDisabled(pl) || !pl.isLoaded()) return;
TAB.getInstance().getCPUManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE,
() -> nameTagX.getArmorStandManager(pl).teleport(receiver));
} else if ((vehicleList = nameTagX.getVehicleManager().getVehicles().get(entityId)) != null){
@@ -145,8 +150,10 @@ public class PacketListener extends TabFeature {
private void onEntityDestroy(TabPlayer receiver, int entity) {
TabPlayer deSpawnedPlayer = entityIdMap.get(entity);
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(deSpawnedPlayer))
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(deSpawnedPlayer)) {
BukkitArmorStandManager asm = nameTagX.getArmorStandManager(deSpawnedPlayer);
TAB.getInstance().getCPUManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY,
() -> nameTagX.getArmorStandManager(deSpawnedPlayer).destroy(receiver));
() -> asm.destroy(receiver));
}
}
}
@@ -31,8 +31,8 @@ public class VehicleRefresher extends TabFeature {
TAB.getInstance().getCPUManager().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();
// feature.getVehicleManager().processPassengers((Entity) inVehicle.getPlayer());
}
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (feature.isPreviewingNametag(p)) {
@@ -40,8 +40,8 @@ public class VehicleRefresher extends TabFeature {
}
}
});
addUsedPlaceholders(Collections.singletonList("%vehicle%"));
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%vehicle%", 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VEHICLE));
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
}
@Override
@@ -67,6 +67,9 @@ public class VehicleRefresher extends TabFeature {
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
if (playersInVehicle.containsKey(disconnectedPlayer)) vehicles.remove(playersInVehicle.get(disconnectedPlayer).getEntityId());
for (List<Entity> entities : vehicles.values()) {
entities.remove((Player) disconnectedPlayer.getPlayer());
}
}
@Override
@@ -105,8 +108,10 @@ public class VehicleRefresher extends TabFeature {
/**
* Returns list of all passengers on specified vehicle
* @param vehicle - vehicle to check passengers of
* @return list of passengers
*
* @param vehicle
* vehicle to check passengers of
* @return list of passengers
*/
@SuppressWarnings("deprecation")
public List<Entity> getPassengers(Entity vehicle){
@@ -120,17 +125,4 @@ public class VehicleRefresher extends TabFeature {
}
}
}
/**
* Teleports armor stands of all passengers on specified vehicle
* @param vehicle - entity to check passengers of
*/
public void processPassengers(Entity vehicle) {
for (Entity passenger : getPassengers(vehicle)) {
if (passenger instanceof Player) {
feature.getArmorStandManager(TAB.getInstance().getPlayer(passenger.getUniqueId())).teleport();
}
processPassengers(passenger);
}
}
}
@@ -15,7 +15,6 @@ import java.util.UUID;
import org.bukkit.Bukkit;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import io.netty.channel.Channel;
@@ -46,7 +45,6 @@ public final class NMSStorage {
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 Constructor<?> newEntityArmorStand;
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;
@@ -54,23 +52,16 @@ public final class NMSStorage {
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 Method World_getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".CraftWorld").getMethod("getHandle");
public final Enum[] EnumChatFormat_values = getEnumValues(EnumChatFormat);
//chat
public Class<?> ChatComponentText;
public Class<?> ChatHoverable;
public Class<?> EnumClickAction;
private Class<?> IChatBaseComponent;
public Constructor<?> newChatComponentText;
public Constructor<?> newChatClickable;
public Constructor<?> newChatModifier;
public Constructor<?> newChatHoverable;
public Field ChatBaseComponent_extra;
public Field ChatBaseComponent_modifier;
public Field ChatComponentText_text;
public Field ChatClickable_action;
public Field ChatClickable_value;
public Field ChatModifier_bold;
public Field ChatModifier_italic;
public Field ChatModifier_underlined;
@@ -78,16 +69,11 @@ public final class NMSStorage {
public Field ChatModifier_obfuscated;
public Field ChatModifier_clickEvent;
public Field ChatModifier_hoverEvent;
public Field ChatHexColor_name;
public Field ChatHexColor_rgb;
public Field ChatModifier_color;
public Method ChatComponentText_addSibling;
public Method EnumHoverAction_a;
public Method ChatHoverable_getAction;
public Method ChatHexColor_ofInt;
public Method ChatHexColor_ofString;
public Method ChatHoverable_serialize;
public Method ChatHoverable_getValue;
public Method EnumHoverAction_fromJson;
public Method EnumHoverAction_fromLegacyComponent;
@@ -182,7 +168,7 @@ public final class NMSStorage {
private final Class<?> IScoreboardCriteria = getNMSClass("net.minecraft.world.scores.criteria.IScoreboardCriteria", "IScoreboardCriteria");
public Class<?> EnumScoreboardHealthDisplay;
public final Constructor<?> newScoreboardObjective = ScoreboardObjective.getConstructors()[0];
public final Constructor<?> newScoreboard = Scoreboard.getConstructor();
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;
@@ -205,6 +191,7 @@ public final class NMSStorage {
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;
@@ -221,10 +208,15 @@ public final class NMSStorage {
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
*
* @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"
@@ -235,8 +227,10 @@ public final class NMSStorage {
if (minorVersion >= 8) {
CHANNEL = getFields(NetworkManager, Channel.class).get(0);
getProfile = getMethods(getNMSClass("net.minecraft.world.entity.player.EntityHuman", "EntityHuman"), GameProfile.class).get(0);
newEntityArmorStand = getNMSClass("net.minecraft.world.entity.decoration.EntityArmorStand", "EntityArmorStand")
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();
@@ -255,7 +249,9 @@ public final class NMSStorage {
/**
* Sets new instance
* @param instance - new instance
*
* @param instance
* new instance
*/
public static void setInstance(NMSStorage instance) {
NMSStorage.instance = instance;
@@ -263,7 +259,8 @@ public final class NMSStorage {
/**
* Returns instance
* @return instance
*
* @return instance
*/
public static NMSStorage getInstance() {
return instance;
@@ -273,19 +270,15 @@ public final class NMSStorage {
if (minorVersion < 7) return;
Class<?> ChatBaseComponent = getNMSClass("net.minecraft.network.chat.ChatBaseComponent", "ChatBaseComponent");
Class<?> ChatClickable = getNMSClass("net.minecraft.network.chat.ChatClickable", "ChatClickable");
ChatComponentText = getNMSClass("net.minecraft.network.chat.ChatComponentText", "ChatComponentText");
ChatHoverable = getNMSClass("net.minecraft.network.chat.ChatHoverable", "ChatHoverable");
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_extra = getFields(ChatBaseComponent, List.class).get(0);
ChatBaseComponent_modifier = getFields(ChatBaseComponent, ChatModifier).get(0);
ChatComponentText_text = getFields(ChatComponentText, String.class).get(0);
ChatClickable_action = getFields(ChatClickable, EnumClickAction).get(0);
ChatClickable_value = getFields(ChatClickable, String.class).get(0);
List<Field> booleans = getFields(ChatModifier, Boolean.class);
ChatModifier_bold = booleans.get(0);
ChatModifier_italic = booleans.get(1);
@@ -296,27 +289,20 @@ public final class NMSStorage {
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);
ChatHoverable_getAction = getMethods(ChatHoverable, EnumHoverAction).get(0);
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);
List<Field> list = getFields(ChatHexColor, String.class);
ChatHexColor_name = list.get(list.size()-1);
ChatHexColor_rgb = getFields(ChatHexColor, int.class).get(0);
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);
ChatHoverable_serialize = getMethods(ChatHoverable, JsonObject.class).get(0);
ChatHoverable_getValue = getMethods(ChatHoverable, Object.class, EnumHoverAction).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);
ChatHoverable_getValue = getMethods(ChatHoverable, IChatBaseComponent).get(0);
}
}
@@ -459,6 +445,7 @@ public final class NMSStorage {
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);
@@ -494,9 +481,12 @@ public final class NMSStorage {
/**
* Returns class with given potential names in same order
* @param names - possible class names
* @return class for specified name(s)
* @throws ClassNotFoundException if class does not exist
*
* @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) {
@@ -514,9 +504,12 @@ public final class NMSStorage {
/**
* Returns class from given name
* @param name - class name
* @return class from given name
* @throws ClassNotFoundException if class was not found
*
* @param name
* class name
* @return class from given name
* @throws ClassNotFoundException
* if class was not found
*/
private Class<?> getLegacyClass(String name) throws ClassNotFoundException {
try {
@@ -536,11 +529,16 @@ public final class NMSStorage {
/**
* Returns method with specified possible names and parameters. Throws exception if no such method was found
* @param clazz - class to get method from
* @param names - possible method names
* @param parameterTypes - parameter types of the method
* @return method with specified name and parameters
* @throws NoSuchMethodException if no such method exists
*
* @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) {
@@ -604,9 +602,12 @@ public final class NMSStorage {
/**
* 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
*
* @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");
@@ -619,12 +620,36 @@ public final class NMSStorage {
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
*
* @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()) {
@@ -20,8 +20,11 @@ public class DataWatcher {
/**
* Sets value into data values
* @param type - type of value
* @param value - value
*
* @param type
* type of value
* @param value
* value
*/
public void setValue(DataWatcherObject type, Object value){
Preconditions.checkNotNull(type, "type");
@@ -31,7 +34,9 @@ public class DataWatcher {
/**
* Removes value by position
* @param position - position of value to remove
*
* @param position
* position of value to remove
*/
public void removeValue(int position) {
dataValues.remove(position);
@@ -39,8 +44,10 @@ public class DataWatcher {
/**
* Returns item with given position
* @param position - position of item
* @return item or null if not set
*
* @param position
* position of item
* @return item or null if not set
*/
public DataWatcherItem getItem(int position) {
return dataValues.get(position);
@@ -48,7 +55,8 @@ public class DataWatcher {
/**
* Returns helper created by this instance
* @return data write helper
*
* @return data write helper
*/
public DataWatcherHelper helper() {
return helper;
@@ -56,9 +64,10 @@ public class DataWatcher {
/**
* Converts the class into an instance of NMS.DataWatcher
* @return an instance of NMS.DataWatcher with same data
* @throws ReflectiveOperationException
* if thrown by reflective operation
*
* @return an instance of NMS.DataWatcher with same data
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object toNMS() throws ReflectiveOperationException {
NMSStorage nms = NMSStorage.getInstance();
@@ -83,10 +92,12 @@ public class DataWatcher {
/**
* Reads NMS data watcher and returns and instance of this class with same data
* @param nmsWatcher - NMS DataWatcher to read
* @return an instance of this class with same values
* @throws ReflectiveOperationException
* if thrown by reflective operation
*
* @param nmsWatcher
* NMS DataWatcher to read
* @return an instance of this class with same values
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@SuppressWarnings("unchecked")
public static DataWatcher fromNMS(Object nmsWatcher) throws ReflectiveOperationException {
@@ -4,7 +4,6 @@ import java.util.Optional;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.BukkitPacketBuilder;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
@@ -25,17 +24,19 @@ public class DataWatcherHelper {
/**
* Constructs new instance of this class with given parent
* @param data - data to write to
*
* @param data
* data to write to
*/
public DataWatcherHelper(DataWatcher data) {
Preconditions.checkNotNull(data, "dataWatcher");
this.data = data;
this.registry = NMSStorage.getInstance().getDataWatcherRegistry();
}
/**
* Returns armor stand flags position based on server version
* @return armor stand flags position based on server version
*
* @return armor stand flags position based on server version
*/
private int getArmorStandFlagsPosition() {
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 17) {
@@ -58,7 +59,9 @@ public class DataWatcherHelper {
/**
* Writes entity byte flags
* @param flags - flags to write
*
* @param flags
* flags to write
*/
public void setEntityFlags(byte flags) {
data.setValue(new DataWatcherObject(0, registry.getByte()), flags);
@@ -66,12 +69,13 @@ public class DataWatcherHelper {
/**
* Writes entity custom name with position based on server version and value depending on client version (RGB or not)
* @param customName - target custom name
* @param clientVersion - client version
*
* @param customName
* target custom name
* @param clientVersion
* client version
*/
public void setCustomName(String customName, ProtocolVersion clientVersion) {
Preconditions.checkNotNull(customName, "customName");
Preconditions.checkNotNull(clientVersion, "clientVersion");
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 13) {
try {
data.setValue(new DataWatcherObject(2, registry.getOptionalComponent()), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPlatform().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
@@ -84,41 +88,46 @@ public class DataWatcherHelper {
//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){
data.setValue(new DataWatcherObject(10, registry.getString()), cutName);
data.setValue(new DataWatcherObject(10, null), cutName);
} else {
data.setValue(new DataWatcherObject(5, registry.getString()), cutName);
data.setValue(new DataWatcherObject(5, null), cutName);
}
}
}
/**
* Writes custom name visibility boolean
* @param visible - if visible or not
*
* @param visible
* if visible or not
*/
public void setCustomNameVisible(boolean visible) {
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
data.setValue(new DataWatcherObject(3, registry.getBoolean()), visible);
} else {
data.setValue(new DataWatcherObject(3, registry.getByte()), (byte)(visible?1:0));
data.setValue(new DataWatcherObject(3, null), (byte)(visible?1:0));
}
}
/**
* Writes entity health
* @param health - health of entity
*
* @param health
* health of entity
*/
public void setHealth(float health) {
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 6) {
data.setValue(new DataWatcherObject(6, registry.getFloat()), health);
} else {
data.setValue(new DataWatcherObject(16, registry.getInteger()), (int)health);
data.setValue(new DataWatcherObject(16, null), (int)health);
}
}
/**
* Writes armor stand flags
* @param flags - flags to write
*
* @param flags
* flags to write
*/
public void setArmorStandFlags(byte flags) {
data.setValue(new DataWatcherObject(armorStandFlagsPosition, registry.getByte()), flags);
@@ -14,8 +14,11 @@ public class DataWatcherItem {
/**
* Constructs new instance of the object with given parameters
* @param type - value type
* @param value - value
*
* @param type
* value type
* @param value
* value
*/
public DataWatcherItem(DataWatcherObject type, Object value){
Preconditions.checkNotNull(type, "type");
@@ -26,10 +29,12 @@ public class DataWatcherItem {
/**
* Returns and instance of this class from given NMS item
* @param nmsItem - NMS item
* @return instance of this class with same data
* @throws ReflectiveOperationException
* if thrown by reflective operation
*
* @param nmsItem
* NMS item
* @return instance of this class with same data
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public static DataWatcherItem fromNMS(Object nmsItem) throws ReflectiveOperationException {
NMSStorage nms = NMSStorage.getInstance();
@@ -13,8 +13,11 @@ public class DataWatcherObject {
/**
* Constructs a new instance of this class with given parameters
* @param position - position in DataWatcher
* @param classType - value class type
*
* @param position
* position in DataWatcher
* @param classType
* value class type
*/
public DataWatcherObject(int position, Object classType){
this.position = position;
@@ -13,26 +13,10 @@ import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
public class DataWatcherRegistry {
private Object registryByte;
private Object registryShort;
private Object registryInteger;
private Object registryFloat;
private Object registryString;
private Object registryIChatBaseComponent;
private Object registryOptionalIChatBaseComponent;
private Object registryOptionalItemStack;
private Object registryItemStack;
private Object registryOptionalIBlockData;
private Object registryBoolean;
private Object registryParticleParam;
private Object registryVector3f;
private Object registryBlockPosition;
private Object registryOptionalBlockPosition;
private Object registryEnumDirection;
private Object registryOptionalUUID;
private Object registryNBTTagCompound;
private Object registryVillagerData;
private Object registryOptionalInt;
private Object registryEntityPose;
/**
* Initializes required NMS classes and fields
@@ -41,51 +25,23 @@ public class DataWatcherRegistry {
if (nms.getMinorVersion() >= 9) {
Map<String, Object> fields = getStaticFields(nms.DataWatcherRegistry, nms);
registryByte = fields.get("a");
registryInteger = fields.get("b");
registryFloat = fields.get("c");
registryString = fields.get("d");
registryIChatBaseComponent = fields.get("e");
if (nms.getMinorVersion() >= 13) {
registryOptionalIChatBaseComponent = fields.get("f");
registryItemStack = fields.get("g");
registryOptionalIBlockData = fields.get("h");
registryBoolean = fields.get("i");
registryParticleParam = fields.get("j");
registryVector3f = fields.get("k");
registryBlockPosition = fields.get("l");
registryOptionalBlockPosition = fields.get("m");
registryEnumDirection = fields.get("n");
registryOptionalUUID = fields.get("o");
registryNBTTagCompound = fields.get("p");
if (nms.getMinorVersion() >= 15) {
registryVillagerData = fields.get("q");
registryOptionalInt = fields.get("r");
registryEntityPose = fields.get("s");
}
} else {
registryOptionalIBlockData = fields.get("g");
registryBoolean = fields.get("h");
registryVector3f = fields.get("i");
registryBlockPosition = fields.get("j");
registryOptionalBlockPosition = fields.get("k");
registryEnumDirection = fields.get("l");
registryOptionalUUID = fields.get("m");
if (nms.getMinorVersion() >= 12) {
registryNBTTagCompound = fields.get("n");
}
if (nms.getMinorVersion() >= 11) {
registryItemStack = fields.get("f");
} else {
registryOptionalItemStack = fields.get("f");
}
}
}
}
/**
* Gets values of all static fields in a class
* @param clazz class to return field values from
* @return map of values
*
* @param clazz
* class to return field values from
* @return map of values
*/
private Map<String, Object> getStaticFields(Class<?> clazz, NMSStorage nms){
Map<String, Object> fields = new HashMap<>();
@@ -106,14 +62,6 @@ public class DataWatcherRegistry {
return registryByte;
}
public Object getShort() {
return registryShort;
}
public Object getInteger() {
return registryInteger;
}
public Object getFloat() {
return registryFloat;
}
@@ -122,67 +70,11 @@ public class DataWatcherRegistry {
return registryString;
}
public Object getComponent() {
return registryIChatBaseComponent;
}
public Object getOptionalComponent() {
return registryOptionalIChatBaseComponent;
}
public Object getOptionalItemStack() {
return registryOptionalItemStack;
}
public Object getItemStack() {
return registryItemStack;
}
public Object getOptionalIBlockData() {
return registryOptionalIBlockData;
}
public Object getBoolean() {
return registryBoolean;
}
public Object getParticleParam() {
return registryParticleParam;
}
public Object getVector3f() {
return registryVector3f;
}
public Object getBlockPosition() {
return registryBlockPosition;
}
public Object getOptionalBlockPosition() {
return registryOptionalBlockPosition;
}
public Object getEnumDirection() {
return registryEnumDirection;
}
public Object getOptionalUUID() {
return registryOptionalUUID;
}
public Object getNBTTagCompound() {
return registryNBTTagCompound;
}
public Object getVillagerData() {
return registryVillagerData;
}
public Object getOptionalInt() {
return registryOptionalInt;
}
public Object getEntityPose() {
return registryEntityPose;
}
}
@@ -17,8 +17,11 @@ public class Vault extends PermissionPlugin {
/**
* Constructs new instance with given parameters
* @param permission permission plugin implementation
* @param vaultVersion vault version
*
* @param permission
* permission plugin implementation
* @param vaultVersion
* vault version
*/
public Vault(Permission permission, String vaultVersion) {
super(vaultVersion);
@@ -27,7 +30,7 @@ public class Vault extends PermissionPlugin {
@Override
public String getPrimaryGroup(TabPlayer p) {
if (getName().equals("SuperPerms")) return TabConstants.DEFAULT_GROUP; //Vault's dummy implementation throws exception on every request
if (getName().equals("SuperPerms")) return TabConstants.NO_GROUP; //Vault's dummy implementation throws exception on every request
return permission.getPrimaryGroup((Player) p.getPlayer());
}
+1 -1
View File
@@ -137,7 +137,7 @@ scoreboard:
lines:
- "%animation:MyAnimation1%"
- "&6Online:"
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eOnline&7: &f%online%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- " "
@@ -18,7 +18,9 @@ public class BungeeEventListener implements Listener {
/**
* Disconnect event listener to forward the event to all features
* @param e - disconnect event
*
* @param e
* disconnect event
*/
@EventHandler
public void onQuit(PlayerDisconnectEvent e){
@@ -29,8 +31,9 @@ public class BungeeEventListener implements Listener {
/**
* Listener to join / server switch to forward the event to all features
* @param e
* switch event
*
* @param e
* switch event
*/
@EventHandler
public void onSwitch(ServerSwitchEvent e){
@@ -46,26 +49,36 @@ public class BungeeEventListener implements Listener {
/**
* Listener to chat packets to forward the event to all features
* @param e
* chat event
*
* @param e
* chat event
*/
@EventHandler
public void onChat(ChatEvent e) {
if (TAB.getInstance().isDisabled()) return;
if (e.getMessage().startsWith("/") && TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
if (e.isCommand() && TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
}
/**
* Listener to plugin message event
* @param event - plugin message event
*
* @param event
* plugin message event
*/
@EventHandler
public void on(PluginMessageEvent event){
if (!event.getTag().equals(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME)) return;
if (event.getReceiver() instanceof ProxiedPlayer) {
event.setCancelled(true);
((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler().onPluginMessage(
((ProxiedPlayer) event.getReceiver()).getUniqueId(), event.getData());
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) {}
});
}
}
}
@@ -7,7 +7,6 @@ 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.api.util.Preconditions;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.PipelineInjector;
@@ -22,11 +21,15 @@ import java.util.Collection;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Pipeline injection to secure proper functionality
* of some features by preventing other plugins
* from overriding it.
*/
@SuppressWarnings("unchecked")
public class BungeePipelineInjector extends PipelineInjector {
//packets that must be deserialized and BungeeCord does not do it automatically
/** Packets used by the plugin that must be deserialized and BungeeCord does not do it automatically */
private final Class<? extends DefinedPacket>[] extraPacketClasses = new Class[]{Team.class, ScoreboardDisplay.class, ScoreboardObjective.class};
private final Supplier<DefinedPacket>[] extraPacketSuppliers = new Supplier[]{Team::new, ScoreboardDisplay::new, ScoreboardObjective::new};
@@ -47,15 +50,16 @@ public class BungeePipelineInjector extends PipelineInjector {
*/
public class BungeeChannelDuplexHandler extends ChannelDuplexHandler {
//injected player
/** Injected player */
protected final TabPlayer player;
/**
* Constructs new instance with given player
* @param player - player to inject
*
* @param player
* player to inject
*/
public BungeeChannelDuplexHandler(TabPlayer player) {
Preconditions.checkNotNull(player, "player");
this.player = player;
}
@@ -68,7 +72,9 @@ public class BungeePipelineInjector extends PipelineInjector {
return;
case "Team":
if (antiOverrideTeams) {
long time = System.nanoTime();
modifyPlayers((Team) packet);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
break;
case "ScoreboardDisplay":
@@ -91,17 +97,18 @@ public class BungeePipelineInjector extends PipelineInjector {
try {
super.write(context, packet, channelPromise);
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to forward packet " + packet.getClass().getSimpleName() + " to " + player.getName(), e);
TAB.getInstance().getErrorManager().printError(String.format("Failed to forward packet %s to %s", packet.getClass().getSimpleName(), player.getName()), e);
}
}
/**
* Removes all real players from packet if the packet doesn't come from TAB
* @param packet - packet to modify
*
* @param packet
* packet to modify
*/
private void modifyPlayers(Team packet){
long time = System.nanoTime();
if (packet.getPlayers() == null) return;
if (packet.getMode() == 1 || packet.getMode() == 2 || packet.getMode() == 4) return;
Collection<String> col = Lists.newArrayList(packet.getPlayers());
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (col.contains(p.getNickname()) && !((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
@@ -113,23 +120,28 @@ public class BungeePipelineInjector extends PipelineInjector {
RedisBungeeSupport redis = (RedisBungeeSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
if (redis != null) {
for (RedisPlayer p : redis.getRedisPlayers().values()) {
if (col.contains(p.getName()) && !packet.getName().equals(p.getTeamName())) {
if (col.contains(p.getNickName()) && !packet.getName().equals(p.getTeamName())) {
logTeamOverride(packet.getName(), p.getName(), p.getTeamName());
col.remove(p.getName());
col.remove(p.getNickName());
}
}
}
packet.setPlayers(col.toArray(new String[0]));
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
}
/**
* Channel duplex handler override if features using packets that must be
* deserialized manually are used. If they are disabled, deserialization is
* disabled for better performance.
*/
public class DeserializableBungeeChannelDuplexHandler extends BungeeChannelDuplexHandler {
/**
* Constructs new instance with given player
*
* @param player - player to inject
* @param player
* player to inject
*/
public DeserializableBungeeChannelDuplexHandler(TabPlayer player) {
super(player);
@@ -139,15 +151,17 @@ public class BungeePipelineInjector extends PipelineInjector {
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
long time = System.nanoTime();
Object modifiedPacket = packet instanceof ByteBuf ? deserialize((ByteBuf) packet) : packet;
TAB.getInstance().getCPUManager().addTime("Packet deserializing", TabConstants.CpuUsageCategory.BYTE_BUF, System.nanoTime()-time);
TAB.getInstance().getCPUManager().addTime(TabConstants.Feature.PACKET_DESERIALIZING, TabConstants.CpuUsageCategory.BYTE_BUF, System.nanoTime()-time);
super.write(context, modifiedPacket, channelPromise);
}
/**
* Deserializes byte buf in case it is one of the tracked packets coming from backend server and returns it.
* If the packet is not one of them, returns input
* @param buf - byte buf to deserialize
* @return deserialized packet or input byte buf if packet is not tracked
*
* @param buf
* byte buf to deserialize
* @return deserialized packet or input byte buf if packet is not tracked
*/
private Object deserialize(ByteBuf buf) {
int marker = buf.readerIndex();
@@ -1,10 +1,6 @@
package me.neznamy.tab.platforms.bungeecord;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bungeecord.event.TabLoadEvent;
import me.neznamy.tab.platforms.bungeecord.event.TabPlayerLoadEvent;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.proxy.ProxyPlatform;
@@ -18,7 +14,7 @@ import net.md_5.bungee.api.plugin.Plugin;
public class BungeePlatform extends ProxyPlatform {
/**
* Constructs new instance with given parameter
* Constructs new instance
*/
public BungeePlatform() {
super(new BungeePacketBuilder());
@@ -29,9 +25,9 @@ public class BungeePlatform extends ProxyPlatform {
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BungeePipelineInjector());
tab.getPlaceholderManager().registerPlayerPlaceholder("%displayname%", 500, p -> ((ProxiedPlayer) p.getPlayer()).getDisplayName());
tab.getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((ProxiedPlayer) p.getPlayer()).getDisplayName());
super.loadFeatures();
if (ProxyServer.getInstance().getPluginManager().getPlugin("RedisBungee") != null) {
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.REDIS_BUNGEE) != null) {
if (RedisBungeeAPI.getRedisBungeeApi() != null) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, new RedisBungeeSupport());
} else {
@@ -43,19 +39,8 @@ public class BungeePlatform extends ProxyPlatform {
}
}
@Override
public void callLoadEvent() {
ProxyServer.getInstance().getPluginManager().callEvent(new TabLoadEvent());
}
@Override
public void callLoadEvent(TabPlayer player) {
ProxyServer.getInstance().getPluginManager().callEvent(new TabPlayerLoadEvent(player));
}
@Override
public String getPluginVersion(String plugin) {
Preconditions.checkNotNull(plugin, "plugin");
Plugin pl = ProxyServer.getInstance().getPluginManager().getPlugin(plugin);
return pl == null ? null : pl.getDescription().getVersion();
}
@@ -1,57 +1,74 @@
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.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.Protocol;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* TabPlayer for BungeeCord
* TabPlayer implementation for BungeeCord
*/
public class BungeeTabPlayer extends ProxyTabPlayer {
//bungee internals to get player channel
/** 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.class.getDeclaredField("ch");
wrapperField = initialHandler.getDeclaredField("ch");
wrapperField.setAccessible(true);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().criticalError("Failed to initialize fields for packet analysis", e);
TAB.getInstance().getErrorManager().criticalError("Failed to initialize bungee internal fields", e);
}
}
/**
* Constructs new instance for given player
* @param p - BungeeCord player
*
* @param p
* BungeeCord player
*/
public BungeeTabPlayer(ProxiedPlayer p) {
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1);
try {
channel = ((ChannelWrapper) wrapperField.get(getPlayer().getPendingConnection())).getHandle();
} catch (IllegalAccessException e) {
channel = (Channel) ChannelWrapper_getHandle.invoke(wrapperField.get(getPlayer().getPendingConnection()));
} catch (IllegalAccessException | InvocationTargetException e) {
TAB.getInstance().getErrorManager().criticalError("Failed to get channel of " + getPlayer().getName(), e);
}
}
@@ -79,9 +96,17 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public Skin getSkin() {
LoginResult loginResult = ((InitialHandler)getPlayer().getPendingConnection()).getLoginProfile();
if (loginResult == null || loginResult.getProperties() == null || loginResult.getProperties().length == 0) return null;
return new Skin(loginResult.getProperties()[0].getValue(), loginResult.getProperties()[0].getSignature());
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;
}
}
@Override
@@ -91,8 +116,10 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
/**
* Returns packet ID for this player of provided packet class
* @param clazz - packet class
* @return - packet ID
*
* @param clazz
* packet class
* @return packet ID
*/
public int getPacketId(Class<? extends DefinedPacket> clazz) {
Preconditions.checkNotNull(clazz, "class");
@@ -112,9 +139,9 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public boolean isVanished() {
try {
if (ProxyServer.getInstance().getPluginManager().getPlugin("PremiumVanish") != null && BungeeVanishAPI.isInvisible(getPlayer())) return true;
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.PREMIUM_VANISH) != null && BungeeVanishAPI.isInvisible(getPlayer())) return true;
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("PremiumVanish v" + TAB.getInstance().getPlatform().getPluginVersion("PremiumVanish") +
TAB.getInstance().getErrorManager().printError("PremiumVanish v" + TAB.getInstance().getPlatform().getPluginVersion(TabConstants.Plugin.PREMIUM_VANISH) +
" generated an error when retrieving vanish status of " + getName(), e);
}
return super.isVanished();
@@ -127,7 +154,12 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public int getGamemode() {
return ((UserConnection)player).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;
}
}
@Override
@@ -35,13 +35,14 @@ public class Main extends Plugin {
getProxy().getPluginManager().registerCommand(this, new BTABCommand());
TAB.getInstance().load();
Metrics metrics = new Metrics(this, 10535);
metrics.addCustomChart(new SimplePie("permission_system", () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie("global_playerlist_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.GLOBAL_PLAYER_LIST) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM, () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.GLOBAL_PLAYER_LIST_ENABLED, () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.GLOBAL_PLAYER_LIST) ? "Yes" : "No"));
}
/**
* Checks for compatibility and returns true if version is supported, false if not
* @return true if version is compatible, false if not
*
* @return true if version is compatible, false if not
*/
private boolean isVersionSupported() {
try {
@@ -58,9 +59,9 @@ public class Main extends Plugin {
}
/**
* TAB command for bungeecord
* TAB command for BungeeCord
*/
public static class BTABCommand extends Command implements TabExecutor {
private static class BTABCommand extends Command implements TabExecutor {
/**
* Constructs new instance
@@ -1,14 +0,0 @@
package me.neznamy.tab.platforms.bungeecord.event;
import net.md_5.bungee.api.plugin.Event;
/**
* Bungee event that is called each time TAB fully loads. This includes server startup, reload and
* /btab reload
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.plugin.TabLoadEvent}
*/
@Deprecated
public class TabLoadEvent extends Event {
}
@@ -1,23 +0,0 @@
package me.neznamy.tab.platforms.bungeecord.event;
import me.neznamy.tab.api.TabPlayer;
import net.md_5.bungee.api.plugin.Event;
/**
* BungeeCord 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 final TabPlayer player;
public TabPlayerLoadEvent(TabPlayer player) {
this.player = player;
}
public TabPlayer getPlayer() {
return player;
}
}
@@ -154,7 +154,7 @@ scoreboard:
lines:
- "%animation:MyAnimation1%"
- "&6Online:"
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eOnline&7: &f%online%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- " "
@@ -200,10 +200,10 @@ placeholder-output-replacements:
"no": ""
"%essentials_nickname%":
"%essentials_nickname%": "%player%" # placeholder did not parse correctly, show real name instead
# bukkit-only placeholders used in default groups.yml, doing this as a fix
"%afk%":
"%afk%": ""
"%afk%": ""
"%vault-prefix%":
"%vault-prefix%" : ""
"%vault-suffix%":
+1 -1
View File
@@ -1,5 +1,5 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=3.1.0
projectVersion=3.1.1
# Gradle properties
org.gradle.daemon=true
+7 -7
View File
@@ -3,18 +3,18 @@ metadata.format.version = "1.1"
[versions]
# API
netty = "4.1.72.Final"
netty = "4.1.75.Final"
jsonSimple = "1.1.1"
snakeyaml = "1.30"
yamlAssist = "1.0.5"
gson = "2.8.9"
gson = "2.9.0"
slf4j = "1.7.36"
# General
luckperms = "5.2"
guava = "30.1.1-jre"
luckperms = "5.4"
guava = "31.1-jre"
floodgate = "2.0-SNAPSHOT"
kotlin = "1.6.0"
kotlin = "1.6.10"
event = "3.0.0"
# Platforms
@@ -52,8 +52,8 @@ kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", versio
event = { group = "net.kyori", name = "event-method", version.ref = "event" }
# Platforms
bukkit = { group = "com.destroystokyo.paper", name = "paper-api", version.ref = "bukkit" }
bungeecord = { group = "net.md-5", name = "bungeecord-proxy", version.ref = "bungeecord" }
bukkit = { group = "org.purpurmc.purpur", name = "purpur-api", version.ref = "bukkit" }
bungeecord = { group = "net.md-5", name = "bungeecord-api", 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" }
+1 -1
View File
@@ -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 {
+10 -16
View File
@@ -3,21 +3,15 @@ enableFeaturePreview("VERSION_CATALOGS")
dependencyResolutionManagement {
repositories {
mavenCentral()
maven("https://jitpack.io")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
maven("https://repo.viaversion.com/")
maven("https://repo.aikar.co/content/groups/aikar/")
maven("https://repo.md-5.net/content/groups/public/")
maven("https://ci.lucko.me/plugin/repository/everything/")
maven("https://repo.essentialsx.net/releases/")
maven("https://repo.codemc.org/repository/maven-public")
maven("https://repo.kryptonmc.org/releases")
maven("https://repo.kryptonmc.org/snapshots")
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://nexus.velocitypowered.com/repository/maven-public/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://papermc.io/repo/repository/maven-public/")
mavenCentral() // Netty, SnakeYaml, json-simple, Gson, slf4j, Guava, Kyori event, bStats
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.kryptonmc.org/releases") // YamlAssist, LuckPerms
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
maven("https://repo.purpurmc.org/snapshots") // Purpur, LibsDisguises, AuthLib, Vault
}
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
}
@@ -31,7 +25,7 @@ rootProject.name = "TAB"
include(":api")
include(":shared")
//include(":krypton")
include(":velocity")
//include(":velocity")
include(":bukkit")
include(":bungeecord")
include(":jar")
@@ -2,10 +2,7 @@ package me.neznamy.tab.shared;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -21,7 +18,7 @@ import me.neznamy.tab.api.task.ThreadManager;
public class CpuManager implements ThreadManager {
/** Data reset interval in milliseconds */
private static final int BUFFER_SIZE_MILLIS = 10000;
private final int BUFFER_SIZE_MILLIS = 10000;
/** Active time in current time period saved as nanoseconds from features */
private Map<String, Map<String, AtomicLong>> featureUsageCurrent = new ConcurrentHashMap<>();
@@ -51,7 +48,8 @@ public class CpuManager implements ThreadManager {
private 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 ExecutorService threadPool = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("TAB Repeating / Delayed Thread %d").build());
private final ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool(
new ThreadFactoryBuilder().setNameFormat("TAB Repeating / Delayed Thread %d").build());
/** Tasks submitted to main thread before plugin was fully enabled */
private final List<Runnable> taskQueue = new ArrayList<>();
@@ -100,9 +98,9 @@ public class CpuManager implements ThreadManager {
* Submits task to TAB's main thread. If plugin is not enabled yet,
* queues the task instead and executes once it's loaded.
*
* @param task
* task to execute
* @return future returned by executor service
* @param task
* task to execute
* @return future returned by executor service
*/
@SuppressWarnings("unchecked")
private Future<Void> submit(Runnable task) {
@@ -113,7 +111,7 @@ public class CpuManager implements ThreadManager {
return (Future<Void>) thread.submit(() -> {
try {
task.run();
} catch (Exception | LinkageError e) {
} catch (Exception | LinkageError | StackOverflowError e) {
TAB.getInstance().getErrorManager().printError("An error was thrown when executing task", e);
}
});
@@ -122,7 +120,7 @@ public class CpuManager implements ThreadManager {
/**
* Returns cpu usage map of placeholders from previous time period
*
* @return cpu usage map of placeholders
* @return cpu usage map of placeholders
*/
public Map<String, Float> getPlaceholderUsage(){
return getUsage(placeholderUsagePrevious);
@@ -131,7 +129,7 @@ public class CpuManager implements ThreadManager {
/**
* Returns cpu usage map of methods previous time period
*
* @return cpu usage map of methods
* @return cpu usage map of methods
*/
public Map<String, Float> getMethodUsage(){
return getUsage(methodUsagePrevious);
@@ -140,7 +138,7 @@ public class CpuManager implements ThreadManager {
/**
* Returns map of sent packets per feature in previous time period
*
* @return map of sent packets per feature
* @return map of sent packets per feature
*/
public Map<String, AtomicInteger> getSentPackets(){
return sortByValue1(packetsPrevious);
@@ -149,9 +147,9 @@ public class CpuManager implements ThreadManager {
/**
* Converts nano map to percent and sorts it from highest to lowest usage.
*
* @param map
* map to convert
* @return converted and sorted map
* @param map
* map to convert
* @return converted and sorted map
*/
private Map<String, Float> getUsage(Map<String, AtomicLong> map){
Map<String, Long> nanoMap = new HashMap<>();
@@ -171,7 +169,7 @@ public class CpuManager implements ThreadManager {
/**
* Returns map of CPU usage per feature and type in the previous time period
*
* @return map of CPU usage per feature and type
* @return map of CPU usage per feature and type
*/
public Map<String, Map<String, Float>> getFeatureUsage(){
Map<String, Map<String, Long>> total = new HashMap<>();
@@ -199,9 +197,9 @@ public class CpuManager implements ThreadManager {
/**
* Converts nanoseconds to percent usage.
*
* @param nanos
* nanoseconds of cpu time
* @return usage in % (0-100)
* @param nanos
* nanoseconds of cpu time
* @return usage in % (0-100)
*/
private float nanosToPercent(long nanos) {
float percent = (float) nanos / BUFFER_SIZE_MILLIS / 1000000; //relative usage (0-1)
@@ -212,13 +210,13 @@ public class CpuManager implements ThreadManager {
/**
* Sorts map by value from highest to lowest
*
* @param <K>
* map key type
* @param <V>
* map value type
* @param map
* map to sort
* @return sorted map
* @param <K>
* map key type
* @param <V>
* map value type
* @param map
* map to sort
* @return sorted map
*/
private <K, V extends Comparable<V>> Map<K, V> sortByValue(Map<K, V> map) {
Comparator<K> valueComparator = (k1, k2) -> {
@@ -233,11 +231,11 @@ public class CpuManager implements ThreadManager {
/**
* Sorts map by value from highest to lowest
*
* @param <K>
* map key type
* @param map
* map to sort
* @return sorted map
* @param <K>
* map key type
* @param map
* map to sort
* @return sorted map
*/
private <K> Map<K, AtomicInteger> sortByValue1(Map<K, AtomicInteger> map) {
Comparator<K> valueComparator = (k1, k2) -> {
@@ -251,11 +249,11 @@ public class CpuManager implements ThreadManager {
/**
* Sorts keys by map nested values from highest to lowest and returns sorted list of keys
* @param <K>
* map key type
* @param map
* map to sort
* @return list of keys sorted from the highest map value to lowest
* @param <K>
* map key type
* @param map
* map to sort
* @return list of keys sorted from the highest map value to lowest
*/
private <K> List<K> sortKeys(Map<K, Map<String, Long>> map){
Map<K, Long> simplified = new LinkedHashMap<>();
@@ -268,12 +266,12 @@ public class CpuManager implements ThreadManager {
/**
* Adds cpu time to specified feature and usage type
*
* @param feature
* feature to add time to
* @param type
* sub-feature to add time to
* @param nanoseconds
* time to add
* @param feature
* feature to add time to
* @param type
* sub-feature to add time to
* @param nanoseconds
* time to add
*/
public void addTime(TabFeature feature, String type, long nanoseconds) {
addTime(feature.getFeatureName(), type, nanoseconds);
@@ -282,12 +280,12 @@ public class CpuManager implements ThreadManager {
/**
* Adds cpu time to specified feature and usage type
*
* @param feature
* feature to add time to
* @param type
* sub-feature to add time to
* @param nanoseconds
* time to add
* @param feature
* feature to add time to
* @param type
* sub-feature to add time to
* @param nanoseconds
* time to add
*/
public void addTime(String feature, String type, long nanoseconds) {
featureUsageCurrent.computeIfAbsent(feature, f -> new ConcurrentHashMap<>()).computeIfAbsent(type, t -> new AtomicLong()).addAndGet(nanoseconds);
@@ -296,12 +294,12 @@ public class CpuManager implements ThreadManager {
/**
* Adds used time to specified key into specified map
*
* @param map
* map to add usage to
* @param key
* usage key
* @param time
* nanoseconds to add
* @param map
* map to add usage to
* @param key
* usage key
* @param time
* nanoseconds to add
*/
private void addTime(Map<String, AtomicLong> map, String key, long time) {
map.computeIfAbsent(key, k -> new AtomicLong()).addAndGet(time);
@@ -310,10 +308,10 @@ public class CpuManager implements ThreadManager {
/**
* Adds placeholder time to specified placeholder
*
* @param placeholder
* placeholder to add time to
* @param nanoseconds
* time to add
* @param placeholder
* placeholder to add time to
* @param nanoseconds
* time to add
*/
public void addPlaceholderTime(String placeholder, long nanoseconds) {
addTime(placeholderUsageCurrent, placeholder, nanoseconds);
@@ -322,10 +320,10 @@ public class CpuManager implements ThreadManager {
/**
* Adds method time to specified method
*
* @param method
* method to add time to
* @param nanoseconds
* time to add
* @param method
* method to add time to
* @param nanoseconds
* time to add
*/
public void addMethodTime(String method, long nanoseconds) {
addTime(methodUsageCurrent, method, nanoseconds);
@@ -334,8 +332,8 @@ public class CpuManager implements ThreadManager {
/**
* Increments packets sent by 1 of specified feature
*
* @param feature
* feature to increment packet counter of
* @param feature
* feature to increment packet counter of
*/
public void packetSent(String feature) {
packetsCurrent.computeIfAbsent(feature, f -> new AtomicInteger()).incrementAndGet();
@@ -372,25 +370,23 @@ public class CpuManager implements ThreadManager {
@Override
public Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task) {
return threadPool.submit(() -> {
try {
Thread.sleep(delayMilliseconds);
runMeasuredTask(feature, type, task);
} catch (InterruptedException pluginDisabled) {
Thread.currentThread().interrupt();
}
});
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;
}
}
@Override
public Future<?> runTaskLater(int delayMilliseconds, Runnable task) {
return threadPool.submit(() -> {
try {
Thread.sleep(delayMilliseconds);
submit(task);
} catch (InterruptedException pluginDisabled) {
Thread.currentThread().interrupt();
}
});
public ThreadPoolExecutor getThreadPool() {
return threadPool;
}
}
@@ -64,14 +64,14 @@ public class DynamicText implements Property {
* Constructs new instance with given parameters and prepares
* the formatter for use by detecting placeholders and reformatting the text.
*
* @param listener
* Feature which should receive refresh method if placeholder changes value
* @param owner
* Player this text belongs to
* @param rawValue
* Raw value using raw placeholder identifiers
* @param source
* Source of the text used in debug command
* @param listener
* Feature which should receive refresh method if placeholder changes value
* @param owner
* Player this text belongs to
* @param rawValue
* Raw value using raw placeholder identifiers
* @param source
* Source of the text used in debug command
*/
public DynamicText(String name, TabFeature listener, TabPlayer owner, String rawValue, String source) {
this.name = name;
@@ -86,8 +86,8 @@ public class DynamicText implements Property {
* Finds all placeholders used in the value and prepares it for
* String formatter using %s for each placeholder.
*
* @param value
* raw value to analyze
* @param value
* raw value to analyze
*/
private void analyze(String value) {
List<String> placeholders0 = new ArrayList<>();
@@ -110,7 +110,7 @@ public class DynamicText implements Property {
rawFormattedValue0 = sb.toString();
}
}
rawFormattedValue0 = RGBUtils.getInstance().applyFormats(rawFormattedValue0, true);
rawFormattedValue0 = RGBUtils.getInstance().applyCleanGradients(rawFormattedValue0);
rawFormattedValue = EnumChatFormat.color(rawFormattedValue0);
placeholders = placeholders0.toArray(new String[0]);
relPlaceholders = relPlaceholders0.toArray(new String[0]);
@@ -130,10 +130,10 @@ public class DynamicText implements Property {
* Changes raw value to new provided value and performs all
* operations related to it. Changes source as well.
*
* @param newValue
* new raw value to use
* @param newSource
* new source of the text
* @param newValue
* new raw value to use
* @param newSource
* new source of the text
*/
public void changeRawValue(String newValue, String newSource) {
if (rawValue.equals(newValue)) return;
@@ -147,7 +147,7 @@ public class DynamicText implements Property {
/**
* Returns source of the raw value or {@code "API"} if it comes from an API call
*
* @return source of the value
* @return source of the value
*/
public String getSource() {
return temporaryValue == null ? source : "API";
@@ -33,8 +33,8 @@ public class ErrorManager {
/**
* Prints error message into errors.log file
*
* @param message
* message to print
* @param message
* message to print
*/
public void printError(String message) {
printError(message, null, false);
@@ -43,10 +43,10 @@ public class ErrorManager {
/**
* Prints error message and stack trace into errors.log file
*
* @param message
* message to print
* @param t
* thrown error
* @param message
* message to print
* @param t
* thrown error
*/
public void printError(String message, Throwable t) {
printError(message, t, false);
@@ -55,12 +55,12 @@ public class ErrorManager {
/**
* Prints error message and stack trace into errors.log file
*
* @param message
* message to print
* @param t
* thrown error
* @param intoConsoleToo
* if the message should be printed into console as well or not
* @param message
* message to print
* @param t
* thrown error
* @param intoConsoleToo
* if the message should be printed into console as well or not
*/
public void printError(String message, Throwable t, boolean intoConsoleToo) {
printError(message, t, intoConsoleToo, errorLog);
@@ -69,14 +69,14 @@ public class ErrorManager {
/**
* Prints error message and stack trace into specified file
*
* @param message
* message to print
* @param t
* thrown error
* @param intoConsoleToo
* if the message should be printed into console as well or not
* @param file
* file to print error to
* @param message
* message to print
* @param t
* thrown error
* @param intoConsoleToo
* if the message should be printed into console as well or not
* @param file
* file to print error to
*/
public void printError(String message, Throwable t, boolean intoConsoleToo, File file) {
Throwable error = t;
@@ -96,25 +96,30 @@ public class ErrorManager {
/**
* Prints error message and stack trace into specified file
*
* @param message
* message to print
* @param error
* thrown error
* @param intoConsoleToo
* if the message should be printed into console as well or not
* @param file
* file to print error to
* @param message
* message to print
* @param error
* thrown error
* @param intoConsoleToo
* if the message should be printed into console as well or not
* @param file
* file to print error to
*/
private void printError(String message, List<String> error, boolean intoConsoleToo, File file) {
private synchronized void printError(String message, List<String> error, boolean intoConsoleToo, File file) {
try {
if (!file.exists()) Files.createFile(file.toPath());
if (file.length() > 1000000) return; //not going over 1 MB
try (BufferedWriter buf = new BufferedWriter(new FileWriter(file, true))){
if (message != null) {
write(buf, "&c[TAB v" + TabConstants.PLUGIN_VERSION + "] ", EnumChatFormat.decolor(message), intoConsoleToo);
if (file.length() < 1000000)
buf.write(dateFormat.format(new Date()) + IChatBaseComponent.fromColoredText("&c[TAB v" + TabConstants.PLUGIN_VERSION + "] ").toRawText() + EnumChatFormat.decolor(message) + System.getProperty("line.separator"));
if (intoConsoleToo || TAB.getInstance().getConfiguration().isDebugMode())
TAB.getInstance().sendConsoleMessage(EnumChatFormat.color("&c[TAB v" + TabConstants.PLUGIN_VERSION + "] ") + EnumChatFormat.decolor(message), false);
}
for (String line : error) {
write(buf, "&c", line, intoConsoleToo);
if (file.length() < 1000000)
buf.write(dateFormat.format(new Date()) + IChatBaseComponent.fromColoredText("&c").toRawText() + line + System.getProperty("line.separator"));
if (intoConsoleToo || TAB.getInstance().getConfiguration().isDebugMode())
TAB.getInstance().sendConsoleMessage(EnumChatFormat.color("&c") + line, false);
}
}
} catch (IOException ex) {
@@ -133,10 +138,10 @@ public class ErrorManager {
/**
* Prints error message thrown by placeholder and stack trace into placeholder-errors.log file
*
* @param message
* message to print
* @param t
* thrown error
* @param message
* message to print
* @param t
* thrown error
*/
public void placeholderError(String message, Throwable t) {
printError(message, t, false, placeholderErrorLog);
@@ -145,39 +150,22 @@ public class ErrorManager {
/**
* Prints error message thrown by placeholder and stack trace into placeholder-errors.log file
*
* @param message
* message to print
* @param t
* thrown stack trace elements
* @param message
* message to print
* @param t
* thrown stack trace elements
*/
public void placeholderError(String message, List<String> t) {
printError(message, t, false, placeholderErrorLog);
}
/**
* Writes message into buffer and console if set
*
* @param buf
* writer to write message to
* @param message
* message to write
* @param forceConsole
* whether to send into console even without debug mode
* @throws IOException
* if I/O writer operation fails
*/
private void write(BufferedWriter buf, String prefix, String message, boolean forceConsole) throws IOException {
buf.write(dateFormat.format(new Date()) + IChatBaseComponent.fromColoredText(prefix).toRawText() + message + System.getProperty("line.separator"));
if (TAB.getInstance().getConfiguration().isDebugMode() || forceConsole) TAB.getInstance().sendConsoleMessage(EnumChatFormat.color(prefix) + message, false);
}
/**
* Prints error message and stack trace into errors.log file as well as the console
*
* @param message
* message to print
* @param t
* thrown error
* @param message
* message to print
* @param t
* thrown error
*/
public void criticalError(String message, Throwable t) {
printError(message, t, true);
@@ -187,11 +175,11 @@ public class ErrorManager {
* Parses integer in given string and returns it.
* Returns second argument if string is not valid.
*
* @param string
* string to parse
* @param defaultValue
* value to return if string is not valid
* @return parsed integer or {@code defaultValue} if input is invalid
* @param string
* string to parse
* @param defaultValue
* value to return if string is not valid
* @return parsed integer or {@code defaultValue} if input is invalid
*/
public int parseInteger(String string, int defaultValue) {
try {
@@ -205,11 +193,11 @@ public class ErrorManager {
* Parses float in given string and returns it.
* Returns second argument if string is not valid.
*
* @param string
* string to parse
* @param defaultValue
* value to return if string is not valid
* @return parsed float or {@code defaultValue} if input is invalid
* @param string
* string to parse
* @param defaultValue
* value to return if string is not valid
* @return parsed float or {@code defaultValue} if input is invalid
*/
public float parseFloat(String string, float defaultValue) {
try {
@@ -223,11 +211,11 @@ public class ErrorManager {
* Parses double in given string and returns it.
* Returns second argument if string is not valid.
*
* @param string
* string to parse
* @param defaultValue
* value to return if string is not valid
* @return parsed float or {@code defaultValue} if input is invalid
* @param string
* string to parse
* @param defaultValue
* value to return if string is not valid
* @return parsed float or {@code defaultValue} if input is invalid
*/
public double parseDouble(String string, double defaultValue) {
try {
@@ -240,11 +228,11 @@ public class ErrorManager {
/**
* Makes interval divisible by 50 and sends error message if it was not already or was 0 or less
*
* @param name
* name of animation used in error message
* @param interval
* configured change interval
* @return fixed change interval
* @param name
* name of animation used in error message
* @param interval
* configured change interval
* @return fixed change interval
*/
public int fixAnimationInterval(String name, int interval) {
if (interval == 0) {
@@ -267,11 +255,11 @@ public class ErrorManager {
/**
* Returns the list if not null, empty list and error message if null
*
* @param name
* name of animation used in error message
* @param list
* list of configured animation frames
* @return the list if it's valid, singleton list with {@code "<Invalid Animation>"} otherwise
* @param name
* name of animation used in error message
* @param list
* list of configured animation frames
* @return the list if it's valid, singleton list with {@code "<Invalid Animation>"} otherwise
*/
public List<String> fixAnimationFrames(String name, List<String> list) {
if (list == null) {
@@ -284,8 +272,8 @@ public class ErrorManager {
/**
* Sends a startup warn message into console
*
* @param message
* message to print into console
* @param message
* message to print into console
*/
public void startupWarn(String message) {
TAB.getInstance().sendConsoleMessage("&c" + message, true);
@@ -294,12 +282,12 @@ public class ErrorManager {
/**
* Sends a startup warn about missing object parameter
*
* @param objectType
* object type missing parameter
* @param objectName
* name of object
* @param attribute
* missing parameter
* @param objectType
* object type missing parameter
* @param objectName
* name of object
* @param attribute
* missing parameter
*/
public void missingAttribute(String objectType, Object objectName, String attribute) {
startupWarn(objectType + " \"&e" + objectName + "&c\" is missing \"&e" + attribute + "&c\" attribute!");
@@ -307,7 +295,8 @@ public class ErrorManager {
/**
* Returns anti-override.log file
* @return anti-override.log file
*
* @return anti-override.log file
*/
public File getAntiOverrideLog() {
return antiOverrideLog;
@@ -57,10 +57,10 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls refresh(TabPlayer, boolean) on all features
*
* @param refreshed
* player to refresh
* @param force
* whether refresh should be forced or not
* @param refreshed
* player to refresh
* @param force
* whether refresh should be forced or not
*/
public void refresh(TabPlayer refreshed, boolean force) {
for (TabFeature f : values) f.refresh(refreshed, force);
@@ -69,13 +69,13 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls onPlayerInfo(TabPlayer, Object) on all features
*
* @param receiver
* packet receiver
* @param packet
* an instance of custom packet class PacketPlayOutPlayerInfo
* @return altered rebuilt packet
* @throws ReflectiveOperationException
* if reflective operation fails
* @param receiver
* packet receiver
* @param packet
* an instance of custom packet class PacketPlayOutPlayerInfo
* @return altered rebuilt packet
* @throws ReflectiveOperationException
* if reflective operation fails
*/
public Object onPacketPlayOutPlayerInfo(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
if (receiver.getVersion().getMinorVersion() < 8) return packet;
@@ -97,8 +97,8 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls onQuit(TabPlayer) on all features
*
* @param disconnectedPlayer
* player who disconnected
* @param disconnectedPlayer
* player who disconnected
*/
public void onQuit(TabPlayer disconnectedPlayer) {
if (disconnectedPlayer == null) return;
@@ -116,19 +116,23 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls onJoin(TabPlayer) on all features
*
* @param connectedPlayer
* player who connected
* @param connectedPlayer
* player who connected
*/
public void onJoin(TabPlayer connectedPlayer) {
if (!connectedPlayer.isOnline()) return;
long millis = System.currentTimeMillis();
TAB.getInstance().addPlayer(connectedPlayer);
for (TabFeature f : values) {
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");
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);
}
}
((ITabPlayer)connectedPlayer).markAsLoaded(true);
TAB.getInstance().debug("Player join of " + connectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
@@ -141,10 +145,10 @@ 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
* @param playerUUID
* player who switched world
* @param to
* name of the new world
*/
public void onWorldChange(UUID playerUUID, String to) {
TabPlayer changed = TAB.getInstance().getPlayer(playerUUID);
@@ -157,41 +161,41 @@ public class FeatureManagerImpl implements FeatureManager {
f.onWorldChange(changed, from, to);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
}
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%world%")).updateValue(changed, to);
((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
* @param playerUUID
* player who switched server
* @param to
* name of the new server
*/
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);
if (!isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION)) ((ITabPlayer)changed).clearRegisteredObjectives();
((ProxyTabPlayer)changed).sendJoinPluginMessage();
if (!isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION)) onLoginPacket(changed);
for (TabFeature f : values) {
if (!f.overridesMethod("onServerChange")) continue;
long time = System.nanoTime();
f.onServerChange(changed, from, to);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.SERVER_SWITCH, System.nanoTime()-time);
}
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%server%")).updateValue(changed, to);
((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
* @param sender
* command sender
* @param command
* command line including /
* @return {@code true} if command should be cancelled, {@code false} if not
*/
public boolean onCommand(TabPlayer sender, String command) {
if (sender == null) return false;
@@ -208,11 +212,11 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls onPacketReceive(TabPlayer, Object) on all features
*
* @param sender
* packet sender
* @param packet
* IN packet coming from player
* @return {@code true} if packet should be cancelled, {@code false} if not
* @param sender
* packet sender
* @param packet
* IN packet coming from player
* @return {@code true} if packet should be cancelled, {@code false} if not
*/
public boolean onPacketReceive(TabPlayer sender, Object packet){
boolean cancel = false;
@@ -232,10 +236,10 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls onPacketSend(TabPlayer, Object) on all features
*
* @param receiver
* packet receiver
* @param packet
* OUT packet coming from the server
* @param receiver
* packet receiver
* @param packet
* OUT packet coming from the server
*/
public void onPacketSend(TabPlayer receiver, Object packet){
for (TabFeature f : values) {
@@ -253,8 +257,8 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls onLoginPacket(TabPlayer) on all features
*
* @param packetReceiver
* player who received the packet
* @param packetReceiver
* player who received the packet
*/
public void onLoginPacket(TabPlayer packetReceiver) {
((ITabPlayer)packetReceiver).clearRegisteredObjectives();
@@ -269,12 +273,12 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls onDisplayObjective(TabPlayer, PacketPlayOutScoreboardDisplayObjective) on all features
*
* @param packetReceiver
* player who received the packet
* @param packet
* the packet
* @throws ReflectiveOperationException
* if reflective operation fails
* @param packetReceiver
* player who received the packet
* @param packet
* the packet
* @throws ReflectiveOperationException
* if reflective operation fails
*/
public void onDisplayObjective(TabPlayer packetReceiver, Object packet) throws ReflectiveOperationException {
long time = System.nanoTime();
@@ -291,10 +295,10 @@ public class FeatureManagerImpl implements FeatureManager {
/**
* Calls onObjective(TabPlayer, PacketPlayOutScoreboardObjective) on all features
*
* @param packetReceiver
* player who received the packet
* @throws ReflectiveOperationException
* if reflective operation fails
* @param packetReceiver
* player who received the packet
* @throws ReflectiveOperationException
* if reflective operation fails
*/
public void onObjective(TabPlayer packetReceiver, Object packet) throws ReflectiveOperationException {
long time = System.nanoTime();
@@ -1,19 +1,11 @@
package me.neznamy.tab.shared;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.None;
import me.neznamy.tab.shared.permission.PermissionPlugin;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.event.EventSubscription;
import net.luckperms.api.event.group.GroupDataRecalculateEvent;
import net.luckperms.api.event.user.UserDataRecalculateEvent;
import java.util.Arrays;
import java.util.List;
/**
* Permission group manager retrieving groups from permission plugin
@@ -29,60 +21,19 @@ public class GroupManager extends TabFeature {
/** List of group permissions to iterate through if {@link #groupsByPermissions} is {@code true} */
private final List<String> primaryGroupFindingList = TAB.getInstance().getConfiguration().getConfig().getStringList("primary-group-finding-list", Arrays.asList("Owner", "Admin", "Helper", "default"));
/** UserDataRecalculateEvent LuckPerms event listener */
private EventSubscription<UserDataRecalculateEvent> luckPermsSub;
/** GroupDataRecalculateEvent LuckPerms event listener */
private EventSubscription<GroupDataRecalculateEvent> luckPermsSub2;
/**
* Constructs new instance with given permission plugin, loads
* event listeners and registers group placeholder.
*
* @param plugin
* Detected permission plugin
* @param plugin
* Detected permission plugin
*/
public GroupManager(PermissionPlugin plugin) {
super("Permission group refreshing", "Refreshing group");
super("Permission group refreshing", null);
this.plugin = plugin;
if (plugin instanceof LuckPerms) {
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%group%", -1, TabPlayer::getGroup).enableTriggerMode();
luckPermsSub = LuckPermsProvider.get().getEventBus().subscribe(UserDataRecalculateEvent.class, this::updatePlayer);
luckPermsSub2 = LuckPermsProvider.get().getEventBus().subscribe(GroupDataRecalculateEvent.class, this::updateGroup);
} else if (plugin instanceof None && !groupsByPermissions){
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%group%", -1, p -> TabConstants.DEFAULT_GROUP);
} else {
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%group%", 1000, this::detectPermissionGroup);
addUsedPlaceholders(Collections.singletonList("%group%"));
}
}
/**
* Processes UserDataRecalculateEvent
*
* @param event
* Event to process
*/
private void updatePlayer(UserDataRecalculateEvent event) {
TAB.getInstance().getCPUManager().runTaskLater(50, this, TabConstants.CpuUsageCategory.LUCKPERMS_USER_RECALCULATE_EVENT, () -> {
TabPlayer p = TAB.getInstance().getPlayer(event.getUser().getUniqueId());
if (p == null) return; // player not loaded yet
refresh(p, false);
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%group%")).updateValue(p, p.getGroup());
});
}
/**
* Processes GroupDataRecalculateEvent
*
* @param event
* Event to process
*/
private void updateGroup(GroupDataRecalculateEvent event) {
TAB.getInstance().getCPUManager().runTaskLater(50, this, TabConstants.CpuUsageCategory.LUCKPERMS_GROUP_RECALCULATE_EVENT, () -> {
for (TabPlayer player : TAB.getInstance().getOnlinePlayers()) {
refresh(player, false);
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%group%")).updateValue(player, player.getGroup());
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(1000, this, TabConstants.CpuUsageCategory.GROUP_REFRESHING, () -> {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
((ITabPlayer)all).setGroup(detectPermissionGroup(all));
}
});
}
@@ -90,9 +41,9 @@ public class GroupManager extends TabFeature {
/**
* Detects player's permission group using configured method and returns it
*
* @param player
* Player to detect permission group of
* @return Detected permission group
* @param player
* Player to detect permission group of
* @return Detected permission group
*/
public String detectPermissionGroup(TabPlayer player) {
return groupsByPermissions ? getByPermission(player) : getByPrimary(player);
@@ -101,42 +52,43 @@ public class GroupManager extends TabFeature {
/**
* Returns player's permission group from detected permission plugin
*
* @param player
* Player to get permission group of
* @return Permission group from permission plugin
* @param player
* Player to get permission group of
* @return Permission group from permission plugin
*/
private String getByPrimary(TabPlayer player) {
try {
return plugin.getPrimaryGroup(player);
String group = plugin.getPrimaryGroup(player);
return group == null ? TabConstants.NO_GROUP : group;
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to get permission group of " + player.getName() + " using " + plugin.getName() + " v" + plugin.getVersion(), e);
return TabConstants.DEFAULT_GROUP;
return TabConstants.NO_GROUP;
}
}
/**
* Returns player's permission group based on highest permission
* or {@link TabConstants#DEFAULT_GROUP} if player has no permission.
* or {@link TabConstants#NO_GROUP} if player has no permission.
*
* @param player
* Player to get permission group of
* @return Highest permission group player has permission for
* or {@link TabConstants#DEFAULT_GROUP} if player does not have any
* @param player
* Player to get permission group of
* @return Highest permission group player has permission for
* or {@link TabConstants#NO_GROUP} if player does not have any
*/
private String getByPermission(TabPlayer player) {
for (String group : primaryGroupFindingList) {
if (player.hasPermission(TabConstants.Permission.GROUP_PREFIX + group)) {
return String.valueOf(group);
return group;
}
}
return TabConstants.DEFAULT_GROUP;
return TabConstants.NO_GROUP;
}
/**
* Returns {@code true} if assigning by permissions is configured,
* {@code false} if not.
*
* @return {@code true} if assigning by permissions, {@code false} if not
* @return {@code true} if assigning by permissions, {@code false} if not
*/
public boolean isGroupsByPermissions() {
return groupsByPermissions;
@@ -145,22 +97,9 @@ public class GroupManager extends TabFeature {
/**
* Returns detected permission plugin
*
* @return detected permission plugin
* @return detected permission plugin
*/
public PermissionPlugin getPlugin() {
return plugin;
}
@Override
public void refresh(TabPlayer p, boolean force) {
((ITabPlayer)p).setGroup(TAB.getInstance().getPlaceholderManager().getPlaceholder("%group%").getLastValue(p));
}
@Override
public void unload() {
if (luckPermsSub != null) {
luckPermsSub.close();
luckPermsSub2.close();
}
}
}
@@ -8,11 +8,12 @@ import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
import me.neznamy.tab.api.protocol.*;
import me.neznamy.tab.api.protocol.PacketPlayOutChat.ChatMessageType;
import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.shared.event.impl.PlayerLoadEventImpl;
import me.neznamy.tab.shared.features.NickCompatibility;
import org.geysermc.floodgate.api.FloodgateApi;
/**
@@ -75,48 +76,51 @@ public abstract class ITabPlayer implements TabPlayer {
/** Scoreboard objectives player has registered */
private final List<String> registeredObjectives = new ArrayList<>();
/** Player's name as seen in GameProfile, can be altered by nick plugins */
private String nickname;
/**
* Constructs new instance with given parameters
*
* @param player
* platform-specific player object
* @param uniqueId
* Player's unique ID
* @param name
* Player's name
* @param server
* Player's server
* @param world
* Player's world
* @param protocolVersion
* Player's game version
* @param player
* platform-specific player object
* @param uniqueId
* Player's unique ID
* @param name
* Player's name
* @param server
* Player's server
* @param world
* Player's world
* @param protocolVersion
* Player's game version
*/
protected ITabPlayer(Object player, UUID uniqueId, String name, String server, String world, int protocolVersion) {
this.player = player;
this.uniqueId = uniqueId;
this.name = name;
this.nickname = name;
this.server = server;
this.world = world;
this.version = ProtocolVersion.fromNetworkId(protocolVersion);
this.bedrockPlayer = TAB.getInstance().isFloodgateInstalled() && FloodgateApi.getInstance() != null && FloodgateApi.getInstance().isFloodgatePlayer(uniqueId);
String group = TAB.getInstance().getGroupManager().detectPermissionGroup(this);
permissionGroup = group != null ? group : TabConstants.DEFAULT_GROUP;
this.permissionGroup = TAB.getInstance().getGroupManager().detectPermissionGroup(this);
}
/**
* Sets player's property with provided key to provided value. If it existed,
* the raw value is changed. If it did not exist, it is created.
*
* @param feature
* Feature creating the property
* @param identifier
* Property's unique identifier
* @param rawValue
* Raw value with raw placeholders
* @param source
* Source of raw value
* @return {@code true} if property did not exist or existed with different raw value,
* {@code false} if property existed with the same raw value already.
* @param feature
* Feature creating the property
* @param identifier
* Property's unique identifier
* @param rawValue
* Raw value with raw placeholders
* @param source
* Source of raw value
* @return {@code true} if property did not exist or existed with different raw value,
* {@code false} if property existed with the same raw value already.
*/
private boolean setProperty(TabFeature feature, String identifier, String rawValue, String source, boolean exposeInExpansion) {
DynamicText p = (DynamicText) getProperty(identifier);
@@ -132,11 +136,16 @@ public abstract class ITabPlayer implements TabPlayer {
}
}
@Override
public boolean setProperty(TabFeature feature, String identifier, String rawValue) {
return setProperty(feature, identifier, rawValue, null, false);
}
/**
* Sets team name to given value
*
* @param name
* Team name to use
* @param name
* Team name to use
*/
public void setTeamName(String name) {
teamName = name;
@@ -145,8 +154,8 @@ public abstract class ITabPlayer implements TabPlayer {
/**
* Sets team name note to given value
*
* @param note
* New team name note
* @param note
* New team name note
*/
public void setTeamNameNote(String note) {
teamNameNote = note;
@@ -155,8 +164,8 @@ public abstract class ITabPlayer implements TabPlayer {
/**
* Marks the player as loaded and calls PlayerLoadEvent
*
* @param join
* {@code true} if this is a player join, {@code false} if reload
* @param join
* {@code true} if this is a player join, {@code false} if reload
*/
public void markAsLoaded(boolean join) {
onJoinFinished = true;
@@ -167,24 +176,22 @@ public abstract class ITabPlayer implements TabPlayer {
/**
* Changes player's group to provided value and all features are refreshed.
*
* @param permissionGroup
* New permission group
* @param permissionGroup
* New permission group
*/
public void setGroup(String permissionGroup) {
Preconditions.checkNotNull(permissionGroup, "permissionGroup");
if (this.permissionGroup.equals(permissionGroup)) return;
if (permissionGroup != null) {
this.permissionGroup = permissionGroup;
} else {
this.permissionGroup = TabConstants.DEFAULT_GROUP;
}
this.permissionGroup = permissionGroup;
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.GROUP)).updateValue(this, permissionGroup);
forceRefresh();
}
/**
* Sets player's world to given value
*
* @param name
* Name of the new world
* @param name
* Name of the new world
*/
public void setWorld(String name) {
world = name;
@@ -193,8 +200,8 @@ public abstract class ITabPlayer implements TabPlayer {
/**
* Sets player's server to given value
*
* @param name
* Name of the new server
* @param name
* Name of the new server
*/
public void setServer(String name) {
server = name;
@@ -259,6 +266,7 @@ public abstract class ITabPlayer implements TabPlayer {
@Override
public void forceRefresh() {
if (!onJoinFinished) return;
TAB.getInstance().getFeatureManager().refresh(this, true);
}
@@ -336,18 +344,6 @@ public abstract class ITabPlayer implements TabPlayer {
sendCustomPacket(packet);
if (feature != null) TAB.getInstance().getCPUManager().packetSent(feature);
}
@Override
public void sendPacket(Object nmsPacket, TabFeature feature) {
sendPacket(nmsPacket);
if (feature != null) TAB.getInstance().getCPUManager().packetSent(feature.getFeatureName());
}
@Override
public void sendPacket(Object packet, String feature) {
sendPacket(packet);
if (feature != null) TAB.getInstance().getCPUManager().packetSent(feature);
}
@Override
public Property getProperty(String name) {
@@ -405,14 +401,13 @@ public abstract class ITabPlayer implements TabPlayer {
public boolean isBedrockPlayer() {
return bedrockPlayer;
}
@Override
public boolean setProperty(TabFeature feature, String identifier, String rawValue) {
return setProperty(feature, identifier, rawValue, null, false);
}
@Override
public String getNickname() {
return ((NickCompatibility) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.NICK_COMPATIBILITY)).getNickname(this);
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
}
@@ -1,8 +1,10 @@
package me.neznamy.tab.shared;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.protocol.PacketBuilder;
import me.neznamy.tab.shared.permission.PermissionPlugin;
import org.slf4j.Logger;
/**
* An interface with methods that are called in universal code,
@@ -16,8 +18,8 @@ public abstract class Platform {
/**
* Constructs new instance with given parameter
*
* @param packetBuilder
* Platform's packet builder
* @param packetBuilder
* Platform's packet builder
*/
protected Platform(PacketBuilder packetBuilder) {
this.packetBuilder = packetBuilder;
@@ -27,27 +29,40 @@ public abstract class Platform {
* Calls platform-specific load event.
* This method is called when plugin is fully enabled.
*/
public void callLoadEvent(){}
public void callLoadEvent(){
//do nothing by default, old event system will be removed
}
/**
* Calls platform-specific player load event.
* This method is called when player is fully loaded.
*/
public void callLoadEvent(TabPlayer player){}
public void callLoadEvent(TabPlayer player){
//do nothing by default, old event system will be removed
}
/**
* Returns platform-specific packet builder implementation
*
* @return platform-specific packet builder
* @return platform-specific packet builder
*/
public PacketBuilder getPacketBuilder(){
return packetBuilder;
}
public void sendConsoleMessage(String message, boolean translateColors) {
Object logger = TAB.getInstance().getLogger();
if (logger instanceof java.util.logging.Logger) {
((java.util.logging.Logger) logger).info(translateColors ? EnumChatFormat.color(message) : message);
} else if (logger instanceof Logger) {
((Logger) logger).info(translateColors ? EnumChatFormat.color(message) : message);
}
}
/**
* Detects permission plugin and returns it's representing object
*
* @return the interface representing the permission hook
* @return the interface representing the permission hook
*/
public abstract PermissionPlugin detectPermissionPlugin();
@@ -60,25 +75,18 @@ public abstract class Platform {
* Creates an instance of {@link me.neznamy.tab.api.placeholder.Placeholder}
* to handle this unknown placeholder (typically a PAPI placeholder)
*
* @param identifier
* placeholder's identifier
* @param identifier
* placeholder's identifier
*/
public abstract void registerUnknownPlaceholder(String identifier);
/**
* Returns {@code true} if this platform is a proxy, {@code false} if a game server
*
* @return {@code true} if this platform is a proxy, {@code false} if a game server
*/
public abstract boolean isProxy();
/**
* Performs platform-specific plugin manager call and returns the result.
* If plugin is not installed, returns {@code null}.
*
* @param plugin
* Plugin to check version of
* @return Version string if plugin is installed, {@code null} if not
* @param plugin
* Plugin to check version of
* @return Version string if plugin is installed, {@code null} if not
*/
public abstract String getPluginVersion(String plugin);
@@ -86,7 +94,7 @@ public abstract class Platform {
* Returns name of default config file for this platform
* as it appears in the final jar in root directory.
*
* @return name of default config file for this platform
* @return name of default config file for this platform
*/
public abstract String getConfigName();
}
@@ -1,22 +1,6 @@
package me.neznamy.tab.shared;
import java.io.File;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.shared.event.EventBusImpl;
import me.neznamy.tab.shared.event.impl.TabLoadEventImpl;
import org.slf4j.Logger;
import org.yaml.snakeyaml.error.YAMLException;
import me.neznamy.tab.api.HeaderFooterManager;
import me.neznamy.tab.api.PropertyConfiguration;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.TablistFormatManager;
import me.neznamy.tab.api.*;
import me.neznamy.tab.api.bossbar.BossBarManager;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
@@ -24,20 +8,20 @@ import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.shared.command.DisabledCommand;
import me.neznamy.tab.shared.command.TabCommand;
import me.neznamy.tab.shared.config.Configs;
import me.neznamy.tab.shared.event.EventBusImpl;
import me.neznamy.tab.shared.event.impl.TabLoadEventImpl;
import me.neznamy.tab.shared.features.*;
import me.neznamy.tab.shared.features.alignedplayerlist.AlignedPlayerList;
import me.neznamy.tab.shared.features.BelowName;
import me.neznamy.tab.shared.features.GhostPlayerFix;
import me.neznamy.tab.shared.features.HeaderFooter;
import me.neznamy.tab.shared.features.NickCompatibility;
import me.neznamy.tab.shared.features.PingSpoof;
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
import me.neznamy.tab.shared.features.PlayerList;
import me.neznamy.tab.shared.features.SpectatorFix;
import me.neznamy.tab.shared.features.YellowNumber;
import me.neznamy.tab.shared.features.layout.LayoutManager;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
import me.neznamy.tab.shared.features.sorting.Sorting;
import org.yaml.snakeyaml.error.YAMLException;
import java.io.File;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Main class of the plugin storing data and implementing API
@@ -112,14 +96,17 @@ public class TAB extends TabAPI {
/** Plugin's console logger provided by platform */
private final Object logger;
/** File with YAML syntax error, which prevented plugin from loading */
private String brokenFile;
/**
* Constructs new instance with given parameters and sets this
* new instance as {@link me.neznamy.tab.api.TabAPI} instance.
*
* @param platform
* Platform interface
* @param serverVersion
* Version the server is running on
* @param platform
* Platform interface
* @param serverVersion
* Version the server is running on
*/
public TAB(Platform platform, ProtocolVersion serverVersion, String serverVersionString, File dataFolder, Object logger) {
this.platform = platform;
@@ -145,9 +132,9 @@ public class TAB extends TabAPI {
* Returns player by TabList UUID. This is required due to Velocity
* as player uuid and TabList uuid do not match there at some circumstances
*
* @param tabListId
* TabList id of player
* @return player with provided id or null if player was not found
* @param tabListId
* TabList id of player
* @return player with provided id or null if player was not found
*/
public TabPlayer getPlayerByTabListUUID(UUID tabListId) {
return playersByTabListId.get(tabListId);
@@ -162,11 +149,10 @@ public class TAB extends TabAPI {
public String load() {
try {
long time = System.currentTimeMillis();
this.errorManager = new ErrorManager();
errorManager = new ErrorManager();
cpu = new CpuManager();
featureManager = new FeatureManagerImpl();
configuration = new Configs(this);
configuration.loadFiles();
configuration = new Configs();
featureManager.registerFeature(TabConstants.Feature.PLACEHOLDER_MANAGER, new PlaceholderManagerImpl());
featureManager.registerFeature(TabConstants.Feature.GROUP_MANAGER, new GroupManager(platform.detectPermissionPlugin()));
platform.loadFeatures();
@@ -182,7 +168,8 @@ public class TAB extends TabAPI {
} catch (YAMLException e) {
sendConsoleMessage("&cDid not enable due to a broken configuration file.", true);
kill();
return configuration.getReloadFailedMessage();
return (configuration == null ? "&4Failed to reload, file %file% has broken syntax. Check console for more info."
: configuration.getMessages().getReloadFailBrokenFile()).replace("%file%", brokenFile);
} catch (Exception e) {
errorManager.criticalError("Failed to enable. Did you just invent a new way to break the plugin by misconfiguring it?", e);
kill();
@@ -256,8 +243,8 @@ public class TAB extends TabAPI {
/**
* Adds specified player to online players
*
* @param player
* Player to add
* @param player
* Player to add
*/
public void addPlayer(TabPlayer player) {
data.put(player.getUniqueId(), player);
@@ -268,8 +255,8 @@ public class TAB extends TabAPI {
/**
* Removes specified player from online players
*
* @param player
* Player to remove
* @param player
* Player to remove
*/
public void removePlayer(TabPlayer player) {
data.remove(player.getUniqueId());
@@ -280,7 +267,7 @@ public class TAB extends TabAPI {
/**
* Returns instance of this class
*
* @return instance of this class
* @return instance of this class
*/
public static TAB getInstance() {
return instance;
@@ -289,8 +276,8 @@ public class TAB extends TabAPI {
/**
* Changes instance of this class to new value
*
* @param instance
* Instance to set variable to
* @param instance
* Instance to set variable to
*/
public static void setInstance(TAB instance) {
TAB.instance = instance;
@@ -299,7 +286,7 @@ public class TAB extends TabAPI {
/**
* Returns {@code true} if floodgate plugin is installed, {@code false} if not
*
* @return {@code true} if floodgate plugin is installed, {@code false} if not
* @return {@code true} if floodgate plugin is installed, {@code false} if not
*/
public boolean isFloodgateInstalled() {
return floodgate;
@@ -308,7 +295,7 @@ public class TAB extends TabAPI {
/**
* Returns TAB's group manager used to refresh player groups from other plugins
*
* @return group manager instance
* @return group manager instance
*/
public GroupManager getGroupManager() {
return (GroupManager) featureManager.getFeature(TabConstants.Feature.GROUP_MANAGER);
@@ -317,7 +304,7 @@ public class TAB extends TabAPI {
/**
* Returns {@link #platform}
*
* @return {@link #platform}
* @return {@link #platform}
*/
public Platform getPlatform() {
return platform;
@@ -326,7 +313,7 @@ public class TAB extends TabAPI {
/**
* Returns {@link #cpu}
*
* @return {@link #cpu}
* @return {@link #cpu}
*/
public CpuManager getCPUManager() {
return cpu;
@@ -335,7 +322,7 @@ public class TAB extends TabAPI {
/**
* Returns {@link #errorManager}
*
* @return {@link #errorManager}
* @return {@link #errorManager}
*/
public ErrorManager getErrorManager() {
return errorManager;
@@ -344,7 +331,7 @@ public class TAB extends TabAPI {
/**
* Returns {@link #configuration}
*
* @return {@link #configuration}
* @return {@link #configuration}
*/
public Configs getConfiguration() {
return configuration;
@@ -353,7 +340,7 @@ public class TAB extends TabAPI {
/**
* Returns {@link #disabled}
*
* @return {@link #disabled}
* @return {@link #disabled}
*/
public boolean isDisabled() {
return disabled;
@@ -362,7 +349,7 @@ public class TAB extends TabAPI {
/**
* Returns {@link #command}
*
* @return {@link #command}
* @return {@link #command}
*/
public TabCommand getCommand() {
return command;
@@ -371,7 +358,7 @@ public class TAB extends TabAPI {
/**
* Returns {@link #disabledCommand}
*
* @return {@link #disabledCommand}
* @return {@link #disabledCommand}
*/
public DisabledCommand getDisabledCommand() {
return disabledCommand;
@@ -380,7 +367,7 @@ public class TAB extends TabAPI {
/**
* Returns {@link #serverVersionString}
*
* @return {@link #serverVersionString}
* @return {@link #serverVersionString}
*/
public String getServerVersionString() {
return serverVersionString;
@@ -389,12 +376,21 @@ public class TAB extends TabAPI {
/**
* Returns {@link #dataFolder}
*
* @return {@link #dataFolder}
* @return {@link #dataFolder}
*/
public File getDataFolder() {
return dataFolder;
}
/**
* Returns {@link #logger}
*
* @return {@link #logger}
*/
public Object getLogger() {
return logger;
}
@Override
public EventBusImpl getEventBus() {
return eventBus;
@@ -441,12 +437,7 @@ public class TAB extends TabAPI {
@Override
public void sendConsoleMessage(String message, boolean translateColors) {
if (translateColors) message = EnumChatFormat.color(message);
if (logger instanceof java.util.logging.Logger) {
((java.util.logging.Logger) logger).info(message);
} else if (logger instanceof org.slf4j.Logger) {
((Logger) logger).info(message);
}
platform.sendConsoleMessage(message, translateColors);
}
@Override
@@ -486,7 +477,7 @@ public class TAB extends TabAPI {
@Override
public void setBrokenFile(String file) {
configuration.setBrokenFile(file);
this.brokenFile = file;
}
@Override
@@ -7,7 +7,8 @@ package me.neznamy.tab.shared;
public class TabConstants {
public static final String PLUGIN_VERSION = "@plugin_version@";
public static final String DEFAULT_GROUP = "NONE";
public static final String NO_GROUP = "NONE";
public static final String DEFAULT_GROUP = "_DEFAULT_";
public static final String PLUGIN_MESSAGE_CHANNEL_NAME = "tab:bridge-2";
public static final String REDIS_CHANNEL_NAME = "TAB";
@@ -31,8 +32,6 @@ public class TabConstants {
public static final String PLAYER_RESPAWN = "Player Respawn";
public static final String PLUGIN_MESSAGE = "PluginMessageEvent";
public static final String REDIS_BUNGEE_MESSAGE = "PubSubMessageEvent";
public static final String LUCKPERMS_USER_RECALCULATE_EVENT = "UserDataRecalculateEvent";
public static final String LUCKPERMS_GROUP_RECALCULATE_EVENT = "GroupDataRecalculateEvent";
public static final String VANISH_CHANGE = "Vanish status change";
public static final String ANTI_OVERRIDE = "Anti override";
@@ -49,8 +48,8 @@ public class TabConstants {
public static final String RAW_PACKET_OUT = "Packet reading (out)";
public static final String PLACEHOLDER_REFRESHING = "Refreshing placeholders";
public static final String GROUP_REFRESHING = "Refreshing groups";
public static final String V1_8_0_BUG_COMPENSATION = "Compensating for 1.8.0 bugs";
public static final String REFRESHING_NAME_TAG_VISIBILITY = "Refreshing NameTag visibility";
public static final String SCOREBOARD_PACKET_CHECK = "Checking for other plugins";
public static final String PROCESSING_PLAYER_MOVEMENT = "Processing player movement";
@@ -78,6 +77,7 @@ public class TabConstants {
public static final String LAYOUT_FIXED_SLOTS = "Layout (Fixed slots)";
public static final String LAYOUT_PLAYER_SLOTS = "Layout (Player slots)";
public static final String LAYOUT_LATENCY = "Layout (Updating latency)";
public static final String NAMETAGS_TEAM_REGISTER = "NameTags (Team register)";
public static final String NAMETAGS_TEAM_UNREGISTER = "NameTags (Team unregister)";
@@ -225,6 +225,7 @@ public class TabConstants {
public static final String PING_SPOOF = "PingSpoof";
public static final String SORTING = "sorting";
public static final String LAYOUT = "layout";
public static final String LAYOUT_LATENCY = "layout-latency";
public static final String NICK_COMPATIBILITY = "nick";
public static final String PIPELINE_INJECTION = "injection";
public static final String BOSS_BAR = "BossBar";
@@ -259,6 +260,10 @@ public class TabConstants {
return "scoreboard-" + line;
}
public static String scoreboardLine(String scoreboard, int index) {
return "scoreboard-score-" + scoreboard + "-" + index;
}
public static String layout(String name) {
return "layout-" + name;
}
@@ -278,9 +283,87 @@ public class TabConstants {
public static String bossBarColorStyle(String name) {
return "BossBar-color-style-" + name;
}
}
public static String scoreboardLine(String scoreboard, int index) {
return "scoreboard-score-" + scoreboard + "-" + index;
/**
* All internal placeholders offered by the plugin
*/
public static class Placeholder {
// Universal
public static final String DISPLAY_NAME = "%displayname%";
public static final String GROUP = "%group%";
public static final String VANISHED = "%vanished%";
public static final String WORLD = "%world%";
public static final String WORLD_ONLINE = "%worldonline%";
public static final String SERVER = "%server%";
public static final String SERVER_ONLINE = "%serveronline%";
public static final String PLAYER = "%player%";
public static final String TIME = "%time%";
public static final String DATE = "%date%";
public static final String PING = "%ping%";
public static final String PLAYER_VERSION = "%player-version%";
public static final String PLAYER_VERSION_ID = "%player-version-id%";
public static final String MEMORY_USED = "%memory-used%";
public static final String MEMORY_MAX = "%memory-max%";
public static final String MEMORY_USED_GB = "%memory-used-gb%";
public static final String MEMORY_MAX_GB = "%memory-max-gb%";
public static final String ONLINE = "%online%";
public static final String STAFF_ONLINE = "%staffonline%";
public static final String NON_STAFF_ONLINE = "%nonstaffonline%";
public static final String LUCKPERMS_PREFIX = "%luckperms-prefix%";
public static final String LUCKPERMS_SUFFIX = "%luckperms-suffix%";
public static final String COUNTDOWN = "%countdown%";
// Placeholders for internal use
public static final String COLLISION = "%collision%";
public static final String INVISIBLE = "%invisible%";
public static final String VEHICLE = "%vehicle%";
// Bukkit only
public static final String TPS = "%tps%";
public static final String MSPT = "%mspt%";
public static final String AFK = "%afk%";
public static final String ESSENTIALS_NICK = "%essentialsnick%";
public static final String VAULT_PREFIX = "%vault-prefix%";
public static final String VAULT_SUFFIX = "%vault-suffix%";
public static final String HEALTH = "%health%";
public static String condition(String name) {
return "%condition:" + name + "%";
}
public static String animation(String name) {
return "%animation:" + name + "%";
}
}
/**
* All plugins TAB hooks into
*/
public static class Plugin {
public static final String LUCKPERMS = "LuckPerms";
public static final String VAULT = "Vault";
public static final String PLACEHOLDER_API = "PlaceholderAPI";
public static final String LIBS_DISGUISES = "LibsDisguises";
public static final String VIAVERSION = "ViaVersion";
public static final String PROTOCOL_SUPPORT = "ProtocolSupport";
public static final String ESSENTIALS = "Essentials";
public static final String REDIS_BUNGEE = "RedisBungee";
public static final String PREMIUM_VANISH = "PremiumVanish";
}
/**
* TAB's custom metrics charts
*/
public static class MetricsChart {
public static final String PERMISSION_SYSTEM = "permission_system";
public static final String GLOBAL_PLAYER_LIST_ENABLED = "global_playerlist_enabled";
public static final String PLACEHOLDER_API = "placeholderapi";
public static final String SERVER_VERSION = "server_version";
public static final String UNLIMITED_NAME_TAG_MODE_ENABLED = "unlimited_nametag_mode_enabled";
}
}
Loaded 100 of 190 files, more files were not shown because too many files have changed in this diff. Show more