Compare commits

...
114 Commits
Author SHA1 Message Date
NEZNAMY 94072acf10 3.2.4 2023-01-25 10:39:44 +01:00
NEZNAMY 0422cc2597 Update issue templates 2023-01-24 14:00:13 +01:00
NEZNAMY 58f61e645b 3.2.4-SNAPSHOT 2023-01-24 10:45:11 +01:00
NEZNAMY 5f416bd9e5 Unlimited NameTags - Avoid Bukkit API NPE (#800, #804) 2023-01-24 10:44:35 +01:00
NEZNAMY 357e7c6a49 Revert "Remove redundant try/catch"
This reverts commit 1777d8bf01.
2023-01-23 14:39:54 +01:00
NEZNAMY 81dd202053 3.2.3 2023-01-21 14:58:34 +01:00
NEZNAMY 189797aa73 Bukkit - Use the other constructor for player info packet which is cleaner (also #797) 2023-01-21 00:18:19 +01:00
NEZNAMY 1ae2ef7cad Fix compilation 2023-01-21 00:17:02 +01:00
NEZNAMY de14e14d33 Revert "Placeholders - Add timeout for placeholders to return value, causing TAB to freeze"
This reverts commit 37caed3123.
2023-01-19 22:30:09 +01:00
NEZNAMY 19892c14c1 Revert "Placeholders - Increase placeholder retrieve timeout to 500ms (#792)"
This reverts commit 6d85875fde.
2023-01-19 22:29:59 +01:00
NEZNAMY 2e00c4c002 Revert "NameTags - Fix error that doesn't make sense (#794)"
This reverts commit 12f5dd52b4.
2023-01-19 22:29:52 +01:00
NEZNAMY 97aa367629 Revert "Fix thread pool not being single threaded (maybe #781?)"
This reverts commit 87cc9a030b.
2023-01-19 22:28:22 +01:00
NEZNAMY 87cc9a030b Fix thread pool not being single threaded (maybe #781?) 2023-01-19 22:01:11 +01:00
NEZNAMY 8ba83d1cb1 BossBar - Fix errors when progress is below 0 or above 100 2023-01-19 16:59:05 +01:00
NEZNAMY 9f0e60bd47 Per world PlayerList - Remove redundant task schedule to main thread 2023-01-19 16:58:45 +01:00
NEZNAMY 93b2a25b2e Merge pull request #795 from Chris6ix/update-actions
Update Actions
2023-01-17 22:01:37 +01:00
Chris 4242d7a839 Remove Test Build Trigger 2023-01-17 21:33:33 +01:00
Chris 0ddb99bd46 Trigger Test Build 2023-01-17 21:29:34 +01:00
Chris 969672e3ad Update Actions 2023-01-17 21:26:40 +01:00
NEZNAMY 2423fbb150 Update README.md 2023-01-17 20:28:47 +01:00
NEZNAMY a99928dbf5 Placeholders - Colorize output of relational placeholders as well 2023-01-17 20:25:06 +01:00
NEZNAMY 12f5dd52b4 NameTags - Fix error that doesn't make sense (#794) 2023-01-17 15:38:20 +01:00
NEZNAMY 2bd105446e Bukkit - Properly send RGB messages into console (only for Paper+, Spigot doesn't support RGB at all) 2023-01-17 14:11:51 +01:00
NEZNAMY 6d85875fde Placeholders - Increase placeholder retrieve timeout to 500ms (#792) 2023-01-16 18:49:23 +01:00
NEZNAMY 42810aa490 3.2.3-SNAPSHOT 2023-01-16 18:47:42 +01:00
NEZNAMY bd31521bd3 3.2.2 2023-01-15 12:02:05 +01:00
NEZNAMY ec0deb8378 Yellow Number - Disable the feature for Bedrock players 2023-01-12 21:57:56 +01:00
NEZNAMY ae66b6e9a1 RGB - Fix MiniMessage->Bukkit conversion being after Bukkit->TAB, making it not translate anymore 2023-01-12 14:55:41 +01:00
NEZNAMY 3b86becebd Header/Footer - Do not add &r after \n anymore 2023-01-12 14:53:38 +01:00
NEZNAMY 4a8d86825b Configuration - Small changes to default configuration files 2023-01-10 18:56:34 +01:00
NEZNAMY 54a3bd81b4 Global PlayerList - Increase player removal prevention delay to 2000ms from 500ms 2023-01-10 16:49:16 +01:00
NEZNAMY b6768cca99 CPU manager - Add try/catch for task scheduling (#781) 2023-01-10 16:48:28 +01:00
NEZNAMY 60c414deed Bukkit - Remove redundant methods in nms loader 2023-01-05 16:48:16 +01:00
NEZNAMY 009283a83f Update README.md 2023-01-05 16:04:38 +01:00
Callum Seabrook 4a430b97df Merge pull request #779 from JT122406/master
Updates
2023-01-04 22:50:08 +00:00
NEZNAMY 722333da2e Fix RejectedExecutionException (#769) 2023-01-04 19:15:56 +01:00
NEZNAMY 1215af5262 Bukkit - Add 1.5.0 support 2023-01-04 18:28:24 +01:00
NEZNAMY 254088c37b Bukkit - Repackage nms loaders (also fix startup on Thermos) 2023-01-04 18:14:39 +01:00
J.T. McQuiggandCallum Seabrook cb2ce4acca Update gradle/libs.versions.toml
Co-authored-by: Callum Seabrook <callum.seabrook@prevarinite.com>
2023-01-04 11:20:55 -05:00
J.T.McQuigg 872adca8fd Updates 2023-01-03 23:09:35 -05:00
J.T.McQuigg f37cc0bdd8 Gradle 7.6 2023-01-03 23:02:04 -05:00
NEZNAMY 4063578ac3 Code style - Remove duplicate .toLowerCase() 2023-01-02 20:45:23 +01:00
NEZNAMY 2528de7eeb Code style - Use built-in String#trim function 2023-01-02 20:25:11 +01:00
NEZNAMY 37caed3123 Placeholders - Add timeout for placeholders to return value, causing TAB to freeze 2022-12-23 14:49:31 +01:00
NEZNAMY 27fe9dfb12 Commands - Make player command update player instantly if uuid is used instead of player name 2022-12-21 22:00:58 +01:00
NEZNAMY 0169cbd4d2 Bukkit - Fix wrong method taken sometimes, causing player kick with unlimited nametag mode 2022-12-20 15:08:18 +01:00
NEZNAMY ed760aec08 Aligned PlayerList - Fix rare error that makes no sense 2022-12-20 15:04:48 +01:00
NEZNAMY 858cbcefeb Code style - Add toString override 2022-12-20 15:04:21 +01:00
NEZNAMY 8a87d581a9 Scoreboard - Do not send packets to players who cannot see that specific scoreboard 2022-12-18 17:16:26 +01:00
NEZNAMY 6d642f4e51 NameTags - Fix players getting unregistered with wrong team name 2022-12-17 19:12:00 +01:00
NEZNAMY aca63640bc Attempt to fix rare NPE 2022-12-17 19:08:32 +01:00
NEZNAMY 1845a35315 3.2.2-SNAPSHOT 2022-12-16 22:25:39 +01:00
NEZNAMY cab5f89a95 Remove redundant check 2022-12-16 22:03:21 +01:00
NEZNAMY 1777d8bf01 Remove redundant try/catch 2022-12-16 21:14:47 +01:00
NEZNAMY 91b0bfaa9d Configuration - Simplify file copying, since default files already are encoded with UTF-8 encoding 2022-12-16 15:04:52 +01:00
NEZNAMY f1cf994d10 3.2.1 2022-12-16 11:27:48 +01:00
NEZNAMY 69957be933 Pet name fix - Fix excessive CPU usage caused by heavy operation and not caching the result 2022-12-15 00:22:23 +01:00
NEZNAMY b78eb5efb6 Bukkit - Remove forgotten debug stack trace printing 2022-12-15 00:21:28 +01:00
NEZNAMY 3577ad1964 Bukkit - Fix NPE in packet creation on <1.13 (#763) 2022-12-14 18:02:04 +01:00
NEZNAMY 4510491177 3.2.0 2022-12-12 09:46:21 +01:00
NEZNAMY 01b3a12377 1.19.3 support 2022-12-12 09:46:12 +01:00
NEZNAMY 64ae9863fb Bukkit - Drop old event API system 2022-12-10 12:27:51 +01:00
NEZNAMY ecd315a407 Sorting - Fix error from previous commit (.toLowerCase made string longer) 2022-12-08 11:20:58 +01:00
NEZNAMY 678eed288e Conditions - Add OR type for anonymous conditions using "|" symbol 2022-12-06 10:32:41 +01:00
NEZNAMY 9510455cb1 Conditions - Fix anonymous conditions not updating from previous commit 2022-12-06 10:31:29 +01:00
NEZNAMY e925ce23d0 Conditions - Fix nested conditions not working if they are defined below the condition using them 2022-12-06 09:51:16 +01:00
NEZNAMY c68cad6a1e Global PlayerList - Fix feature breaking nick/skin changes (minecraft mechanic changed in 1.19) (#752) 2022-12-06 09:50:00 +01:00
NEZNAMY 01d6eda4fb Configuration - Use luckperms placeholders instead of vault in default configuration (and make them return empty value if LP is not found) 2022-12-04 14:19:21 +01:00
NEZNAMY 73e7119bbc Configuration - add hidden server-name option for %server% value (useful with MySQL) 2022-12-04 11:49:59 +01:00
NEZNAMY cea781382a Sorting - Fix wrong team name being unregistered (#727) 2022-12-03 21:18:38 +01:00
NEZNAMY cf25b84e86 Commands - Fix debug command showing sorting is disabled even if layout is enabled for sorting 2022-12-03 21:16:29 +01:00
NEZNAMY 86e1d22390 Sorting - Only use 1 character for sorting by groups, permissions and predefined values; Improve debug output of sorting results 2022-12-03 12:16:38 +01:00
NEZNAMY 1625be111f Global PlayerList - Make server names in server groups and spy-servers not case-sensitive 2022-12-02 23:18:40 +01:00
NEZNAMY 00cda3b2aa Placeholders - Load all used placeholders on join in placeholder manager to make cpu usage appear under that "feature" instead of a feature actually using the placeholders 2022-12-01 22:46:40 +01:00
NEZNAMY 85a00a4436 Code style - Extract minimum placeholder refresh interval into an editable constant 2022-12-01 22:36:33 +01:00
NEZNAMY 37aec1a497 Global PlayerList - Add %playerlist-group_<group>% placeholder for online count in each group 2022-12-01 22:32:43 +01:00
NEZNAMY ef637616ee Placeholders - Improve error message 2022-11-27 17:13:24 +01:00
NEZNAMY af3b5067fb BossBar - Ignore players in disabled worlds/servers in announce command (#748) 2022-11-25 11:23:48 +01:00
NEZNAMY 53e5b15f96 Tab Expansion - Fix error in nested placeholder parsing 2022-11-24 11:01:55 +01:00
NEZNAMY 6a3afae607 Implement empty method 2022-11-16 12:24:03 +01:00
NEZNAMY 2e05c7d9d1 Another attempt for #727 2022-11-15 19:15:49 +01:00
NEZNAMY fb698a04b7 Configuration - Merge default config.yml files into one 2022-11-12 19:11:36 +01:00
NEZNAMY 52bdf30ddb Merge remote-tracking branch 'origin/master' 2022-11-09 22:34:22 +01:00
NEZNAMY a92f07aa40 Tab Expansion - Resend values to bridge on server switch (#740) 2022-11-09 22:34:09 +01:00
NEZNAMY 003ff7f99d Merge pull request #739 from CoachYT1/patch-1
bump org.yaml:snakeyaml to 1.33
2022-11-09 21:59:08 +01:00
NEZNAMY 47b9806ae7 Tab Expansion - Parse nested placeholders in replace function 2022-11-07 22:26:17 +01:00
CoachYT d2a7c65e08 bump org.yaml:snakeyaml to 1.33
snakeyaml 1.30 is affected by 5 vulnerabilities and should be upgraded to the latest version
2022-11-07 17:53:24 +01:00
NEZNAMY e232336a5f Add check for modifying non-existing teams (#727) 2022-11-06 22:42:23 +01:00
NEZNAMY 8816c75212 BungeeCord - Fix crash on 1.7 caused by unregistering non-existing objectives (login packet cleared them) (#727) 2022-11-04 13:31:34 +01:00
NEZNAMY f9d54751ba Fix rare ConcurrentModificationException 2022-11-04 12:53:00 +01:00
NEZNAMY 43d41ee761 BungeeCord - Improve unsupported BungeeCord build message 2022-11-01 17:55:43 +01:00
NEZNAMY 4512ba8d5e NameTags - Print a console warn if both unlimited nametags and invisible nametags are enabled 2022-10-30 17:56:00 +01:00
NEZNAMY 8b55bca564 Remove redundant variable 2022-10-30 13:40:32 +01:00
NEZNAMY d8d54970d3 Code style - Clean up vanish check code 2022-10-30 13:29:45 +01:00
NEZNAMY 15ea4482f9 Bukkit - Drop direct Essentials hook for vanishing as it supports metadata 2022-10-30 13:27:24 +01:00
NEZNAMY 87f9f38eac Update issue templates 2022-10-29 18:35:11 +02:00
NEZNAMY dba559f041 Layout - Fix startup error caused by invalid number as slot 2022-10-29 12:17:04 +02:00
NEZNAMY c610b6f323 PlayerList - Fix name applying on server switch when pipeline injection is disabled 2022-10-26 17:36:08 +02:00
NEZNAMY a5fb900e19 API - Add back TabPlayer#getTeamName() 2022-10-24 14:05:39 +02:00
NEZNAMY 09d6e455f5 Layout - Implement a fix for compatibility with plugins which add empty player name into their 2022-10-21 19:55:59 +02:00
NEZNAMY e68097b0f4 Layout - Fix startup error if slots is a single number instead of interval 2022-10-16 15:32:26 +02:00
NEZNAMY 0744fed953 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2022-10-16 14:40:42 +02:00
NEZNAMY de0adf4067 MySQL - Attempt to fix startup error (#717) 2022-10-16 13:12:54 +02:00
NEZNAMY 76bf2b3efc Update links in README 2022-10-16 13:11:56 +02:00
NEZNAMY c089badeb4 Configuration - Remove owner and admin settings from default configuration 2022-10-15 19:15:38 +02:00
NEZNAMY b3b99d366a Placeholders - Fix relational placeholders updating causing normal placeholders to not refresh 2022-10-14 19:28:05 +02:00
NEZNAMY 4a36486f65 Bukkit - Fix startup on 1.8 2022-10-08 12:41:05 +02:00
NEZNAMY 75126d00d9 BossBar - Fix countdown placeholder not activating on usage in PlaceholderAPI 2022-10-06 16:43:06 +02:00
NEZNAMY 17ebf95d5b BossBar - Fix announcement bars disappearing on refresh 2022-10-06 16:42:32 +02:00
NEZNAMY c764420cfe Code Style - Simplify field value assignment 2022-10-06 15:33:50 +02:00
Callum Seabrook 28f05fc2af Changed publishing artifact IDs to begin with "tab-" 2022-10-06 12:02:28 +01:00
Callum Seabrook a420892887 Fixed publishing ID 2022-10-06 11:54:06 +01:00
NEZNAMY d910dc5913 Header / Footer - Prioritize world over server in config.yml as well 2022-10-06 11:11:00 +02:00
NEZNAMY c9c1cbc349 Layout - Fix anti-override for playerlist names being disabled even when no layout is visible 2022-09-26 11:02:14 +02:00
103 changed files with 1427 additions and 1543 deletions

No files matched your search

+5 -3
View File
@@ -1,6 +1,6 @@
name: Bug Report
description: Unwanted behavior that does not match configuration.
labels: ["Bug"]
description: Plugin does not work as described.
labels: ["Type: Bug"]
body:
- type: input
attributes:
@@ -45,7 +45,7 @@ body:
- type: textarea
attributes:
label: Additional info
description: Any other info you want to provide, such as screenshots.
description: Anything else you want to provide that will help in resolving the issue faster, such as screenshots or videos.
validations:
required: false
- type: checkboxes
@@ -57,5 +57,7 @@ body:
required: true
- label: I have read the wiki to make sure it's not an issue with configuration
required: true
- label: I ticked all of the boxes without actually reading them
required: false
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
required: true
+3 -1
View File
@@ -1,6 +1,6 @@
name: Error
description: Error message with a stack trace in console or error file.
labels: ["Error"]
labels: ["Type: Error"]
body:
- type: input
attributes:
@@ -51,5 +51,7 @@ body:
required: true
- label: I have included a paste of the error
required: true
- label: I ticked all of the boxes without actually reading them
required: false
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
required: true
+1 -1
View File
@@ -1,6 +1,6 @@
name: Wiki change
description: Change to text on a wiki page or adding a new section.
labels: ["Wiki"]
labels: ["Type: Wiki change"]
body:
- type: input
attributes:
+5 -5
View File
@@ -11,22 +11,22 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3.3.0 #https://github.com/actions/checkout/releases
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 11
uses: actions/setup-java@v3
uses: actions/setup-java@v3.9.0 #https://github.com/actions/setup-java/releases
with:
distribution: 'temurin'
java-version: 11
- name: Cache SonarCloud packages
uses: actions/cache@v2
uses: actions/cache@v3.2.3 #https://github.com/actions/cache/releases
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Gradle
uses: actions/cache@v2
uses: actions/cache@v3.2.3 #https://github.com/actions/cache/releases
with:
path: |
~/.gradle/caches
@@ -39,7 +39,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
run: ./gradlew clean build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2 #https://github.com/actions/upload-artifact/releases
with:
name: Jar
path: jar/build/libs/TAB-**.jar
+12 -16
View File
@@ -6,8 +6,8 @@ More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/
# Distribution
As of TAB 3.0.0, released with MC 1.18 (November 30th, 2021), there is no free / premium
version anymore. Only one version of the plugin exists now, which contains all implemented features
and is available to everyone for free. However, official support on discord is only provided to
those who have purchased the plugin.
and is available to everyone for free. However, customer support is only provided to
customers who have purchased the plugin.
# Compiling
Compilation requires JDK 8 and up.
@@ -15,17 +15,10 @@ To compile the plugin, run `./gradlew build` from the terminal.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
# Links
The precompiled JAR can be downloaded for free from:
* GitHub releases: https://github.com/NEZNAMY/TAB/releases
* MC-Market: https://www.mc-market.org/resources/20631/
Supporting my work:
* MC-Market: https://www.mc-market.org/resources/14009/
* Polymart: https://polymart.org/resource/484
Other links:
* Discord support for buyers: https://discord.gg/EaSvdk6
* Wiki: https://github.com/NEZNAMY/TAB/wiki
Download the plugin for free: [GitHub releases](https://github.com/NEZNAMY/TAB/releases),
[BuiltByBit](https://builtbybit.com/resources/20631/), [Modrinth](https://modrinth.com/plugin/tab-was-taken)
Support my work & receive customer support: [BuiltByBit](https://builtbybit.com/resources/14009/), [Polymart](https://polymart.org/resource/484)
Other links: [Discord support for customers](https://discord.gg/EaSvdk6), [Wiki](https://github.com/NEZNAMY/TAB/wiki)
# Issues
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is **only** available for cases included in templates.
@@ -33,6 +26,9 @@ In other words, they **are not available for**:
* **Support** - support is provided on discord for buyers, where roles are given to verified users
allowing us to know who bought it and who didn't. It also serves as a much faster way of communication.
* **Feature requests** - closed indefinitely.
**If you're a customer, please use discord for reporting bugs, especially if you aren't good at making bug reports.
You will be provided with a much faster and higher quality service.**
# Contributing
If this plugin has helped you in any way, and you would like to return something back
@@ -40,8 +36,8 @@ to make the plugin even better, there is a lot of ways to contribute:
* Open a **pull request** containing a new feature or a bug fix, which you believe many users will benefit from.
* Make detailed high-quality **bug reports**. The difference between a bug getting fixed in 1 week vs 1 hour
is in quality of the report (typically providing correct steps to reproduce that actually work).
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) or in a form of pre-releases on discord
for problems of any kind including bugs. This way you will help make the actual releases more stable / help get stable releases sooner.
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) for problems of any kind including bugs.
This way you will help make the actual releases more stable / help get stable releases sooner.
* Help improve the wiki by opening a [Wiki change issue](https://github.com/NEZNAMY/TAB/issues/new?assignees=&labels=Wiki&template=wiki_change.yml),
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples of usage of functions.
* [Buy a copy](https://www.mc-market.org/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
* [Buy a copy](https://builtbybit.com/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
@@ -8,6 +8,7 @@ public enum ProtocolVersion {
UNKNOWN_SERVER_VERSION ("Unknown"),
UNKNOWN_CLIENT_VERSION ("Unknown"),
PROXY ("Proxy"),
V1_19_3 (761),
V1_19_2 (760),
V1_19_1 (760),
V1_19 (759),
@@ -83,6 +83,8 @@ public class TabConstants {
public static final String NAMETAGS_TEAM_UNREGISTER = "NameTags (Team unregister)";
public static final String NAMETAGS_TEAM_UPDATE = "NameTags (Team update)";
public static final String SORTING_CHANGING_TEAM_NAME = "Sorting (Changing team name)";
public static final String SCOREBOARD_TITLE = "Scoreboard (Title)";
public static final String SCOREBOARD_LINES = "Scoreboard (Lines)";
@@ -333,6 +335,9 @@ public class TabConstants {
public static final String VAULT_SUFFIX = "%vault-suffix%";
public static final String HEALTH = "%health%";
public static final int MINIMUM_REFRESH_INTERVAL = 50;
public static String condition(String name) {
return "%condition:" + name + "%";
}
@@ -340,6 +345,10 @@ public class TabConstants {
public static String animation(String name) {
return "%animation:" + name + "%";
}
public static String globalPlayerListGroup(String group) {
return "%playerlist-group_" + group + "%";
}
}
/**
@@ -212,13 +212,6 @@ public interface TabPlayer {
*/
String getTeamName();
/**
* Returns user-friendly explanation of team name
*
* @return explanation behind team name
*/
String getTeamNameNote();
/**
* Returns {@code true} if player is disguised using LibsDisguises, {@code false} if not
*
@@ -318,4 +311,10 @@ public interface TabPlayer {
* @return Player's direct chat message signing key
*/
Object getProfilePublicKey();
/**
* Return player's chat session ID
* @return player's chat session ID
*/
UUID getChatSessionId();
}
@@ -39,15 +39,15 @@ public class RGBUtils {
*/
public RGBUtils() {
List<RGBFormatter> list = new ArrayList<>();
try {
Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
list.add(new MiniMessageFormat());
} catch (ClassNotFoundException ignored) {}
list.add(new BukkitFormat());
list.add(new CMIFormat());
list.add(new UnnamedFormat1());
list.add(new HtmlFormat());
list.add(new KyoriFormat());
try {
Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
list.add(new MiniMessageFormat());
} catch (ClassNotFoundException ignored) {}
formats = list.toArray(new RGBFormatter[0]);
gradients = new GradientPattern[] {
@@ -1,20 +1,18 @@
package me.neznamy.tab.api.chat.rgb.format;
import me.neznamy.tab.api.chat.EnumChatFormat;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
/**
* Formatter for &lt;#RRGGBB&gt;
* Call to MiniMessage API to reformat text to &amp;x&amp;R&amp;R&amp;G&amp;G&amp;B&amp;B
*/
public class MiniMessageFormat implements RGBFormatter {
@Override
public String reformat(String text) {
if (!text.contains("<")) return text;
String format = text.replace(EnumChatFormat.RESET.getFormat(), ""); //remove &r from header/footer newline inserts
try {
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(format));
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(text));
} catch (Throwable ignored) {
return text;
}
@@ -1,14 +1,11 @@
package me.neznamy.tab.api.config;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -16,9 +13,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
/**
* Abstract class for configuration file
*/
@@ -53,18 +47,9 @@ public abstract class ConfigurationFile {
Preconditions.checkNotNull(destination, "destination");
this.file = destination;
if (file.getParentFile() != null && !file.getParentFile().exists()) Files.createDirectories(file.getParentFile().toPath());
if (!file.exists() && source == null) throw new IllegalStateException("File does not exist and source is null");
if (file.createNewFile()) {
// Files.copy(source, file.toPath());
//avoiding MalformedInputException thrown on fabric due to file not having UTF8 encoding by default
//also automatically using the encoding so users don't have to worry about it anymore
try (BufferedReader reader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))){
String line;
while ((line = reader.readLine()) != null) {
writer.write(line + "\n");
}
}
if (!file.exists()) {
if (source == null) throw new IllegalStateException("File does not exist and source is null");
Files.copy(source, file.toPath());
}
detectHeader();
}
@@ -1,6 +1,7 @@
package me.neznamy.tab.api.protocol;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
@@ -12,8 +13,8 @@ import me.neznamy.tab.api.util.Preconditions;
*/
public class PacketPlayOutPlayerInfo implements TabPacket {
/** Packet action */
private final EnumPlayerInfoAction action;
/** Packet actions */
private final EnumSet<EnumPlayerInfoAction> actions;
/** List of affected entries */
private final List<PlayerInfoData> entries;
@@ -27,10 +28,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, PlayerInfoData... entries) {
Preconditions.checkNotNull(action, "action");
Preconditions.checkNotNull(entries, "entries");
this.action = action;
this.entries = Arrays.asList(entries);
this(action, Arrays.asList(entries));
}
/**
@@ -44,22 +42,61 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
Preconditions.checkNotNull(action, "action");
Preconditions.checkNotNull(entries, "entries");
this.action = action;
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
actions = EnumSet.of(EnumPlayerInfoAction.ADD_PLAYER,
EnumPlayerInfoAction.INITIALIZE_CHAT,
EnumPlayerInfoAction.UPDATE_GAME_MODE,
EnumPlayerInfoAction.UPDATE_LISTED,
EnumPlayerInfoAction.UPDATE_LATENCY,
EnumPlayerInfoAction.UPDATE_DISPLAY_NAME);
} else {
this.actions = EnumSet.of(action);
}
this.entries = entries;
}
/**
* Constructs new instance with given parameters.
*
* @param actions
* Packet actions
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, PlayerInfoData... entries) {
Preconditions.checkNotNull(actions, "action");
Preconditions.checkNotNull(entries, "entries");
this.actions = actions;
this.entries = Arrays.asList(entries);
}
/**
* Constructs new instance with given parameters.
*
* @param actions
* Packet actions
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, List<PlayerInfoData> entries) {
Preconditions.checkNotNull(actions, "action");
Preconditions.checkNotNull(entries, "entries");
this.actions = actions;
this.entries = entries;
}
@Override
public String toString() {
return String.format("PacketPlayOutPlayerInfo{action=%s,entries=%s}", action, entries);
return String.format("PacketPlayOutPlayerInfo{actions=%s,entries=%s}", actions, entries);
}
/**
* Returns {@link #action}
* Returns {@link #actions}
*
* @return packet action
*/
public EnumPlayerInfoAction getAction() {
return action;
public EnumSet<EnumPlayerInfoAction> getActions() {
return actions;
}
/**
@@ -79,6 +116,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/** Latency */
private int latency;
/** I have no idea what this is */
private boolean listed;
/** GameMode */
private EnumGamemode gameMode = EnumGamemode.SURVIVAL; //ProtocolLib causes NPE even when action does not use GameMode
@@ -97,12 +137,15 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/** Player's skin, null for empty skin */
private Skin skin;
/** Chat session ID */
private UUID chatSessionId;
/** Player's chat signing key */
private Object profilePublicKey;
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.ADD_PLAYER action
* {@link EnumPlayerInfoAction#ADD_PLAYER} action
*
* @param name
* Player's name
@@ -119,20 +162,22 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* @param profilePublicKey
* Player's chat signing key
*/
public PlayerInfoData(String name, UUID uniqueId, Skin skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, Object profilePublicKey) {
public PlayerInfoData(String name, UUID uniqueId, Skin skin, boolean listed, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, UUID chatSessionId, Object profilePublicKey) {
Preconditions.checkNotNull(uniqueId, "uuid");
this.name = name;
this.uniqueId = uniqueId;
this.skin = skin;
this.listed = listed;
this.latency = latency;
this.gameMode = gameMode;
this.displayName = displayName;
this.chatSessionId = chatSessionId;
this.profilePublicKey = profilePublicKey;
}
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_GAME_MODE action
* {@link EnumPlayerInfoAction#UPDATE_GAME_MODE} action
*
* @param uniqueId
* Player's uuid
@@ -147,7 +192,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_LATENCY action
* {@link EnumPlayerInfoAction#UPDATE_LATENCY} action
*
* @param uniqueId
* Player's uuid
@@ -162,7 +207,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_DISPLAY_NAME action
* {@link EnumPlayerInfoAction#UPDATE_DISPLAY_NAME} action
*
* @param uniqueId
* Player's uuid
@@ -177,7 +222,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Constructs new instance with given parameter. Suitable for
* {@link EnumPlayerInfoAction}.REMOVE_PLAYER action
* {@link EnumPlayerInfoAction#REMOVE_PLAYER} action
*
* @param uniqueId
* Player's uuid
@@ -211,6 +256,25 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
this.latency = latency;
}
/**
* Returns {@link #listed}
*
* @return listed
*/
public boolean isListed() {
return listed;
}
/**
* Sets {@link #listed} to specified value
*
* @param listed
* Listed flag
*/
public void setListed(boolean listed) {
this.listed = listed;
}
/**
* Returns {@link #displayName}
*
@@ -306,6 +370,25 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
this.skin = skin;
}
/**
* Returns {@link #chatSessionId}
*
* @return chatSessionId
*/
public UUID getChatSessionId() {
return chatSessionId;
}
/**
* Sets {@link #chatSessionId} to specified value
*
* @param chatSessionId
* chatSessionId to use
*/
public void setChatSessionId(UUID chatSessionId) {
this.chatSessionId = chatSessionId;
}
/**
* Returns {@link #profilePublicKey}
*
@@ -328,12 +411,13 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* En enum representing packet action
* Calling ordinal() will return action's network ID.
*/
public enum EnumPlayerInfoAction {
ADD_PLAYER,
INITIALIZE_CHAT,
UPDATE_GAME_MODE,
UPDATE_LISTED,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
REMOVE_PLAYER
@@ -0,0 +1,12 @@
package me.neznamy.tab.api.util;
/**
* Supplier that can throw an exception
*
* @param <T>
* Return class type
*/
public interface SupplierWithException<T> {
T get() throws Exception;
}
@@ -4,6 +4,9 @@ plugins {
}
indra {
publishReleasesTo("krypton-repo", "https://repo.kryptonmc.org/releases")
publishSnapshotsTo("krypton-repo", "https://repo.kryptonmc.org/snapshots")
publishReleasesTo("krypton", "https://repo.kryptonmc.org/releases")
publishSnapshotsTo("krypton", "https://repo.kryptonmc.org/snapshots")
configurePublications {
artifactId = "tab-$artifactId"
}
}
@@ -12,13 +12,18 @@ import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
import me.neznamy.tab.platforms.bukkit.nms.*;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.Collectors;
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitPacketBuilder extends PacketBuilder {
@@ -76,9 +81,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
return nms.newPacketPlayOutChat.newInstance(component, packet.getType().ordinal());
}
if (nms.getMinorVersion() >= 16)
return nms.newPacketPlayOutChat.newInstance(component, nms.ChatMessageType_values[packet.getType().ordinal()], UUID.randomUUID());
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.getType().toString()), UUID.randomUUID());
if (nms.getMinorVersion() >= 12)
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf((Class<Enum>) nms.ChatMessageType, packet.getType().toString()));
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.getType().toString()));
if (nms.getMinorVersion() >= 8)
return nms.newPacketPlayOutChat.newInstance(component, (byte) packet.getType().ordinal());
if (nms.getMinorVersion() == 7)
@@ -89,24 +94,55 @@ public class BukkitPacketBuilder extends PacketBuilder {
@Override
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() < 8) return null;
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(nms.EnumPlayerInfoAction_values[packet.getAction().ordinal()], Array.newInstance(nms.EntityPlayer, 0));
List<Object> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
if (data.getSkin() != null) profile.getProperties().put("textures", new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
List<Object> parameters = new ArrayList<>();
if (nms.newPlayerInfoData.getParameterTypes()[0] == nms.PacketPlayOutPlayerInfo) {
parameters.add(nmsPacket);
if (nms.ClientboundPlayerInfoRemovePacket != null) {
//1.19.3+
if (packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
return nms.newClientboundPlayerInfoRemovePacket.newInstance(
packet.getEntries().stream().map(PlayerInfoData::getUniqueId).collect(Collectors.toList())
);
}
parameters.add(profile);
parameters.add(data.getLatency());
parameters.add(data.getGameMode() == null ? null : nms.EnumGamemode_values[nms.EnumGamemode_values.length-EnumGamemode.VALUES.length+data.getGameMode().ordinal()]); //not_set was removed in 1.17
parameters.add(data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion));
if (nms.getMinorVersion() >= 19) parameters.add(data.getProfilePublicKey());
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
Enum[] array = packet.getActions().stream().map(action -> Enum.valueOf(nms.EnumPlayerInfoAction, action.toString())).toArray(Enum[]::new);
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(EnumSet.of(array[0], array), Collections.emptyList());
List<Object> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
if (data.getSkin() != null) profile.getProperties().put("textures",
new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
Object obj = nms.newPlayerInfoData.newInstance(
data.getUniqueId(),
profile,
data.isListed(),
data.getLatency(),
data.getGameMode() == null ? null : Enum.valueOf(nms.EnumGamemode, data.getGameMode().toString()),
data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion),
data.getProfilePublicKey() == null ? null : nms.newRemoteChatSession$Data.newInstance(data.getChatSessionId(), data.getProfilePublicKey()));
items.add(obj);
}
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
return nmsPacket;
} else {
//1.19.2-
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(Enum.valueOf(nms.EnumPlayerInfoAction, action.toString()), Array.newInstance(nms.EntityPlayer, 0));
List<Object> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
if (data.getSkin() != null) profile.getProperties().put("textures", new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
List<Object> parameters = new ArrayList<>();
if (nms.newPlayerInfoData.getParameterTypes()[0] == nms.PacketPlayOutPlayerInfo) {
parameters.add(nmsPacket);
}
parameters.add(profile);
parameters.add(data.getLatency());
parameters.add(data.getGameMode() == null ? null : Enum.valueOf(nms.EnumGamemode, data.getGameMode().toString()));
parameters.add(data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion));
if (nms.getMinorVersion() >= 19) parameters.add(data.getProfilePublicKey());
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
}
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
return nmsPacket;
}
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
return nmsPacket;
}
@Override
@@ -132,7 +168,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (nms.getMinorVersion() >= 13) {
return nms.newPacketPlayOutScoreboardObjective.newInstance(nms.newScoreboardObjective.newInstance(null, packet.getObjectiveName(), null,
toNMSComponent(IChatBaseComponent.optimizedComponent(displayName), clientVersion),
packet.getRenderType() == null ? null : nms.EnumScoreboardHealthDisplay_values[packet.getRenderType().ordinal()]),
packet.getRenderType() == null ? null : Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString())),
packet.getAction());
}
@@ -140,7 +176,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME, packet.getObjectiveName());
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_DISPLAYNAME, displayName);
if (nms.getMinorVersion() >= 8 && packet.getRenderType() != null) {
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_RENDERTYPE, Enum.valueOf((Class<Enum>) nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString()));
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_RENDERTYPE, Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString()));
}
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_METHOD, packet.getAction());
return nmsPacket;
@@ -149,7 +185,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
@Override
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() >= 13) {
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(nms.EnumScoreboardAction_values[packet.getAction().ordinal()], packet.getObjectiveName(), packet.getPlayer(), packet.getScore());
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(Enum.valueOf(nms.EnumScoreboardAction, packet.getAction().toString()), packet.getObjectiveName(), packet.getPlayer(), packet.getScore());
}
if (packet.getAction() == PacketPlayOutScoreboardScore.Action.REMOVE) {
return nms.newPacketPlayOutScoreboardScore_String.newInstance(packet.getPlayer());
@@ -189,9 +225,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
case 2:
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, false);
case 3:
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().toArray(new String[0])[0], nms.PacketPlayOutScoreboardTeam_PlayerAction_values[0]);
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().iterator().next(), Enum.valueOf(nms.PacketPlayOutScoreboardTeam_PlayerAction, "ADD"));
case 4:
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().toArray(new String[0])[0], nms.PacketPlayOutScoreboardTeam_PlayerAction_values[1]);
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().iterator().next(), Enum.valueOf(nms.PacketPlayOutScoreboardTeam_PlayerAction, "REMOVE"));
default:
throw new IllegalArgumentException("Invalid action: " + packet.getAction());
}
@@ -227,7 +263,12 @@ public class BukkitPacketBuilder extends PacketBuilder {
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityMetadata packet) throws ReflectiveOperationException {
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
if (nms.newPacketPlayOutEntityMetadata.getParameterCount() == 2) {
//1.19.3+
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), nms.DataWatcher_b.invoke(packet.getDataWatcher().toNMS()));
} else {
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
}
}
/**
@@ -264,7 +305,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
int id = entityIds.get(packet.getEntityType());
if (nms.getMinorVersion() >= 19) {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, nms.Registry_a.invoke(nms.IRegistry_X, id));
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, nms.EntityTypes_ARMOR_STAND); // :(
} else {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, id);
}
@@ -322,9 +363,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(prefix), clientVersion));
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(suffix), clientVersion));
EnumChatFormat format = packet.getColor() != null ? packet.getColor() : EnumChatFormat.lastColorsOf(prefix);
nms.ScoreboardTeam_setColor.invoke(team, nms.EnumChatFormat_values[format.ordinal()]);
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNameTagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
nms.ScoreboardTeam_setColor.invoke(team, Enum.valueOf(nms.EnumChatFormat, format.toString()));
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(nms.EnumNameTagVisibility, String.valueOf(packet.getNameTagVisibility()).equals("always") ? "ALWAYS" : "NEVER"));
nms.ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(nms.EnumTeamPush, String.valueOf(packet.getCollisionRule()).equals("always") ? "ALWAYS" : "NEVER"));
}
/**
@@ -344,31 +385,71 @@ public class BukkitPacketBuilder extends PacketBuilder {
private void createTeamLegacy(PacketPlayOutScoreboardTeam packet, Object team, String prefix, String suffix) throws ReflectiveOperationException {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, prefix);
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, suffix);
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNameTagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
if (nms.getMinorVersion() >= 9) nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(nms.EnumNameTagVisibility, String.valueOf(packet.getNameTagVisibility()).equals("always") ? "ALWAYS" : "NEVER"));
if (nms.getMinorVersion() >= 9) nms.ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(nms.EnumTeamPush, String.valueOf(packet.getCollisionRule()).equals("always") ? "ALWAYS" : "NEVER"));
}
@Override
public PacketPlayOutPlayerInfo readPlayerInfo(Object nmsPacket, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() < 8) return null;
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
List<PlayerInfoData> listData = new ArrayList<>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
IChatBaseComponent listName = nmsComponent == null ? null : new WrappedChatComponent(nmsComponent);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = profile.getProperties().get("textures").iterator().next();
skin = new Skin(pr.getValue(), pr.getSignature());
if (nms.ClientboundPlayerInfoRemovePacket != null) {
//1.19.3+
if (nms.ClientboundPlayerInfoRemovePacket.isInstance(nmsPacket)) {
List<UUID> entries = (List<UUID>) nms.ClientboundPlayerInfoRemovePacket_getEntries.invoke(nmsPacket);
return new PacketPlayOutPlayerInfo(
EnumPlayerInfoAction.REMOVE_PLAYER,
entries.stream().map(PlayerInfoData::new).collect(Collectors.toList())
);
}
Object profilePublicKey = nms.getMinorVersion() >= 19 ? nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData) : null;
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), skin,
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName, profilePublicKey));
EnumSet<?> set = (EnumSet<?>) nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket);
EnumPlayerInfoAction[] array = set.stream().map(action -> EnumPlayerInfoAction.valueOf(action.toString())).toArray(EnumPlayerInfoAction[]::new);
EnumSet<EnumPlayerInfoAction> actions = EnumSet.of(array[0], array);
List<PlayerInfoData> listData = new ArrayList<>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = profile.getProperties().get("textures").iterator().next();
skin = new Skin(pr.getValue(), pr.getSignature());
}
Object remoteChatSession = nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData);
listData.add(
new PlayerInfoData(
profile.getName(),
profile.getId(),
skin,
(boolean) nms.PlayerInfoData_isListed.invoke(nmsData),
(int) nms.PlayerInfoData_getLatency.invoke(nmsData),
gameMode,
nmsComponent == null ? null : new WrappedChatComponent(nmsComponent),
remoteChatSession == null ? null : (UUID) nms.RemoteChatSession$Data_getSessionId.invoke(remoteChatSession),
remoteChatSession == null ? null : nms.RemoteChatSession$Data_getProfilePublicKey.invoke(remoteChatSession)));
}
return new PacketPlayOutPlayerInfo(actions, listData);
} else {
//1.19.2-
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
List<PlayerInfoData> listData = new ArrayList<>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
IChatBaseComponent listName = nmsComponent == null ? null : new WrappedChatComponent(nmsComponent);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = profile.getProperties().get("textures").iterator().next();
skin = new Skin(pr.getValue(), pr.getSignature());
}
Object profilePublicKey = nms.getMinorVersion() >= 19 ? nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData) : null;
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), skin, true,
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName, null, profilePublicKey));
}
return new PacketPlayOutPlayerInfo(action, listData);
}
return new PacketPlayOutPlayerInfo(action, listData);
}
@Override
@@ -9,10 +9,11 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PipelineInjector;
import me.neznamy.tab.shared.features.sorting.Sorting;
import org.jetbrains.annotations.NotNull;
/**
@@ -66,7 +67,8 @@ public class BukkitPipelineInjector extends PipelineInjector {
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
try {
if (nms.PacketPlayOutPlayerInfo.isInstance(packet)) {
if (nms.PacketPlayOutPlayerInfo.isInstance(packet) ||
(nms.ClientboundPlayerInfoRemovePacket != null && nms.ClientboundPlayerInfoRemovePacket.isInstance(packet))) {
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
return;
}
@@ -117,9 +119,10 @@ public class BukkitPipelineInjector extends PipelineInjector {
newList.add(entry);
continue;
}
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (!((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(p.getTeamName())) {
logTeamOverride(teamName, p.getName(), p.getTeamName());
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(sorting.getShortTeamName(p))) {
logTeamOverride(teamName, p.getName(), sorting.getShortTeamName(p));
} else {
newList.add(entry);
}
@@ -1,12 +1,11 @@
package me.neznamy.tab.platforms.bukkit;
import com.earth2me.essentials.Essentials;
import com.viaversion.viaversion.api.Via;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.platforms.bukkit.event.TabLoadEvent;
import me.neznamy.tab.platforms.bukkit.event.TabPlayerLoadEvent;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.platforms.bukkit.features.PetFix;
@@ -15,10 +14,10 @@ import me.neznamy.tab.platforms.bukkit.features.unlimitedtags.BukkitNameTagX;
import me.neznamy.tab.platforms.bukkit.permission.Vault;
import me.neznamy.tab.shared.Platform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.None;
import me.neznamy.tab.shared.permission.PermissionPlugin;
@@ -43,7 +42,6 @@ public class BukkitPlatform extends Platform {
/** Variables checking presence of other plugins to hook into */
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API);
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
private Plugin viaVersion;
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
@@ -88,6 +86,7 @@ public class BukkitPlatform extends Platform {
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.SORTING, new Sorting());
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && tab.getServerVersion().getMinorVersion() >= 8) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, new BukkitNameTagX(plugin));
} else {
@@ -182,16 +181,6 @@ public class BukkitPlatform extends Platform {
}
}
@Override
public void callLoadEvent() {
Bukkit.getPluginManager().callEvent(new TabLoadEvent());
}
@Override
public void callLoadEvent(TabPlayer player) {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> Bukkit.getPluginManager().callEvent(new TabPlayerLoadEvent(player)));
}
/**
* Returns status of LibsDisguises plugin presence
*
@@ -213,20 +202,6 @@ public class BukkitPlatform extends Platform {
libsDisguises = enabled;
}
/**
* Returns Essentials' main class if the plugin is installed, {@code null} if not
*
* @return Essentials instance or {@code null} if plugin is not installed
*/
public Essentials getEssentials() {
return (Essentials) essentials;
}
@Override
public String getConfigName() {
return "bukkitconfig.yml";
}
/**
* Gets protocol version of requested player and returns it.
*
@@ -298,6 +273,7 @@ public class BukkitPlatform extends Platform {
/**
* Sends console message using ConsoleCommandSender, due to
* Paper not translating colors correctly in Logger messages
* and to allow RGB (at least on Paper, doesn't work on spigot)
*
* @param message
* Message to send
@@ -306,6 +282,9 @@ public class BukkitPlatform extends Platform {
*/
@Override
public void sendConsoleMessage(String message, boolean translateColors) {
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ? EnumChatFormat.color(message) : message));
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ?
EnumChatFormat.color(RGBUtils.getInstance().convertToBukkitFormat(message,
TAB.getInstance().getServerVersion().getMinorVersion() >= 16))
: message));
}
}
@@ -16,7 +16,6 @@ import org.bukkit.entity.Player;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.potion.PotionEffectType;
import com.earth2me.essentials.Essentials;
import com.mojang.authlib.GameProfile;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
@@ -26,7 +25,7 @@ import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
import io.netty.channel.Channel;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
@@ -56,7 +55,7 @@ public class BukkitTabPlayer extends ITabPlayer {
* Player's protocol network id
*/
public BukkitTabPlayer(Player p, int protocolVersion){
super(p, p.getUniqueId(), p.getName(), "N/A", p.getWorld().getName(), protocolVersion, true);
super(p, p.getUniqueId(), p.getName(), TAB.getInstance().getConfiguration().getServerName(), p.getWorld().getName(), protocolVersion, true);
try {
handle = NMSStorage.getInstance().getHandle.invoke(player);
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
@@ -277,10 +276,7 @@ public class BukkitTabPlayer extends ITabPlayer {
@Override
public boolean isVanished() {
Essentials essentials = ((BukkitPlatform)TAB.getInstance().getPlatform()).getEssentials();
if (essentials != null && essentials.getUser(getUniqueId()).isVanished()) return true;
List<MetadataValue> metadata = getPlayer().getMetadata("vanished");
return !metadata.isEmpty() && metadata.get(0).asBoolean();
return getPlayer().getMetadata("vanished").stream().anyMatch(MetadataValue::asBoolean);
}
@SuppressWarnings("deprecation")
@@ -293,15 +289,36 @@ public class BukkitTabPlayer extends ITabPlayer {
public Object getProfilePublicKey() {
if (NMSStorage.getInstance().getMinorVersion() < 19) return null;
try {
Object key = NMSStorage.getInstance().EntityHuman_ProfilePublicKey.get(handle);
if (key == null) return null;
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
if (NMSStorage.getInstance().RemoteChatSession != null) {
//1.19.3+
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
if (session == null) return null;
Object key = NMSStorage.getInstance().RemoteChatSession_getProfilePublicKey.invoke(session);
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
} else {
Object key = NMSStorage.getInstance().EntityHuman_ProfilePublicKey.get(handle);
if (key == null) return null;
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
}
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get profile key of " + getName(), e);
return null;
}
}
@Override
public UUID getChatSessionId() {
if (NMSStorage.getInstance().RemoteChatSession != null) return null;
try {
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
if (session == null) return null;
return (UUID) NMSStorage.getInstance().RemoteChatSession_getSessionId.invoke(session);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get chat session of " + getName(), e);
return null;
}
}
@Override
public Channel getChannel() {
try {
@@ -1,25 +1,25 @@
package me.neznamy.tab.platforms.bukkit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.platforms.bukkit.nms.*;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.util.SupplierWithException;
import me.neznamy.tab.platforms.bukkit.nms.storage.*;
import me.neznamy.tab.shared.TAB;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Main class for Bukkit platform
*/
@@ -72,37 +72,42 @@ public class Main extends JavaPlugin {
"v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2", "v1_8_R3",
"v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1", "v1_13_R1", "v1_13_R2",
"v1_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1", "v1_18_R1",
"v1_18_R2", "v1_19_R1");
"v1_18_R2", "v1_19_R1", "v1_19_R2");
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
try {
long time = System.currentTimeMillis();
int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
if (minorVersion >= 17) {
try {
NMSStorage.setInstance(new BukkitModernNMSStorage());
} catch (ClassNotFoundException e) {
NMSStorage.setInstance(new MojangModernNMSStorage());
}
} else {
NMSStorage.setInstance(new LegacyNMSStorage());
}
NMSStorage.setInstance(getNMSLoader());
if (supportedVersions.contains(serverPackage)) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
return true;
} else {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on 1.5.x - 1.19.2. Disabling just to stay safe."));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on 1.5.2 - 1.19.3. Disabling just to stay safe."));
}
} catch (Exception ex) {
} catch (IllegalStateException ex) {
if (supportedVersions.contains(serverPackage)) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report the error below (include your server version & fork too)"));
getLogger().log(Level.SEVERE, "", ex);
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report this issue (include your server version & fork too)"));
} else {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.x - 1.19.2. Disabling."));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.2 - 1.19.3. Disabling."));
}
}
return false;
}
private NMSStorage getNMSLoader() {
List<SupplierWithException<NMSStorage>> loaders = new ArrayList<>();
loaders.add(BukkitLegacyNMSStorage::new);
loaders.add(BukkitModernNMSStorage::new);
loaders.add(MojangModernNMSStorage::new);
loaders.add(ThermosNMSStorage::new);
for (SupplierWithException<NMSStorage> loader : loaders) {
try {
return loader.get();
} catch (Exception ignored){}
}
throw new IllegalStateException("Unsupported server version");
}
/**
* Command handler for /tab command
*/
@@ -1,25 +0,0 @@
package me.neznamy.tab.platforms.bukkit.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Bukkit event that is called each time TAB fully loads. This includes server startup, reload,
* /tab reload and /plugman reload tab
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.plugin.TabLoadEvent}
*/
@Deprecated
public class TabLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@Override
public HandlerList getHandlers(){
return getHandlerList();
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -1,36 +0,0 @@
package me.neznamy.tab.platforms.bukkit.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import me.neznamy.tab.api.TabPlayer;
/**
* Bukkit event that is called when player is successfully loaded after joining. This also includes plugin reloading.
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.player.PlayerLoadEvent}
*/
@Deprecated
public class TabPlayerLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final TabPlayer player;
public TabPlayerLoadEvent(TabPlayer player) {
this.player = player;
}
@Override
public HandlerList getHandlers(){
return getHandlerList();
}
public static HandlerList getHandlerList() {
return handlers;
}
public TabPlayer getPlayer() {
return player;
}
}
@@ -6,6 +6,7 @@ import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.TabExpansion;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -45,9 +46,16 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
@Override
public String onPlaceholderRequest(Player player, @NotNull String identifier){
if (identifier.startsWith("replace_")) {
String placeholder = "%" + identifier.substring(8) + "%";
String output = PlaceholderAPI.setPlaceholders(player, placeholder);
return TabAPI.getInstance().getPlaceholderManager().findReplacement(placeholder, output);
String text = "%" + identifier.substring(8) + "%";
String textBefore;
do {
textBefore = text;
for (String placeholder : TAB.getInstance().getPlaceholderManager().detectPlaceholders(text)) {
text = text.replace(placeholder, TabAPI.getInstance().getPlaceholderManager().findReplacement(placeholder,
PlaceholderAPI.setPlaceholders(player, placeholder)));
}
} while (!textBefore.equals(text));
return text;
}
if (identifier.startsWith("placeholder_")) {
TabAPI.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", (TabFeature) TabAPI.getInstance().getPlaceholderManager());
@@ -25,9 +25,6 @@ import me.neznamy.tab.shared.TAB;
@SuppressWarnings("deprecation")
public class PerWorldPlayerList extends TabFeature implements Listener {
/** Reference to main class */
private final JavaPlugin plugin;
/** Config options */
private final boolean allowBypass = TabAPI.getInstance().getConfig().getBoolean("per-world-playerlist.allow-bypass-permission", false);
private final List<String> ignoredWorlds = TabAPI.getInstance().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
@@ -41,14 +38,13 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
*/
public PerWorldPlayerList(JavaPlugin plugin) {
super("Per world PlayerList", null);
this.plugin = plugin;
Bukkit.getPluginManager().registerEvents(this, plugin);
TabAPI.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
}
@Override
public void load(){
Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getOnlinePlayers().forEach(this::checkPlayer));
Bukkit.getOnlinePlayers().forEach(this::checkPlayer);
}
@Override
@@ -3,7 +3,7 @@ package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
@@ -125,8 +125,16 @@ public class PetFix extends TabFeature {
try {
for (Object item : items) {
if (item == null) continue;
if (nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item)) == petOwnerPosition) {
Object value = nms.DataWatcherItem_VALUE.get(item);
int slot;
Object value;
if (nms.is1_19_3Plus()) {
slot = nms.DataWatcher$DataValue_POSITION.getInt(item);
value = nms.DataWatcher$DataValue_VALUE.get(item);
} else {
slot = nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item));
value = nms.DataWatcherItem_VALUE.get(item);
}
if (slot == petOwnerPosition) {
if (value instanceof Optional || value instanceof com.google.common.base.Optional) {
removedEntry = item;
}
@@ -4,7 +4,7 @@ import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -55,7 +55,11 @@ public class VehicleRefresher extends TabFeature {
}
});
addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VEHICLE));
TabAPI.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
TabAPI.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100, p -> {
Entity v = ((Player)p.getPlayer()).getVehicle();
//There's a bug in Bukkit 1.19.3 throwing NPE on .toString(), use default toString implementation
return v == null ? "" : v.getClass().getName() + "@" + Integer.toHexString(v.hashCode());
});
}
@Override
@@ -1,42 +0,0 @@
package me.neznamy.tab.platforms.bukkit.nms;
import java.util.Arrays;
/**
* NMS loader for minecraft 1.17+ using Mojang packaging.
*/
public abstract class ModernNMSStorage extends NMSStorage {
/** Additional classes used in 1.19+ */
protected Class<?> IRegistry;
protected Class<?> Registry;
/**
* Creates new instance, initializes required NMS classes and fields
*
* @throws ReflectiveOperationException
* If any class, field or method fails to load
*/
public ModernNMSStorage() throws ReflectiveOperationException {
}
/**
* Returns class with given potential names in same order
*
* @param names
* possible class names
* @return class for specified names
* @throws ClassNotFoundException
* if class does not exist
*/
protected Class<?> getModernClass(String... names) throws ClassNotFoundException {
for (String name : names) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
//not the first class name in array
}
}
throw new ClassNotFoundException("No class found with possible names " + Arrays.toString(names));
}
}
@@ -5,7 +5,7 @@ import java.util.List;
import java.util.Map;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
/**
* A class representing the n.m.s.DataWatcher class to make work with it much easier
@@ -85,6 +85,7 @@ public class DataWatcher {
position = item.getType().getPosition();
}
nms.DataWatcher_REGISTER.invoke(nmsWatcher, position, item.getValue());
if (nms.is1_19_3Plus()) nms.DataWatcher_markDirty.invoke(nmsWatcher, position);
}
return nmsWatcher;
}
@@ -111,4 +112,9 @@ public class DataWatcher {
}
return watcher;
}
@Override
public String toString() {
return String.format("DataWatcher{values=%s}", dataValues);
}
}
@@ -4,7 +4,7 @@ import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.platforms.bukkit.BukkitPacketBuilder;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import me.neznamy.tab.shared.TAB;
import java.util.Optional;
@@ -2,7 +2,7 @@ package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
/**
* Class representing NMS Data Watcher Item
@@ -41,12 +41,15 @@ public class DataWatcherItem {
*/
public static DataWatcherItem fromNMS(Object nmsItem) throws ReflectiveOperationException {
NMSStorage nms = NMSStorage.getInstance();
Object value = nms.DataWatcherItem_VALUE.get(nmsItem);
DataWatcherObject object;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9) {
Object nmsObject = nms.DataWatcherItem_TYPE.get(nmsItem);
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherObject_SLOT.getInt(nmsObject), nms.DataWatcherObject_SERIALIZER.get(nmsObject)), nms.DataWatcherItem_VALUE.get(nmsItem));
object = new DataWatcherObject(nms.DataWatcherObject_SLOT.getInt(nmsObject), nms.DataWatcherObject_SERIALIZER.get(nmsObject));
} else {
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherItem_TYPE.getInt(nmsItem), null), nms.DataWatcherItem_VALUE.get(nmsItem));
object = new DataWatcherObject(nms.DataWatcherItem_TYPE.getInt(nmsItem), null);
}
return new DataWatcherItem(object, value);
}
/**
@@ -64,4 +67,9 @@ public class DataWatcherItem {
public Object getValue() {
return value;
}
@Override
public String toString() {
return String.format("DataWatcherItem{type=%s,value=%s}", type, value);
}
}
@@ -39,4 +39,9 @@ public class DataWatcherObject {
public Object getSerializer() {
return serializer;
}
@Override
public String toString() {
return String.format("DataWatcherObject{position=%d,serializer=%s}", position, serializer);
}
}
@@ -1,6 +1,6 @@
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import java.util.Map;
@@ -36,8 +36,13 @@ public class DataWatcherRegistry {
registryFloat = fields.get("c");
registryString = fields.get("d");
if (nms.getMinorVersion() >= 13) {
registryOptionalIChatBaseComponent = fields.get("f");
registryBoolean = fields.get("i");
if (nms.is1_19_3Plus()) {
registryOptionalIChatBaseComponent = fields.get("g");
registryBoolean = fields.get("j");
} else {
registryOptionalIChatBaseComponent = fields.get("f");
registryBoolean = fields.get("i");
}
} else {
registryBoolean = fields.get("h");
}
@@ -1,13 +1,12 @@
package me.neznamy.tab.platforms.bukkit.nms;
import me.neznamy.tab.platforms.bukkit.Main;
package me.neznamy.tab.platforms.bukkit.nms.storage;
import java.util.Arrays;
/**
* NMS loader for Minecraft 1.16.5 and lower using old NMS class structure.
* NMS loader for Minecraft 1.5.2 - 1.16.5 using Bukkit mapping.
*/
public class LegacyNMSStorage extends NMSStorage {
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitLegacyNMSStorage extends NMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
@@ -15,7 +14,7 @@ public class LegacyNMSStorage extends NMSStorage {
* @throws ReflectiveOperationException
* If any class, field or method fails to load
*/
public LegacyNMSStorage() throws ReflectiveOperationException {
public BukkitLegacyNMSStorage() throws ReflectiveOperationException {
}
/**
@@ -47,44 +46,33 @@ public class LegacyNMSStorage extends NMSStorage {
* @throws ClassNotFoundException
* if class was not found
*/
private Class<?> getLegacyClass(String name) throws ClassNotFoundException {
try {
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
} catch (ClassNotFoundException | NullPointerException e) {
try {
//modded server?
Class<?> clazz = Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
if (clazz != null) return clazz;
} catch (NullPointerException ignored) {}
throw new ClassNotFoundException(name);
}
public Class<?> getLegacyClass(String name) throws ClassNotFoundException {
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
}
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PING = getField(EntityPlayer, "ping", "field_71138_i"); // {Bukkit, Thermos}
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[]{"setScore", "func_96647_c"}, int.class); // {Bukkit, Thermos}
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam,
new String[]{"setAllowFriendlyFire", "func_96660_a"}, boolean.class); // {Bukkit, Thermos}
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam,
new String[]{"setCanSeeFriendlyInvisibles", "func_98300_b"}, boolean.class); // {Bukkit, Thermos}
PING = getField(EntityPlayer, "ping");
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[] {"setScore", "c"}, int.class); // 1.5.1+, 1.5
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[] {"setAllowFriendlyFire", "a"}, boolean.class); // 1.5.1+, 1.5
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[] {"setCanSeeFriendlyInvisibles", "b"}, boolean.class); // 1.5.1+, 1.5
if (minorVersion >= 7) {
ChatSerializer_DESERIALIZE = getMethod(ChatSerializer, new String[]{"a", "func_150699_a"}, String.class); // {Bukkit, Thermos}
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
}
if (minorVersion >= 8) {
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, "setNameTagVisibility", EnumNameTagVisibility);
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[] {"setNameTagVisibility", "a"}, EnumNameTagVisibility); // {1.8.1+, 1.8}
}
if (minorVersion >= 9) {
DataWatcher_REGISTER = getMethod(DataWatcher, "register", DataWatcherObject, Object.class);
DataWatcher_REGISTER = DataWatcher.getMethod("register", DataWatcherObject, Object.class);
} else {
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"a", "func_75682_a"}, int.class, Object.class); // {Bukkit, Thermos}
DataWatcher_REGISTER = DataWatcher.getMethod("a", int.class, Object.class);
}
if (minorVersion >= 13) {
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, "setPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, "setSuffix", IChatBaseComponent);
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("setPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("setSuffix", IChatBaseComponent);
} else {
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[]{"setPrefix", "func_96666_b"}, String.class); // {Bukkit, Thermos}
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[]{"setSuffix", "func_96662_c"}, String.class); // {Bukkit, Thermos}
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[] {"setPrefix", "b"}, String.class); // 1.5.1+, 1.5
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[] {"setSuffix", "c"}, String.class); // 1.5.1+, 1.5
}
}
@@ -93,7 +81,7 @@ public class LegacyNMSStorage extends NMSStorage {
EntityHuman = getLegacyClass("EntityHuman");
World = getLegacyClass("World");
Packet = getLegacyClass("Packet");
EnumChatFormat = getLegacyClass("EnumChatFormat");
EnumChatFormat = (Class<Enum>) getLegacyClass("EnumChatFormat");
EntityPlayer = getLegacyClass("EntityPlayer");
Entity = getLegacyClass("Entity");
EntityLiving = getLegacyClass("EntityLiving");
@@ -109,7 +97,7 @@ public class LegacyNMSStorage extends NMSStorage {
EntityArmorStand = getLegacyClass("EntityArmorStand");
}
if (minorVersion >= 12) {
ChatMessageType = getLegacyClass("ChatMessageType");
ChatMessageType = (Class<Enum>) getLegacyClass("ChatMessageType");
}
// DataWatcher
@@ -131,15 +119,15 @@ public class LegacyNMSStorage extends NMSStorage {
PacketPlayOutEntityMetadata = getLegacyClass("PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
PacketPlayOutNamedEntitySpawn = getLegacyClass("PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
if (minorVersion >= 7) {
EnumEntityUseAction = getLegacyClass("PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction");
EnumEntityUseAction = (Class<Enum>) getLegacyClass("PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction");
}
// Player Info
if (minorVersion >= 8) {
PacketPlayOutPlayerInfo = getLegacyClass("PacketPlayOutPlayerInfo");
EnumPlayerInfoAction = getLegacyClass("PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
EnumPlayerInfoAction = (Class<Enum>) getLegacyClass("PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
PlayerInfoData = getLegacyClass("PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
EnumGamemode = getLegacyClass("EnumGamemode", "WorldSettings$EnumGamemode");
EnumGamemode = (Class<Enum>) getLegacyClass("EnumGamemode", "WorldSettings$EnumGamemode");
}
// Scoreboard
@@ -150,15 +138,15 @@ public class LegacyNMSStorage extends NMSStorage {
Scoreboard = getLegacyClass("Scoreboard");
ScoreboardObjective = getLegacyClass("ScoreboardObjective");
ScoreboardScore = getLegacyClass("ScoreboardScore");
IScoreboardCriteria = getLegacyClass("IScoreboardCriteria");
IScoreboardCriteria = getLegacyClass("IScoreboardCriteria", "IObjective"); // 1.5.1+, 1.5
ScoreboardTeam = getLegacyClass("ScoreboardTeam");
if (minorVersion >= 8) {
EnumScoreboardHealthDisplay = getLegacyClass("IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
EnumScoreboardAction = getLegacyClass("ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
EnumNameTagVisibility = getLegacyClass("ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
EnumScoreboardHealthDisplay = (Class<Enum>) getLegacyClass("IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
EnumScoreboardAction = (Class<Enum>) getLegacyClass("ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
EnumNameTagVisibility = (Class<Enum>) getLegacyClass("ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
}
if (minorVersion >= 9) {
EnumTeamPush = getLegacyClass("ScoreboardTeamBase$EnumTeamPush");
EnumTeamPush = (Class<Enum>) getLegacyClass("ScoreboardTeamBase$EnumTeamPush");
}
}
}
@@ -1,9 +1,10 @@
package me.neznamy.tab.platforms.bukkit.nms;
package me.neznamy.tab.platforms.bukkit.nms.storage;
/**
* NMS loader for minecraft 1.17+ using Mojang packaging and bukkit names.
*/
public class BukkitModernNMSStorage extends ModernNMSStorage {
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitModernNMSStorage extends NMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
@@ -17,7 +18,7 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PING = getField(EntityPlayer, "e");
ChatSerializer_DESERIALIZE = getMethod(ChatSerializer, "a", String.class);
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"register", "a"}, DataWatcherObject, Object.class); // {Bukkit, Bukkit 1.18+}
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[]{"setScore", "b"}, int.class); // {Bukkit, Bukkit 1.18+}
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a"}, boolean.class); // {Bukkit, Bukkit 1.18+}
@@ -26,9 +27,9 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[]{"setSuffix", "c"}, IChatBaseComponent); // {Bukkit, Bukkit 1.18+}
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[]{"setNameTagVisibility", "a"}, EnumNameTagVisibility); // {Bukkit, Bukkit 1.18+}
if (minorVersion >= 19) {
Registry_a = Registry.getMethod("a", int.class);
IRegistry_X = IRegistry.getDeclaredField("X").get(null);
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("d").get(null);
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getField(PacketPlayOutSpawnEntityLiving, "e");
DataWatcher_b = DataWatcher.getMethod("b");
}
}
@@ -41,16 +42,18 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
NetworkManager = Class.forName("net.minecraft.network.NetworkManager");
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
Packet = Class.forName("net.minecraft.network.protocol.Packet");
EnumChatFormat = Class.forName("net.minecraft.EnumChatFormat");
EnumChatFormat = (Class<Enum>) Class.forName("net.minecraft.EnumChatFormat");
EntityPlayer = Class.forName("net.minecraft.server.level.EntityPlayer");
Entity = Class.forName("net.minecraft.world.entity.Entity");
EntityLiving = Class.forName("net.minecraft.world.entity.EntityLiving");
PlayerConnection = Class.forName("net.minecraft.server.network.PlayerConnection");
PacketPlayOutPlayerListHeaderFooter = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter");
PacketPlayOutChat = getModernClass("net.minecraft.network.protocol.game.ClientboundSystemChatPacket",
"net.minecraft.network.protocol.game.PacketPlayOutChat");
if (minorVersion < 19) {
ChatMessageType = Class.forName("net.minecraft.network.chat.ChatMessageType");
if (minorVersion >= 19) {
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundSystemChatPacket");
} else {
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutChat");
ChatMessageType = (Class<Enum>) Class.forName("net.minecraft.network.chat.ChatMessageType");
}
// DataWatcher
@@ -59,10 +62,16 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
DataWatcherObject = Class.forName("net.minecraft.network.syncher.DataWatcherObject");
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.DataWatcherRegistry");
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.DataWatcherSerializer");
if (is1_19_3Plus()) {
DataWatcher$DataValue = Class.forName("net.minecraft.network.syncher.DataWatcher$b");
}
// Entities
PacketPlayOutSpawnEntityLiving = getModernClass("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving",
"net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
if (minorVersion >= 19) {
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
} else {
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
}
PacketPlayOutEntityTeleport = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport");
PacketPlayInUseEntity = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity");
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$d");
@@ -71,19 +80,30 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook");
PacketPlayOutEntityMetadata = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata");
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn");
EnumEntityUseAction = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction");
EnumEntityUseAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction");
if (minorVersion >= 19) {
Registry = Class.forName("net.minecraft.core.Registry");
IRegistry = Class.forName("net.minecraft.core.IRegistry");
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$a");
EntityTypes = Class.forName("net.minecraft.world.entity.EntityTypes");
}
// Player Info
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo");
EnumPlayerInfoAction = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData");
EnumGamemode = Class.forName("net.minecraft.world.level.EnumGamemode");
if (minorVersion >= 19) {
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$a");
}
if (is1_19_3Plus()) {
//1.19.3+
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$a");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$b");
RemoteChatSession = Class.forName("net.minecraft.network.chat.RemoteChatSession");
RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$a");
} else {
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo");
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData");
}
EnumGamemode = (Class<Enum>) Class.forName("net.minecraft.world.level.EnumGamemode");
// Scoreboard
PacketPlayOutScoreboardDisplayObjective = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective");
@@ -93,12 +113,12 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
ScoreboardObjective = Class.forName("net.minecraft.world.scores.ScoreboardObjective");
ScoreboardScore = Class.forName("net.minecraft.world.scores.ScoreboardScore");
IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria");
EnumScoreboardHealthDisplay = Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay");
EnumScoreboardAction = Class.forName("net.minecraft.server.ScoreboardServer$Action");
EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay");
EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ScoreboardServer$Action");
PacketPlayOutScoreboardTeam = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam");
ScoreboardTeam = Class.forName("net.minecraft.world.scores.ScoreboardTeam");
EnumNameTagVisibility = Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility");
EnumTeamPush = Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush");
PacketPlayOutScoreboardTeam_PlayerAction = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$a");
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility");
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush");
PacketPlayOutScoreboardTeam_PlayerAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$a");
}
}
@@ -1,9 +1,10 @@
package me.neznamy.tab.platforms.bukkit.nms;
package me.neznamy.tab.platforms.bukkit.nms.storage;
/**
* NMS loader for minecraft 1.17+ using Mojang packaging and names.
*/
public class MojangModernNMSStorage extends ModernNMSStorage {
@SuppressWarnings({"unchecked", "rawtypes"})
public class MojangModernNMSStorage extends NMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
@@ -17,18 +18,18 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PING = getField(EntityPlayer, "latency");
ChatSerializer_DESERIALIZE = getMethod(ChatSerializer, "fromJson", String.class);
DataWatcher_REGISTER = getMethod(DataWatcher, "define", DataWatcherObject, Object.class);
ScoreboardScore_setScore = getMethod(ScoreboardScore, "setScore", int.class);
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, "setAllowFriendlyFire", boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, "setSeeFriendlyInvisibles", boolean.class);
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, "setPlayerPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, "setPlayerSuffix", IChatBaseComponent);
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, "setNameTagVisibility", EnumNameTagVisibility);
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("fromJson", String.class);
DataWatcher_REGISTER = DataWatcher.getMethod("define", DataWatcherObject, Object.class);
ScoreboardScore_setScore = ScoreboardScore.getMethod("setScore", int.class);
ScoreboardTeam_setAllowFriendlyFire = ScoreboardTeam.getMethod("setAllowFriendlyFire", boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = ScoreboardTeam.getMethod("setSeeFriendlyInvisibles", boolean.class);
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("setPlayerPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("setPlayerSuffix", IChatBaseComponent);
ScoreboardTeam_setNameTagVisibility = ScoreboardTeam.getMethod("setNameTagVisibility", EnumNameTagVisibility);
if (minorVersion >= 19) {
Registry_a = Registry.getMethod("byId", int.class);
IRegistry_X = IRegistry.getDeclaredField("ENTITY_TYPE").get(null);
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("ARMOR_STAND").get(null);
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getField(PacketPlayOutSpawnEntityLiving, "type");
DataWatcher_b = DataWatcher.getMethod("packDirty");
}
}
@@ -41,16 +42,17 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
EntityHuman = Class.forName("net.minecraft.world.entity.player.Player");
NetworkManager = Class.forName("net.minecraft.network.Connection");
Packet = Class.forName("net.minecraft.network.protocol.Packet");
EnumChatFormat = Class.forName("net.minecraft.ChatFormatting");
EnumChatFormat = (Class<Enum>) Class.forName("net.minecraft.ChatFormatting");
EntityPlayer = Class.forName("net.minecraft.server.level.ServerPlayer");
Entity = Class.forName("net.minecraft.world.entity.Entity");
EntityLiving = Class.forName("net.minecraft.world.entity.LivingEntity");
PlayerConnection = Class.forName("net.minecraft.server.network.ServerGamePacketListenerImpl");
PacketPlayOutPlayerListHeaderFooter = Class.forName("net.minecraft.network.protocol.game.ClientboundTabListPacket");
PacketPlayOutChat = getModernClass("net.minecraft.network.protocol.game.ClientboundSystemChatPacket",
"net.minecraft.network.protocol.game.ClientboundChatPacket");
if (minorVersion < 19) {
ChatMessageType = Class.forName("net.minecraft.network.chat.ChatType");
if (minorVersion >= 19) {
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundSystemChatPacket");
} else {
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundChatPacket");
ChatMessageType = (Class<Enum>) Class.forName("net.minecraft.network.chat.ChatType");
}
// DataWatcher
@@ -59,10 +61,16 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
DataWatcherObject = Class.forName("net.minecraft.network.syncher.EntityDataAccessor");
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.EntityDataSerializers");
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.EntityDataSerializer");
if (is1_19_3Plus()) {
DataWatcher$DataValue = Class.forName("net.minecraft.network.syncher.SynchedEntityData$DataValue");
}
// Entities
PacketPlayOutSpawnEntityLiving = getModernClass("net.minecraft.network.protocol.game.ClientboundAddMobPacket",
"net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
if (minorVersion >= 19) {
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
} else {
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.ClientboundAddMobPacket");
}
PacketPlayOutEntityTeleport = Class.forName("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
PacketPlayInUseEntity = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket");
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$InteractionAction");
@@ -71,19 +79,29 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot");
PacketPlayOutEntityMetadata = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket");
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket");
EnumEntityUseAction = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action");
EnumEntityUseAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action");
if (minorVersion >= 19) {
Registry = Class.forName("net.minecraft.core.IdMap");
IRegistry = Class.forName("net.minecraft.core.Registry");
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$Data");
EntityTypes = Class.forName("net.minecraft.world.entity.EntityType");
}
// Player Info
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket");
EnumPlayerInfoAction = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$Action");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate");
EnumGamemode = Class.forName("net.minecraft.world.level.GameType");
if (minorVersion >= 19) {
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$Data");
}
if (is1_19_3Plus()) {
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry");
RemoteChatSession = Class.forName("net.minecraft.network.chat.RemoteChatSession");
RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$Data");
} else {
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket");
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$Action");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate");
}
EnumGamemode = (Class<Enum>) Class.forName("net.minecraft.world.level.GameType");
// Scoreboard
PacketPlayOutScoreboardDisplayObjective = Class.forName("net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket");
@@ -93,12 +111,12 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
ScoreboardObjective = Class.forName("net.minecraft.world.scores.Objective");
ScoreboardScore = Class.forName("net.minecraft.world.scores.Score");
IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria");
EnumScoreboardHealthDisplay = Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType");
EnumScoreboardAction = Class.forName("net.minecraft.server.ServerScoreboard$Method");
EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType");
EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ServerScoreboard$Method");
PacketPlayOutScoreboardTeam = Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket");
ScoreboardTeam = Class.forName("net.minecraft.world.scores.PlayerTeam");
EnumNameTagVisibility = Class.forName("net.minecraft.world.scores.Team$Visibility");
EnumTeamPush = Class.forName("net.minecraft.world.scores.Team$CollisionRule");
PacketPlayOutScoreboardTeam_PlayerAction = Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket$Action");
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$Visibility");
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$CollisionRule");
PacketPlayOutScoreboardTeam_PlayerAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket$Action");
}
}
@@ -1,4 +1,4 @@
package me.neznamy.tab.platforms.bukkit.nms;
package me.neznamy.tab.platforms.bukkit.nms.storage;
import com.mojang.authlib.GameProfile;
import io.netty.channel.Channel;
@@ -24,12 +24,15 @@ public abstract class NMSStorage {
/** Server's minor version */
protected final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
/** Flag determining whether the server version is at least 1.19.3 or not */
private boolean is1_19_3Plus;
/** Basic universal values */
protected Class<?> Packet;
protected Class<?> NetworkManager;
protected Class<?> EntityArmorStand;
protected Class<?> World;
public Class<?> EnumChatFormat;
public Class<Enum> EnumChatFormat;
public Class<?> EntityPlayer;
protected Class<?> EntityHuman;
protected Class<?> Entity;
@@ -40,10 +43,10 @@ public abstract class NMSStorage {
public Field NETWORK_MANAGER;
public Field CHANNEL;
public Field EntityHuman_ProfilePublicKey;
public Field EntityPlayer_RemoteChatSession;
public Method getHandle;
public Method sendPacket;
public Method getProfile;
public Enum[] EnumChatFormat_values;
/** Chat components */
protected Class<?> IChatBaseComponent;
@@ -52,9 +55,8 @@ public abstract class NMSStorage {
/** PacketPlayOutChat */
protected Class<?> PacketPlayOutChat;
public Class<?> ChatMessageType;
public Class<Enum> ChatMessageType;
public Constructor<?> newPacketPlayOutChat;
public Enum[] ChatMessageType_values;
/** DataWatcher */
protected Class<?> DataWatcher;
@@ -70,9 +72,16 @@ public abstract class NMSStorage {
public Field DataWatcherObject_SERIALIZER;
public Method DataWatcher_REGISTER;
private final DataWatcherRegistry registry;
//1.19.3+
protected Class<?> DataWatcher$DataValue;
public Field DataWatcher$DataValue_POSITION;
public Field DataWatcher$DataValue_VALUE;
public Method DataWatcher_markDirty;
public Method DataWatcher_b;
/** PacketPlayOutSpawnEntityLiving */
public Class<?> PacketPlayOutSpawnEntityLiving;
public Class<?> EntityTypes;
public Constructor<?> newPacketPlayOutSpawnEntityLiving;
public Field PacketPlayOutSpawnEntityLiving_ENTITYID;
public Field PacketPlayOutSpawnEntityLiving_ENTITYTYPE;
@@ -83,8 +92,7 @@ public abstract class NMSStorage {
public Field PacketPlayOutSpawnEntityLiving_Y;
public Field PacketPlayOutSpawnEntityLiving_Z;
public Field PacketPlayOutSpawnEntityLiving_DATAWATCHER;
public Method Registry_a;
public Object IRegistry_X;
public Object EntityTypes_ARMOR_STAND;
/** PacketPlayOutEntityTeleport */
public Class<?> PacketPlayOutEntityTeleport;
@@ -105,7 +113,7 @@ public abstract class NMSStorage {
/** Other entity packets */
public Class<?> PacketPlayInUseEntity;
public Class<?> PacketPlayInUseEntity$d;
protected Class<?> EnumEntityUseAction;
protected Class<Enum> EnumEntityUseAction;
public Field PacketPlayInUseEntity_ENTITY;
public Field PacketPlayInUseEntity_ACTION;
@@ -127,9 +135,9 @@ public abstract class NMSStorage {
/** PacketPlayOutPlayerInfo */
public Class<?> PacketPlayOutPlayerInfo;
protected Class<?> EnumPlayerInfoAction;
public Class<Enum> EnumPlayerInfoAction;
protected Class<?> PlayerInfoData;
protected Class<?> EnumGamemode;
public Class<Enum> EnumGamemode;
protected Class<?> ProfilePublicKey;
protected Class<?> ProfilePublicKey$a;
public Constructor<?> newPacketPlayOutPlayerInfo;
@@ -137,13 +145,25 @@ public abstract class NMSStorage {
public Field PacketPlayOutPlayerInfo_ACTION;
public Field PacketPlayOutPlayerInfo_PLAYERS;
public Method PlayerInfoData_getProfile;
public Method PlayerInfoData_isListed;
public Method PlayerInfoData_getLatency;
public Method PlayerInfoData_getGamemode;
public Method PlayerInfoData_getDisplayName;
public Method PlayerInfoData_getProfilePublicKeyRecord;
public Method ProfilePublicKey_getRecord;
public Enum[] EnumPlayerInfoAction_values;
public Enum[] EnumGamemode_values;
//1.19.3+
public Class<?> ClientboundPlayerInfoRemovePacket;
public Class<?> RemoteChatSession;
public Class<?> RemoteChatSession$Data;
public Constructor<?> newClientboundPlayerInfoRemovePacket;
public Constructor<?> newRemoteChatSession;
public Constructor<?> newRemoteChatSession$Data;
public Method ClientboundPlayerInfoRemovePacket_getEntries;
public Method RemoteChatSession_getSessionId;
public Method RemoteChatSession_getProfilePublicKey;
public Method RemoteChatSession$Data_getSessionId;
public Method RemoteChatSession$Data_getProfilePublicKey;
/** Scoreboard objectives */
public Class<?> PacketPlayOutScoreboardDisplayObjective;
@@ -153,8 +173,8 @@ public abstract class NMSStorage {
protected Class<?> ScoreboardObjective;
protected Class<?> ScoreboardScore;
protected Class<?> IScoreboardCriteria;
public Class<?> EnumScoreboardHealthDisplay;
protected Class<?> EnumScoreboardAction;
public Class<Enum> EnumScoreboardHealthDisplay;
public Class<Enum> EnumScoreboardAction;
public Constructor<?> newScoreboardObjective;
protected Constructor<?> newScoreboard;
public Constructor<?> newScoreboardScore;
@@ -171,15 +191,13 @@ public abstract class NMSStorage {
public Field PacketPlayOutScoreboardObjective_RENDERTYPE;
public Field PacketPlayOutScoreboardObjective_DISPLAYNAME;
public Method ScoreboardScore_setScore;
public Enum[] EnumScoreboardHealthDisplay_values;
public Enum[] EnumScoreboardAction_values;
/** PacketPlayOutScoreboardTeam */
public Class<?> PacketPlayOutScoreboardTeam;
protected Class<?> ScoreboardTeam;
protected Class<?> EnumNameTagVisibility;
protected Class<?> EnumTeamPush;
protected Class<?> PacketPlayOutScoreboardTeam_PlayerAction;
public Class<Enum> EnumNameTagVisibility;
public Class<Enum> EnumTeamPush;
public Class<Enum> PacketPlayOutScoreboardTeam_PlayerAction;
public Constructor<?> newScoreboardTeam;
public Constructor<?> newPacketPlayOutScoreboardTeam;
public Field PacketPlayOutScoreboardTeam_NAME;
@@ -196,9 +214,6 @@ public abstract class NMSStorage {
public Method PacketPlayOutScoreboardTeam_of;
public Method PacketPlayOutScoreboardTeam_ofBoolean;
public Method PacketPlayOutScoreboardTeam_ofString;
public Enum[] EnumNameTagVisibility_values;
public Enum[] EnumTeamPush_values;
public Enum[] PacketPlayOutScoreboardTeam_PlayerAction_values;
/** Other */
public Object emptyScoreboard;
@@ -211,6 +226,10 @@ public abstract class NMSStorage {
* If some field, constructor or method was not found
*/
public NMSStorage() throws ReflectiveOperationException {
try {
Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
is1_19_3Plus = true;
} catch (ClassNotFoundException | NullPointerException ignored) {}
ProtocolVersion.UNKNOWN_SERVER_VERSION.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
loadClasses();
if (minorVersion >= 7) {
@@ -226,7 +245,6 @@ public abstract class NMSStorage {
PLAYER_CONNECTION = getFields(EntityPlayer, PlayerConnection).get(0);
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
sendPacket = getMethods(PlayerConnection, void.class, Packet).get(0);
EnumChatFormat_values = getEnumValues(EnumChatFormat);
registry = new DataWatcherRegistry(this);
dataWatcher();
entityTeleport();
@@ -266,9 +284,6 @@ public abstract class NMSStorage {
* If some method, field or constructor was not found
*/
protected void chat() throws ReflectiveOperationException {
if (minorVersion >= 12 && minorVersion <= 18) {
ChatMessageType_values = getEnumValues(ChatMessageType);
}
if (minorVersion >= 19) {
try {
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, boolean.class);
@@ -295,20 +310,36 @@ public abstract class NMSStorage {
*/
protected void playerInfo() throws ReflectiveOperationException {
if (minorVersion < 8) return;
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
if (is1_19_3Plus()) {
newClientboundPlayerInfoRemovePacket = ClientboundPlayerInfoRemovePacket.getConstructor(List.class);
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumSet.class, Collection.class);
ClientboundPlayerInfoRemovePacket_getEntries = getMethods(ClientboundPlayerInfoRemovePacket, List.class).get(0);
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumSet.class).get(0);
newRemoteChatSession$Data = RemoteChatSession$Data.getConstructor(UUID.class, ProfilePublicKey$a);
RemoteChatSession$Data_getSessionId = getMethods(RemoteChatSession$Data, UUID.class).get(0);
RemoteChatSession$Data_getProfilePublicKey = getMethods(RemoteChatSession$Data, ProfilePublicKey$a).get(0);
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, RemoteChatSession$Data).get(0);
EntityPlayer_RemoteChatSession = getFields(EntityPlayer, RemoteChatSession).get(0);
PlayerInfoData_isListed = getMethods(PlayerInfoData, boolean.class).get(0);
} else {
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
if (minorVersion >= 19) {
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, ProfilePublicKey$a).get(0);
EntityHuman_ProfilePublicKey = getFields(EntityHuman, ProfilePublicKey).get(0);
}
}
newPlayerInfoData = PlayerInfoData.getConstructors()[0];
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
PacketPlayOutPlayerInfo_PLAYERS = getFields(PacketPlayOutPlayerInfo, List.class).get(0);
PlayerInfoData_getProfile = getMethods(PlayerInfoData, GameProfile.class).get(0);
PlayerInfoData_getLatency = getMethods(PlayerInfoData, int.class).get(0);
for (Method m: getMethods(PlayerInfoData, int.class)) {
// do not take .hashCode(), which is final
if (!Modifier.isFinal(m.getModifiers())) PlayerInfoData_getLatency = m;
}
PlayerInfoData_getGamemode = getMethods(PlayerInfoData, EnumGamemode).get(0);
PlayerInfoData_getDisplayName = getMethods(PlayerInfoData, IChatBaseComponent).get(0);
EnumPlayerInfoAction_values = getEnumValues(EnumPlayerInfoAction);
EnumGamemode_values = getEnumValues(EnumGamemode);
if (minorVersion >= 19) {
EntityHuman_ProfilePublicKey = getFields(EntityHuman, ProfilePublicKey).get(0);
ProfilePublicKey_getRecord = getMethods(ProfilePublicKey, ProfilePublicKey$a).get(0);
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, ProfilePublicKey$a).get(0);
}
}
@@ -320,7 +351,11 @@ public abstract class NMSStorage {
*/
protected void spawnEntityLiving() throws ReflectiveOperationException {
if (minorVersion >= 17) {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
if (is1_19_3Plus()) {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(Entity);
} else {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
}
} else {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor();
}
@@ -403,7 +438,11 @@ public abstract class NMSStorage {
protected void otherEntity() throws ReflectiveOperationException {
PacketPlayOutEntity_ENTITYID = getFields(PacketPlayOutEntity, int.class).get(0);
PacketPlayOutEntityDestroy_ENTITIES = setAccessible(PacketPlayOutEntityDestroy.getDeclaredFields()[0]);
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class);
if (is1_19_3Plus()) {
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, List.class);
} else {
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class);
}
PacketPlayOutEntityMetadata_LIST = getFields(PacketPlayOutEntityMetadata, List.class).get(0);
PacketPlayOutNamedEntitySpawn_ENTITYID = getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
if (minorVersion >= 7) {
@@ -432,6 +471,11 @@ public abstract class NMSStorage {
DataWatcherItem_TYPE = getFields(DataWatcherItem, DataWatcherObject).get(0);
DataWatcherObject_SLOT = getFields(DataWatcherObject, int.class).get(0);
DataWatcherObject_SERIALIZER = getFields(DataWatcherObject, DataWatcherSerializer).get(0);
if (is1_19_3Plus()) {
DataWatcher$DataValue_POSITION = getFields(DataWatcher$DataValue, int.class).get(0);
DataWatcher$DataValue_VALUE = getFields(DataWatcher$DataValue, Object.class).get(0);
DataWatcher_markDirty = getMethods(DataWatcher, void.class, DataWatcherObject).get(0);
}
} else {
DataWatcherItem_TYPE = getFields(DataWatcherItem, int.class).get(1);
}
@@ -456,8 +500,6 @@ public abstract class NMSStorage {
List<Field> list = getFields(PacketPlayOutScoreboardObjective, int.class);
PacketPlayOutScoreboardObjective_METHOD = list.get(list.size()-1);
if (minorVersion >= 8) {
EnumScoreboardHealthDisplay_values = getEnumValues(EnumScoreboardHealthDisplay);
EnumScoreboardAction_values = getEnumValues(EnumScoreboardAction);
PacketPlayOutScoreboardObjective_RENDERTYPE = getFields(PacketPlayOutScoreboardObjective, EnumScoreboardHealthDisplay).get(0);
}
if (minorVersion >= 13) {
@@ -488,11 +530,7 @@ public abstract class NMSStorage {
PacketPlayOutScoreboardTeam_ACTION = getInstanceFields(PacketPlayOutScoreboardTeam, int.class).get(0);
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
ScoreboardTeam_getPlayerNameSet = getMethods(ScoreboardTeam, Collection.class).get(0);
if (minorVersion >= 8) {
EnumNameTagVisibility_values = getEnumValues(EnumNameTagVisibility);
}
if (minorVersion >= 9) {
EnumTeamPush_values = getEnumValues(EnumTeamPush);
ScoreboardTeam_setCollisionRule = getMethods(ScoreboardTeam, void.class, EnumTeamPush).get(0);
}
if (minorVersion >= 13) {
@@ -502,7 +540,6 @@ public abstract class NMSStorage {
PacketPlayOutScoreboardTeam_of = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam).get(0);
PacketPlayOutScoreboardTeam_ofBoolean = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam, boolean.class).get(0);
PacketPlayOutScoreboardTeam_ofString = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam, String.class, PacketPlayOutScoreboardTeam_PlayerAction).get(0);
PacketPlayOutScoreboardTeam_PlayerAction_values = getEnumValues(PacketPlayOutScoreboardTeam_PlayerAction);
} else {
newPacketPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.getConstructor(ScoreboardTeam, int.class);
}
@@ -541,7 +578,7 @@ public abstract class NMSStorage {
protected Method getMethod(Class<?> clazz, String[] names, Class<?>... parameterTypes) throws NoSuchMethodException {
for (String name : names) {
try {
return getMethod(clazz, name, parameterTypes);
return clazz.getMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
//not the first method in array
}
@@ -563,39 +600,6 @@ public abstract class NMSStorage {
Arrays.toString(parameterTypes) + " in class " + clazz.getName() + ". Methods with matching parameters: " + list);
}
/**
* Returns Method from class with given name and parameter types. This should be equal to
* simply calling Class#getMethod, which however does not work with a few specific methods on
* CatServer fork. This workaround works for them.
*
* @param clazz
* Class to get method from
* @param name
* Name of the method
* @param parameterTypes
* Parameter types of the method
* @return Method with specified name and parameter types
* @throws NoSuchMethodException
* If no such method was found
*/
protected Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException {
List<Method> list = new ArrayList<>();
for (Method m : clazz.getMethods()) {
if (!m.getName().equals(name) || m.getParameterCount() != parameterTypes.length) continue;
Class<?>[] types = m.getParameterTypes();
boolean valid = true;
for (int i=0; i<types.length; i++) {
if (types[i] != parameterTypes[i]) {
valid = false;
break;
}
}
if (valid) list.add(m);
}
if (!list.isEmpty()) return list.get(0);
throw new NoSuchMethodException("No method found with name " + name + " in class " + clazz.getName() + " with parameters " + Arrays.toString(parameterTypes));
}
/**
* Returns all methods from class which return specified class type and have specified parameter types.
*
@@ -682,46 +686,6 @@ public abstract class NMSStorage {
throw new NoSuchFieldException("Field \"" + name + "\" was not found in class " + clazz.getName());
}
/**
* Returns field from class with specified possible names of the field.
* Names are checked in specified order and first match is returned.
* If no match is found, {@link NoSuchFieldException} is thrown.
*
* @param clazz
* Class to get field from
* @param potentialNames
* Potential names of the field
* @return Field with specified name
* @throws NoSuchFieldException
* If no match is found
*/
protected Field getField(Class<?> clazz, String... potentialNames) throws NoSuchFieldException {
for (String name : potentialNames) {
try {
return getField(clazz, name);
} catch (NoSuchFieldException e) {
//not the first field name from array
}
}
throw new NoSuchFieldException("No field found in class " + clazz.getName() + " with potential names " + Arrays.toString(potentialNames));
}
/**
* Returns all Enum values of specified class
*
* @param enumClass
* Class to get values of
* @return Array of all values of the enum class
*/
protected Enum[] getEnumValues(Class<?> enumClass) {
if (!enumClass.isEnum()) throw new IllegalArgumentException(enumClass.getName() + " is not an enum class");
try {
return (Enum[]) enumClass.getMethod("values").invoke(null);
} catch (ReflectiveOperationException ignored) {
return new Enum[0]; // this will never happen
}
}
/**
* Returns server's minor version
* @return server's minor version
@@ -787,4 +751,8 @@ public abstract class NMSStorage {
}
return fields;
}
public boolean is1_19_3Plus() {
return is1_19_3Plus;
}
}
@@ -0,0 +1,49 @@
package me.neznamy.tab.platforms.bukkit.nms.storage;
import me.neznamy.tab.platforms.bukkit.Main;
/**
* NMS loader for Thermos 1.7.10.
*/
public class ThermosNMSStorage extends BukkitLegacyNMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
*
* @throws ReflectiveOperationException
* If any class, field or method fails to load
*/
public ThermosNMSStorage() throws ReflectiveOperationException {
}
/**
* Returns class from given name
*
* @param name
* class name
* @return class from given name
* @throws ClassNotFoundException
* if class was not found
*/
@Override
public Class<?> getLegacyClass(String name) throws ClassNotFoundException {
try {
return Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
} catch (NullPointerException e) {
// nested class not found
throw new ClassNotFoundException(name);
}
}
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PING = getField(EntityPlayer, "field_71138_i");
ScoreboardScore_setScore = ScoreboardScore.getMethod("func_96647_c", int.class);
ScoreboardTeam_setAllowFriendlyFire = ScoreboardTeam.getMethod("func_96660_a", boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = ScoreboardTeam.getMethod("func_98300_b", boolean.class);
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("func_150699_a", String.class);
DataWatcher_REGISTER = DataWatcher.getMethod("func_75682_a", int.class, Object.class);
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("func_96666_b", String.class);
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("func_96662_c", String.class);
}
}
-232
View File
@@ -1,232 +0,0 @@
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Header-&-Footer
header-footer:
enabled: true
disable-in-worlds:
- disabledworld
header:
- "&3&lServer name"
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
- "&7Online players: &f%online%"
- "&6Online staff: &e%staffonline%"
- ""
- "%animation:MyAnimation1%"
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- "%animation:ping-tps%"
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- ""
- "&7Visit our webpage %animation:web%"
per-world:
world1:
header:
- "an example of world with custom"
footer:
- "header/footer and prefix/suffix"
world2;world3:
header:
- "This is a shared header for"
- "world2 and world3"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting
tablist-name-formatting:
enabled: true
align-tabsuffix-on-the-right: false
character-width-overrides: {}
anti-override: true
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Nametags
scoreboard-teams:
enabled: true
enable-collision: true
invisible-nametags: false
anti-override: true
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Sorting-players-in-tablist
sorting-types:
- "GROUPS:owner,admin,mod,helper,builder,vip,default"
- "PLACEHOLDER_A_TO_Z:%player%"
case-sensitive-sorting: true
can-see-friendly-invisibles: false
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Unlimited-nametag-mode
unlimited-nametag-mode:
enabled: false
use-marker-tag-for-1-8-x-clients: false
disable-on-boats: true
disable-in-worlds:
- disabledworld
dynamic-lines:
- abovename
- nametag #the original one, combination of tagprefix + customtagname + tagsuffix
- belowname
- another
static-lines:
myCustomLine: 0.66
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Yellow-number
yellow-number-in-tablist:
enabled: true
value: "%ping%"
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Belowname
belowname-objective:
enabled: false
number: "%health%"
text: "&cHealth"
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix
prevent-spectator-effect:
enabled: false
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar
bossbar:
enabled: false
toggle-command: /bossbar
remember-toggle-choice: false
hidden-by-default: false
disable-in-worlds:
- disabledworld
bars:
ServerInfo:
style: "PROGRESS" # for 1.9+: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20
color: "%animation:barcolors%" # for 1.9+: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
progress: "100" # in %
text: "&fWebsite: &bwww.domain.com"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Per-world-playerlist
per-world-playerlist:
enabled: false
# players with tab.staff will always see all players
allow-bypass-permission: false
# players in these worlds will always see all players
ignore-effect-in-worlds:
- ignoredworld
- build
shared-playerlist-world-groups:
lobby:
- lobby1
- lobby2
minigames:
- paintball
- bedwars
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard
scoreboard:
enabled: false
toggle-command: /sb
remember-toggle-choice: false
hidden-by-default: false
use-numbers: false
static-number: 0
delay-on-join-milliseconds: 0
respect-other-plugins: true
disable-in-worlds:
- disabledworld
scoreboards:
admin:
# Only players with tab.scoreboard.admin can see this scoreboard, others will see scoreboard1
display-condition: "permission:tab.scoreboard.admin"
title: "Admin scoreboard"
lines:
- "%animation:MyAnimation1%"
- "&6Online:"
- "* &eOnline&7: &f%online%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- " "
- "&6Server Info:"
- "* &bTPS&7: %tps%"
- "* &bUptime&7: &f%server_uptime%"
- "* &bMemory&7: &f%memory-used%&7/&4%memory-max%"
- "%animation:MyAnimation1%"
scoreboard1:
title: "Default"
lines:
- "%animation:MyAnimation1%"
- "&6My Stats:"
- "* &eKills&7: &f%statistic_player_kills%"
- "* &eDeaths&7: &f%statistic_deaths%"
- "* &eHealth&7: &f%health%"
- " "
- "&6Personal Info:"
- "* &bRank&7: &f%group%"
- "* &bPing&7: &f%ping%&7ms"
- "* &bWorld&7: &f%world%"
- "%animation:MyAnimation1%"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Ping-Spoof
ping-spoof:
enabled: false
value: 0
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Pet-name-fix
fix-pet-names:
enabled: false
placeholders:
date-format: "dd.MM.yyyy"
time-format: "[HH:mm:ss / h:mm a]"
time-offset: 0
register-tab-expansion: false
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements
placeholder-output-replacements:
"%essentials_vanished%":
"yes": "&7| Vanished"
"no": ""
"%afk%":
true: " &o[AFK]&r"
false: ""
"%essentials_nickname%":
"%essentials_nickname%": "%player%" #placeholder did not parse correctly, show real name instead
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders
conditions:
nick: # use it with %condition:nick%
conditions:
- "%player%=%essentials_nickname%"
yes: "%player%"
no: "~%essentials_nickname%"
placeholderapi-refresh-intervals:
default-refresh-interval: 500
server:
"%server_uptime%": 1000
"%server_tps_1_colored%": 1000
"%server_unique_joins%": 5000
player:
"%player_health%": 200
"%player_ping%": 1000
"%vault_prefix%": 1000
relational:
"%rel_factionsuuid_relation_color%": 1000
# assigning groups by permission nodes instead of taking them from permission plugin
assign-groups-by-permissions: false
# if the option above is true, all groups are taken based on permissions and the one higher in this list is used as primary
# Warning! This is not sorting list and has nothing to do with sorting players in tablist!
primary-group-finding-list:
- Owner
- Admin
- Mod
- Helper
- default
debug: false
# https://github.com/NEZNAMY/TAB/wiki/MySQL
mysql:
enabled: false
host: 127.0.0.1
port: 3306
database: tab
username: user
password: password
@@ -1,7 +1,7 @@
package me.neznamy.tab.platforms.bungeecord;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.chat.EnumChatFormat;
@@ -44,6 +44,37 @@ public class BungeePacketBuilder extends PacketBuilder {
@Override
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) {
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
if (packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
PlayerListItemRemove remove = new PlayerListItemRemove();
remove.setUuids(packet.getEntries().stream().map(PlayerInfoData::getUniqueId).toArray(UUID[]::new));
return remove;
}
List<Item> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
Item item = new Item();
if (data.getDisplayName() != null) item.setDisplayName(data.getDisplayName().toString(clientVersion));
if (data.getGameMode() != null) item.setGamemode(data.getGameMode().ordinal()-1);
item.setListed(data.isListed());
item.setPing(data.getLatency());
if (data.getSkin() != null) {
item.setProperties(new Property[]{new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature())});
} else {
item.setProperties(new Property[0]);
}
item.setUsername(data.getName());
item.setUuid(data.getUniqueId());
item.setChatSessionId(data.getChatSessionId());
item.setPublicKey((PlayerPublicKey) data.getProfilePublicKey());
items.add(item);
}
PlayerListItemUpdate bungeePacket = new PlayerListItemUpdate();
PlayerListItemUpdate.Action[] array = packet.getActions().stream().map(action -> PlayerListItemUpdate.Action.valueOf(
action.toString().replace("GAME_MODE", "GAMEMODE"))).toArray(PlayerListItemUpdate.Action[]::new);
bungeePacket.setActions(EnumSet.of(array[0], array));
bungeePacket.setItems(items.toArray(new Item[0]));
return bungeePacket;
}
List<Item> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
Item item = new Item();
@@ -69,7 +100,9 @@ public class BungeePacketBuilder extends PacketBuilder {
items.add(item);
}
PlayerListItem bungeePacket = new PlayerListItem();
bungeePacket.setAction(PlayerListItem.Action.valueOf(packet.getAction().toString().replace("GAME_MODE", "GAMEMODE")));
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
bungeePacket.setAction(PlayerListItem.Action.valueOf(action.toString().replace("GAME_MODE", "GAMEMODE")));
bungeePacket.setItems(items.toArray(new Item[0]));
return bungeePacket;
}
@@ -106,13 +139,50 @@ public class BungeePacketBuilder extends PacketBuilder {
@Override
public PacketPlayOutPlayerInfo readPlayerInfo(Object bungeePacket, ProtocolVersion clientVersion) {
PlayerListItem item = (PlayerListItem) bungeePacket;
List<PlayerInfoData> listData = new ArrayList<>();
for (Item i : item.getItems()) {
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
listData.add(new PlayerInfoData(i.getUsername(), i.getUuid(), skin, i.getPing(), EnumGamemode.VALUES[i.getGamemode()+1], IChatBaseComponent.deserialize(i.getDisplayName()), i.getPublicKey()));
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
if (bungeePacket instanceof PlayerListItemRemove) {
return new PacketPlayOutPlayerInfo(
EnumPlayerInfoAction.REMOVE_PLAYER,
Arrays.stream(((PlayerListItemRemove) bungeePacket).getUuids()).map(PlayerInfoData::new).collect(Collectors.toList())
);
}
PlayerListItemUpdate item = (PlayerListItemUpdate) bungeePacket;
List<PlayerInfoData> listData = new ArrayList<>();
for (Item i : item.getItems()) {
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
listData.add(new PlayerInfoData(
i.getUsername(),
i.getUuid(),
skin,
Boolean.TRUE.equals(i.getListed()),
i.getPing() == null ? 0 : i.getPing(),
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
IChatBaseComponent.deserialize(i.getDisplayName()),
i.getChatSessionId(),
i.getPublicKey()));
}
EnumPlayerInfoAction[] array = item.getActions().stream().map(action ->
EnumPlayerInfoAction.valueOf(action.toString().replace("GAMEMODE", "GAME_MODE"))).toArray(EnumPlayerInfoAction[]::new);
EnumSet<EnumPlayerInfoAction> actions = EnumSet.of(array[0], array);
return new PacketPlayOutPlayerInfo(actions, listData);
} else {
PlayerListItem item = (PlayerListItem) bungeePacket;
List<PlayerInfoData> listData = new ArrayList<>();
for (Item i : item.getItems()) {
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
listData.add(new PlayerInfoData(
i.getUsername(),
i.getUuid(),
skin,
true,
i.getPing() == null ? 0 : i.getPing(),
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
IChatBaseComponent.deserialize(i.getDisplayName()),
null,
i.getPublicKey()));
}
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
}
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
}
@Override
@@ -11,6 +11,7 @@ import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.PipelineInjector;
import me.neznamy.tab.shared.features.redis.RedisPlayer;
import me.neznamy.tab.shared.features.sorting.Sorting;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
@@ -68,6 +69,8 @@ public class BungeePipelineInjector extends PipelineInjector {
try {
switch(packet.getClass().getSimpleName()) {
case "PlayerListItem":
case "PlayerListItemUpdate":
case "PlayerListItemRemove":
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
return;
case "Team":
@@ -91,7 +94,7 @@ public class BungeePipelineInjector extends PipelineInjector {
default:
break;
}
} catch (Exception e){
} catch (Exception | NoSuchMethodError e){
TAB.getInstance().getErrorManager().printError("An error occurred when analyzing packets for player " + player.getName() + " with client version " + player.getVersion().getFriendlyName(), e);
}
try {
@@ -111,9 +114,10 @@ public class BungeePipelineInjector extends PipelineInjector {
if (packet.getMode() == 1 || packet.getMode() == 2 || packet.getMode() == 4) return;
Collection<String> col = Lists.newArrayList(packet.getPlayers());
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (col.contains(p.getNickname()) && !((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !packet.getName().equals(p.getTeamName())) {
logTeamOverride(packet.getName(), p.getName(), p.getTeamName());
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !packet.getName().equals(sorting.getShortTeamName(p))) {
logTeamOverride(packet.getName(), p.getName(), sorting.getShortTeamName(p));
col.remove(p.getNickname());
}
}
@@ -19,6 +19,7 @@ import net.md_5.bungee.protocol.Protocol;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.UUID;
/**
* TabPlayer implementation for BungeeCord
@@ -142,6 +143,11 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
return ((InitialHandler)getPlayer().getPendingConnection()).getLoginRequest().getPublicKey();
}
@Override
public UUID getChatSessionId() {
throw new UnsupportedOperationException("Not implemented yet");
}
@Override
public void sendPluginMessage(byte[] message) {
Preconditions.checkNotNull(message, "message");
@@ -26,7 +26,7 @@ public class Main extends Plugin {
@Override
public void onEnable(){
if (!isVersionSupported()) {
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1637 and up to work. Get it at https://ci.md-5.net/job/BungeeCord/"));
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1671 and up (or an equivalent fork) to work."));
return;
}
BungeePlatform platform = new BungeePlatform();
@@ -47,7 +47,7 @@ public class Main extends Plugin {
*/
private boolean isVersionSupported() {
try {
Class.forName("net.md_5.bungee.protocol.Property");
Class.forName("net.md_5.bungee.protocol.packet.PlayerListItemUpdate");
return true;
} catch (ClassNotFoundException e) {
return false;
+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.5
projectVersion=3.2.4
# Gradle properties
org.gradle.daemon=true
+8 -8
View File
@@ -3,19 +3,19 @@ metadata.format.version = "1.1"
[versions]
# API
netty = "4.1.80.Final"
netty = "4.1.86.Final"
jsonSimple = "1.1.1"
snakeyaml = "1.30"
snakeyaml = "1.33"
yamlAssist = "1.0.5"
gson = "2.9.1"
slf4j = "1.7.36"
minimessage = "4.11.0"
gson = "2.10"
slf4j = "2.0.6"
minimessage = "4.12.0"
# General
luckperms = "5.4"
guava = "31.1-jre"
floodgate = "2.2.0-SNAPSHOT"
kotlin = "1.7.10"
kotlin = "1.8.0"
event = "3.0.0"
# Platforms
@@ -27,12 +27,12 @@ velocity = "3.1.2-SNAPSHOT"
# Platform extras
placeholderapi = "2.11.2"
vault = "1.7"
via = "4.4.2"
via = "4.5.1"
authlib = "1.5.25"
bstats = "3.0.0"
essentials = "2.19.7"
premiumVanish = "2.7.11-2"
redisBungee = "0.7.3"
redisBungee = "0.10.1"
# Plugins
indra = "2.1.1"
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+6
View File
@@ -205,6 +205,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
Vendored
+8 -6
View File
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
@@ -95,8 +95,6 @@ class KryptonPlatform(
override fun isPluginEnabled(plugin: String): Boolean = server.pluginManager.isLoaded(plugin)
override fun getConfigName(): String = "kryptonconfig.yml"
private fun loadNametagFeature() {
if (!TAB.getInstance().config.getBoolean("scoreboard-teams.enabled", true)) return
if (TAB.getInstance().config.getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false)) {
@@ -1,214 +0,0 @@
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
scoreboard-teams:
enabled: true
enable-collision: true
invisible-nametags: false
anti-override: true
# types: GROUPS:<group1>,<group2>... , PERMISSIONS:<permission1>,<perm2>... , PLACEHOLDER:<output1>,<output2>... , PLACEHOLDER_A_TO_Z, PLACEHOLDER_Z_TO_A, PLACEHOLDER_HIGH_TO_LOW, PLACEHOLDER_LOW_TO_HIGH
sorting-types:
- "GROUPS:owner,admin,mod,helper,builder,vip,default"
- "PLACEHOLDER_A_TO_Z:%player%"
case-sensitive-sorting: true
disable-in-worlds:
- disabledworld
unlimited-nametag-mode:
enabled: false
use-marker-tag-for-1-8-x-clients: false
disable-on-boats: true
space-between-lines: 0.22
disable-in-worlds:
- disabledworld
dynamic-lines:
- abovename
- nametag #the original one, combination of tagprefix + customtagname + tagsuffix
- belowname
- another
static-lines:
myCustomLine: 0.66
tablist-name-formatting:
enabled: true
align-tabsuffix-on-the-right: false
character-width-overrides: {}
anti-override: true
disable-in-worlds:
- disabledworld
header-footer:
enabled: true
disable-in-worlds:
- disabledworld
header:
- "%animation:ServerName%"
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
- "&7Online players: &f%online%"
- "&6Online staff: &e%staffonline%"
- ""
- "%animation:MyAnimation1%"
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- "%animation:ping-tps%"
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- ""
- "&7Visit our webpage %animation:web%"
per-world:
world1:
header:
- "an example of world with custom"
footer:
- "header/footer and prefix/suffix"
world2-world3:
header:
- "This is a shared header for"
- "world2 and world3"
yellow-number-in-tablist:
enabled: true
value: "%ping%"
disable-in-worlds:
- disabledworld
belowname-objective:
enabled: false
number: "%health%"
text: "&c❤"
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix
prevent-spectator-effect:
enabled: false
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar
bossbar:
enabled: false
toggle-command: /bossbar
remember-toggle-choice: false
hidden-by-default: false
disable-in-worlds:
- disabledworld
bars:
ServerInfo:
style: "PROGRESS" # can be one of: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20
color: "%animation:barcolors%" # can be one of: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
progress: "100" # in %
text: "&fWebsite: &bwww.domain.com"
tpsbar:
announcement-bar: true #marks this bossbar as announcement one and doesn't send it always
style: NOTCHED_20
color: GREEN
progress: "%mspt_mean_10s%"
text: "&7TPS&e: %condition:tps_5s% &7MSPT&e: %condition:mspt_mean_10s% &7Ping&e: %condition:ping%&7ms"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Per-world-playerlist
per-world-playerlist:
enabled: false
# players with tab.staff will always see all players
allow-bypass-permission: false
# players in those worlds will always see all players
ignore-effect-in-worlds:
- ignoredworld
- build
shared-playerlist-world-groups:
lobby:
- lobby1
- lobby2
minigames:
- paintball
- bedwars
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard-(premium-only)
scoreboard:
enabled: false
# show/hide the scoreboard
toggle-command: /sb
remember-toggle-choice: false
hidden-by-default: false
# when enabled, will show 0-14 in red next to lines
use-numbers: false
# if use-numbers is disabled, this number will show in red next to all lines
static-number: 0
delay-on-join-milliseconds: 0
respect-other-plugins: true
disable-in-worlds:
- disabledworld
scoreboards:
admin:
# Only players with tab.scoreboard.admin can see this scoreboard, others will see scoreboard1
display-condition: "permission:tab.scoreboard.admin"
title: "Admin scoreboard"
lines:
- "%animation:MyAnimation1%"
- "&6Online:"
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- ""
- "&6Server Info:"
- "* &bTPS&7: %tps_5s%"
- "* &bUptime&7: &f%server_uptime%"
- "* &bMemory&7: &f%memory-used%&7/&4%memory-max%"
- "%animation:MyAnimation1%"
scoreboard1:
title: "Default"
lines:
- "%animation:MyAnimation1%"
- "&6My Stats:"
- "* &eKills&7: &f%statistic_player_kills%"
- "* &eDeaths&7: &f%statistic_deaths%"
- "* &eHealth&7: &f%health%"
- ""
- "&6Personal Info:"
- "* &bRank&7: &f%rank%"
- "* &bPing&7: &f%ping%&7ms"
- "* &bWorld&7: &f%world%"
- "%animation:MyAnimation1%"
ping-spoof:
enabled: false
# when enabled, all players will have this value as their ping
value: 0
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Pet-name-fix
fix-pet-names:
enabled: false
# https://github.com/NEZNAMY/TAB/wiki/config.yml#placeholders
placeholders:
date-format: "dd.MM.yyyy"
time-format: "[HH:mm:ss / h:mm a]"
time-offset: 0
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements-(premium-only)
placeholder-output-replacements:
"%essentials_vanished%":
"yes": "&7| Vanished"
"no": ""
"%afk%":
true: " &o[AFK]&r"
false: ""
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list below> permission
assign-groups-by-permissions: false
#if the option above is true, all groups are taken based on permissions and the one higher in this list is used as primary
primary-group-finding-list:
- Owner
- Admin
- Mod
- Helper
- default
debug: false
mysql:
enabled: false
host: 127.0.0.1
port: 3306
database: tab
username: user
password: password
+1
View File
@@ -9,6 +9,7 @@ dependencyResolutionManagement {
maven("https://repo.viaversion.com/") // ViaVersion
maven("https://repo.essentialsx.net/releases/") // Essentials
maven("https://repo.codemc.org/repository/maven-public/") // RedisBungee
maven("https://repo.codemc.io/repository/maven-snapshots/") // Fixing compilation
maven("https://repo.kryptonmc.org/releases") // YamlAssist, LuckPerms
maven("https://repo.kryptonmc.org/snapshots") // BungeeCord
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
@@ -44,10 +44,10 @@ public class CpuManager implements ThreadManager {
private Map<String, AtomicInteger> packetsPrevious = new ConcurrentHashMap<>();
/** TAB's main thread where all tasks are executed */
private ExecutorService thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
private final ExecutorService thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
/** Thread pool for delayed and repeating tasks to perform sleep before submitting task to main thread */
private final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(20,
private final ScheduledThreadPoolExecutor threadPool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(20,
new ThreadFactoryBuilder().setNameFormat("TAB Repeating / Delayed Thread %d").build());
/** Tasks submitted to main thread before plugin was fully enabled */
@@ -74,13 +74,10 @@ public class CpuManager implements ThreadManager {
}
/**
* Cancels all tasks, new instance is set to avoid errors when starting tasks on shutdown (such as packet readers)
* Cancels all tasks and shuts down thread pools
*/
public void cancelAllTasks() {
//preventing errors when tasks are inserted while shutting down
ExecutorService old = thread;
thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
old.shutdownNow();
thread.shutdownNow();
threadPool.shutdownNow();
}
@@ -89,7 +86,7 @@ public class CpuManager implements ThreadManager {
*/
public void enable() {
enabled = true;
taskQueue.forEach(this::submit);
new ArrayList<>(taskQueue).forEach(this::submit);
taskQueue.clear();
}
@@ -107,13 +104,20 @@ public class CpuManager implements ThreadManager {
taskQueue.add(task);
return null;
}
return (Future<Void>) thread.submit(() -> {
try {
task.run();
} catch (Exception | LinkageError | StackOverflowError e) {
TAB.getInstance().getErrorManager().printError("An error was thrown when executing task", e);
}
});
if (thread.isShutdown()) return null;
try {
return (Future<Void>) thread.submit(() -> {
try {
task.run();
} catch (Exception | LinkageError | StackOverflowError e) {
TAB.getInstance().getErrorManager().printError("An error was thrown when executing task", e);
}
});
} catch (OutOfMemoryError e) {
TAB.getInstance().getErrorManager().criticalError("Failed to schedule task due to " + e.getClass().getName() +
": " + e.getMessage() + ". Threads created by TAB: " + (threadPool.getActiveCount()+1), null);
return null;
}
}
/**
@@ -359,21 +363,25 @@ public class CpuManager implements ThreadManager {
@Override
public Future<?> startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task) {
if (threadPool.isShutdown()) return null;
return threadPool.scheduleAtFixedRate(() -> runMeasuredTask(feature, type, task), 0, intervalMilliseconds, TimeUnit.MILLISECONDS);
}
@Override
public Future<?> startRepeatingTask(int intervalMilliseconds, Runnable task) {
if (threadPool.isShutdown()) return null;
return threadPool.scheduleAtFixedRate(() -> runTask(task), 0, intervalMilliseconds, TimeUnit.MILLISECONDS);
}
@Override
public Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task) {
if (threadPool.isShutdown()) return null;
return threadPool.schedule(() -> runMeasuredTask(feature, type, task), delayMilliseconds, TimeUnit.MILLISECONDS);
}
@Override
public Future<?> runTaskLater(int delayMilliseconds, Runnable task) {
if (threadPool.isShutdown()) return null;
return threadPool.schedule(() -> submit(task), delayMilliseconds, TimeUnit.MILLISECONDS);
}
}
@@ -225,6 +225,6 @@ public class DynamicText implements Property {
RelationalPlaceholderImpl pl = (RelationalPlaceholderImpl) TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
format = format.replace(pl.getIdentifier(), viewer == null ? "" : pl.getLastValue(viewer, owner));
}
return format;
return EnumChatFormat.color(format);
}
}
@@ -227,7 +227,8 @@ public class ErrorManager {
}
/**
* Makes interval divisible by 50 and sends error message if it was not already or was 0 or less
* Makes interval divisible by {@link me.neznamy.tab.api.TabConstants.Placeholder#MINIMUM_REFRESH_INTERVAL}
* and sends error message if it was not already or was 0 or less
*
* @param name
* name of animation used in error message
@@ -244,10 +245,10 @@ public class ErrorManager {
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s. Refresh cannot be negative! &bUsing 1000.", name, interval));
return 1000;
}
if (interval % 50 != 0) {
int newInterval = interval - interval%50;
if (newInterval == 0) newInterval = 50;
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s which is not divisible by 50! &bUsing %s.", name, interval, newInterval));
if (interval % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL != 0) {
int newInterval = interval - interval % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL;
if (newInterval == 0) newInterval = TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL;
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s which is not divisible by " + TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL + "! &bUsing %s.", name, interval, newInterval));
return newInterval;
}
return interval;
@@ -9,9 +9,9 @@ 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.sorting.Sorting;
import org.geysermc.floodgate.api.FloodgateApi;
/**
@@ -47,12 +47,6 @@ public abstract class ITabPlayer implements TabPlayer {
/** Player's permission group override using API */
private String temporaryGroup;
/** Player's personal scoreboard team name */
private String teamName;
/** Note explaining the team name, used in debug command */
private String teamNameNote;
/** Player's game type, {@code true} for Bedrock, {@code false} for Java */
private final boolean bedrockPlayer;
@@ -72,10 +66,10 @@ public abstract class ITabPlayer implements TabPlayer {
private boolean onJoinFinished;
/** Scoreboard teams player has registered */
private final List<String> registeredTeams = new ArrayList<>();
private final Set<String> registeredTeams = new HashSet<>();
/** Scoreboard objectives player has registered */
private final List<String> registeredObjectives = new ArrayList<>();
private final Set<String> registeredObjectives = new HashSet<>();
/** Player's name as seen in GameProfile, can be altered by nick plugins */
private String nickname;
@@ -146,26 +140,6 @@ public abstract class ITabPlayer implements TabPlayer {
return setProperty(feature, identifier, rawValue, null, false);
}
/**
* Sets team name to given value
*
* @param name
* Team name to use
*/
public void setTeamName(String name) {
teamName = name;
}
/**
* Sets team name note to given value
*
* @param note
* New team name note
*/
public void setTeamNameNote(String note) {
teamNameNote = note;
}
/**
* Marks the player as loaded and calls PlayerLoadEvent
*
@@ -175,7 +149,6 @@ public abstract class ITabPlayer implements TabPlayer {
public void markAsLoaded(boolean join) {
onJoinFinished = true;
if (TAB.getInstance().getEventBus() != null) TAB.getInstance().getEventBus().fire(new PlayerLoadEventImpl(this, join));
TAB.getInstance().getPlatform().callLoadEvent(this);
}
/**
@@ -298,13 +271,20 @@ public abstract class ITabPlayer implements TabPlayer {
String team = ((PacketPlayOutScoreboardTeam) packet).getName();
int method = ((PacketPlayOutScoreboardTeam) packet).getAction();
if (method == 0) {
if (registeredTeams.contains(team)) {
if (!registeredTeams.add(team)) {
TAB.getInstance().getErrorManager().printError("Tried to register duplicated team " + team + " to player " + getName());
return;
}
registeredTeams.add(team);
} else if (method == 1) {
registeredTeams.remove(team);
if (!registeredTeams.remove(team)) {
TAB.getInstance().getErrorManager().printError("Tried to unregister non-existing team " + team + " for player " + getName());
return;
}
} else if (method == 2) {
if (!registeredTeams.contains(team)) {
TAB.getInstance().getErrorManager().printError("Tried to modify non-existing team " + team + " for player " + getName());
return;
}
}
}
//avoiding BungeeCord bug kicking all players
@@ -312,13 +292,20 @@ public abstract class ITabPlayer implements TabPlayer {
String objective = ((PacketPlayOutScoreboardObjective) packet).getObjectiveName();
int method = ((PacketPlayOutScoreboardObjective) packet).getAction();
if (method == 0) {
if (registeredObjectives.contains(objective)) {
if (!registeredObjectives.add(objective)) {
TAB.getInstance().getErrorManager().printError("Tried to register duplicated objective " + objective + " to player " + getName());
return;
}
registeredObjectives.add(objective);
} else if (method == 1) {
registeredObjectives.remove(objective);
if (!registeredObjectives.remove(objective)) {
TAB.getInstance().getErrorManager().printError("Tried to unregister non-existing objective " + objective + " for player " + getName());
return;
}
} else if (method == 2) {
if (!registeredObjectives.contains(objective)) {
TAB.getInstance().getErrorManager().printError("Tried to modify non-existing objective " + objective + " for player " + getName());
return;
}
}
}
//avoiding console spam from geyser
@@ -391,20 +378,15 @@ public abstract class ITabPlayer implements TabPlayer {
}
@Override
public String getTeamName() {
TeamManager teams = TAB.getInstance().getTeamManager();
if (teams != null && teams.getForcedTeamName(this) != null) return teams.getForcedTeamName(this);
return teamName;
public boolean isBedrockPlayer() {
return bedrockPlayer;
}
@Override
public String getTeamNameNote() {
return teamNameNote;
}
@Override
public boolean isBedrockPlayer() {
return bedrockPlayer;
public String getTeamName() {
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting == null) return null;
return sorting.getShortTeamName(this);
}
@Override
@@ -1,6 +1,5 @@
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;
@@ -25,22 +24,6 @@ public abstract class Platform {
this.packetBuilder = packetBuilder;
}
/**
* Calls platform-specific load event.
* This method is called when plugin is fully enabled.
*/
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){
//do nothing by default, old event system will be removed
}
/**
* Returns platform-specific packet builder implementation
*
@@ -89,12 +72,4 @@ public abstract class Platform {
* @return Version string if plugin is installed, {@code null} if not
*/
public abstract String getPluginVersion(String plugin);
/**
* 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
*/
public abstract String getConfigName();
}
@@ -150,8 +150,8 @@ public class TAB extends TabAPI {
long time = System.currentTimeMillis();
errorManager = new ErrorManager();
cpu = new CpuManager();
featureManager = new FeatureManagerImpl();
configuration = new Configs();
featureManager = new FeatureManagerImpl();
featureManager.registerFeature(TabConstants.Feature.PLACEHOLDER_MANAGER, new PlaceholderManagerImpl());
featureManager.registerFeature(TabConstants.Feature.GROUP_MANAGER, new GroupManager(platform.detectPermissionPlugin()));
platform.loadFeatures();
@@ -160,7 +160,6 @@ public class TAB extends TabAPI {
for (TabPlayer p : players) ((ITabPlayer)p).markAsLoaded(false);
cpu.enable();
if (eventBus != null) eventBus.fire(TabLoadEventImpl.getInstance());
platform.callLoadEvent();
disabled = false;
sendConsoleMessage("&aEnabled in " + (System.currentTimeMillis()-time) + "ms", true);
return configuration.getMessages().getReloadSuccess();
@@ -232,7 +231,7 @@ public class TAB extends TabAPI {
if (serverVersion.getMinorVersion() >= 8 && configuration.getLayout().getBoolean("enabled", false)) {
if (getTeamManager() == null) {
//sorting is disabled, but layout needs team names
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting(null));
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
}
featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManager());
}
@@ -12,7 +12,7 @@ import me.neznamy.tab.shared.DynamicText;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.PlayerList;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.sorting.Sorting;
/**
* Handler for "/tab debug" subcommand
@@ -111,9 +111,9 @@ public class DebugCommand extends SubCommand {
* @return sorting type
*/
private String getSortingType() {
NameTag nametag = (NameTag) TAB.getInstance().getTeamManager();
if (nametag != null) {
return nametag.getSorting().typesToString();
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting != null) {
return sorting.typesToString();
} else {
return "&cDISABLED";
}
@@ -141,17 +141,16 @@ public class DebugCommand extends SubCommand {
* @return team name of specified player
*/
private String getTeamName(TabPlayer analyzed) {
if (TAB.getInstance().getTeamManager() != null) {
if (((TabFeature) TAB.getInstance().getTeamManager()).isDisabled(analyzed.getServer(), analyzed.getWorld())) {
return "&eTeam name: &cSorting is disabled in player's world/server";
} else {
return "&eTeam name: &a" + analyzed.getTeamName();
}
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting == null) return "";
if (TAB.getInstance().getTeamManager() != null &&
((TabFeature) TAB.getInstance().getTeamManager()).isDisabled(analyzed.getServer(), analyzed.getWorld())) {
return "&eTeam name: &cSorting is disabled in player's world/server";
}
return "";
return "&eTeam name: &a" + (TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.LAYOUT)
? sorting.getFullTeamName(analyzed) : sorting.getShortTeamName(analyzed));
}
/**
* Returns team name note of specified player
*
@@ -160,11 +159,13 @@ public class DebugCommand extends SubCommand {
* @return team name note of specified player
*/
private String getTeamNameNote(TabPlayer analyzed) {
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting == null) return "";
if (TAB.getInstance().getTeamManager() != null &&
!((TabFeature) TAB.getInstance().getTeamManager()).isDisabled(analyzed.getServer(), analyzed.getWorld()) &&
analyzed.getTeamNameNote() != null)
return "&eTeam name note: &r" + analyzed.getTeamNameNote();
return "";
((TabFeature) TAB.getInstance().getTeamManager()).isDisabled(analyzed.getServer(), analyzed.getWorld())) {
return "";
}
return "&eSorting note: &r" + sorting.getTeamNameNote(analyzed);
}
/**
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.command;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
@@ -93,6 +94,9 @@ public class PlayerCommand extends PropertyCommand {
if (property.length > 0 && String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(property[0]))) return;
TAB.getInstance().getConfiguration().getUsers().setProperty(player, type, server, world, value.length() == 0 ? null : value);
TabPlayer pl = TAB.getInstance().getPlayer(player);
try {
if (pl == null) pl = TAB.getInstance().getPlayer(UUID.fromString(player));
} catch (IllegalArgumentException ignored){} // not an uuid string
if (pl != null) {
pl.forceRefresh();
}
@@ -21,13 +21,14 @@ import java.util.List;
public class Configs {
//config.yml file
private final ConfigurationFile config = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream(TAB.getInstance().getPlatform().getConfigName()),
private final ConfigurationFile config = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("config.yml"),
new File(TAB.getInstance().getDataFolder(), "config.yml"));
private final boolean bukkitPermissions = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY && config.getBoolean("use-bukkit-permissions-manager", false);
private final boolean debugMode = config.getBoolean("debug", false);
private final boolean removeGhostPlayers = getSecretOption("remove-ghost-players", false);
private final boolean pipelineInjection = getSecretOption("pipeline-injection", true) && TAB.getInstance().getServerVersion().getMinorVersion() >= 8;
private final String serverName = getSecretOption("server-name", "N/A");
//animations.yml file
private final ConfigurationFile animation = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("animations.yml"),
@@ -64,12 +65,18 @@ public class Configs {
converter.convertAnimationFile(animation);
if (config.getBoolean("mysql.enabled", false)) {
try {
// Initialization to try to avoid java.sql.SQLException: No suitable driver found
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
Class.forName("com.mysql.jdbc.Driver");
}
mysql = new MySQL(config.getString("mysql.host", "127.0.0.1"), config.getInt("mysql.port", 3306),
config.getString("mysql.database", "tab"), config.getString("mysql.username", "user"), config.getString("mysql.password", "password"));
groupFile = new MySQLGroupConfiguration(mysql);
userFile = new MySQLUserConfiguration(mysql);
return;
} catch (SQLException e) {
} catch (SQLException | ClassNotFoundException e) {
TAB.getInstance().getErrorManager().criticalError("Failed to connect to MySQL", e);
}
}
@@ -163,4 +170,8 @@ public class Configs {
public boolean isDebugMode() {
return debugMode;
}
public String getServerName() {
return serverName;
}
}
@@ -1,17 +1,16 @@
package me.neznamy.tab.shared.features;
import me.neznamy.tab.api.HeaderFooterManager;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerListHeaderFooter;
import me.neznamy.tab.shared.TAB;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import me.neznamy.tab.api.HeaderFooterManager;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerListHeaderFooter;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
/**
* Feature handler for header and footer
*/
@@ -90,7 +89,7 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager {
private String getProperty(TabPlayer p, String property) {
String append = getFromConfig(p, property + "append");
if (append.length() > 0) append = "\n" + EnumChatFormat.COLOR_CHAR + "r" + append;
if (append.length() > 0) append = "\n" + append;
return getFromConfig(p, property) + append;
}
@@ -107,15 +106,15 @@ public class HeaderFooter extends TabFeature implements HeaderFooterManager {
if (value.length > 0) {
return value[0];
}
List<String> lines = TAB.getInstance().getConfiguration().getConfig().getStringList("header-footer.per-server." + TAB.getInstance().getConfiguration().getGroup(serverGroups, p.getServer()) + "." + property);
List<String> lines = TAB.getInstance().getConfiguration().getConfig().getStringList("header-footer.per-world." + TAB.getInstance().getConfiguration().getGroup(worldGroups, p.getWorld()) + "." + property);
if (lines == null) {
lines = TAB.getInstance().getConfiguration().getConfig().getStringList("header-footer.per-world." + TAB.getInstance().getConfiguration().getGroup(worldGroups, p.getWorld()) + "." + property);
lines = TAB.getInstance().getConfiguration().getConfig().getStringList("header-footer.per-server." + TAB.getInstance().getConfiguration().getGroup(serverGroups, p.getServer()) + "." + property);
}
if (lines == null) {
lines = TAB.getInstance().getConfiguration().getConfig().getStringList("header-footer." + property);
}
if (lines == null) lines = new ArrayList<>();
return String.join("\n" + EnumChatFormat.COLOR_CHAR + "r", lines);
return String.join("\n", lines);
}
@Override
@@ -38,7 +38,7 @@ public class NickCompatibility extends TabFeature {
@Override
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo packet) {
if (packet.getAction() != EnumPlayerInfoAction.ADD_PLAYER) return;
if (!packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER)) return;
for (PlayerInfoData data : packet.getEntries()) {
TabPlayer packetPlayer = TAB.getInstance().getPlayerByTabListUUID(data.getUniqueId());
if (packetPlayer != null && packetPlayer != receiver && !packetPlayer.getNickname().equals(data.getName())) {
@@ -63,10 +63,10 @@ public class NickCompatibility extends TabFeature {
if (nameTags != null && !nameTags.hasTeamHandlingPaused(player)) {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(player.getTeamName()), this);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(nameTags.getSorting().getShortTeamName(player)), this);
String replacedPrefix = player.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
String replacedSuffix = player.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(player.getTeamName(), replacedPrefix, replacedSuffix, nameTags.translate(nameTags.getTeamVisibility(player, viewer)),
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(nameTags.getSorting().getShortTeamName(player), replacedPrefix, replacedSuffix, nameTags.translate(nameTags.getTeamVisibility(player, viewer)),
nameTags.translate(nameTags.getCollisionManager().getCollision(player)), Collections.singletonList(player.getNickname()), nameTags.getTeamOptions()), this);
}
}
@@ -36,7 +36,7 @@ public class PingSpoof extends TabFeature {
@Override
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
if (info.getAction() != EnumPlayerInfoAction.UPDATE_LATENCY && info.getAction() != EnumPlayerInfoAction.ADD_PLAYER) return;
if (!info.getActions().contains(EnumPlayerInfoAction.UPDATE_LATENCY)) return;
for (PlayerInfoData playerInfoData : info.getEntries()) {
if (TAB.getInstance().getPlayerByTabListUUID(playerInfoData.getUniqueId()) != null) playerInfoData.setLatency(value);
if (layoutManager != null) {
@@ -72,19 +72,7 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
}
private void refresh(Map<TabPlayer, Set<TabFeature>> forceUpdate, Map<TabPlayer, Set<TabFeature>> update) {
for (Entry<TabPlayer, Set<TabFeature>> entry : update.entrySet()) {
if (forceUpdate.containsKey(entry.getKey())) {
entry.getValue().removeAll(forceUpdate.get(entry.getKey()));
}
}
long startRefreshTime = System.nanoTime();
for (Entry<TabPlayer, Set<TabFeature>> entry : forceUpdate.entrySet()) {
for (TabFeature r : entry.getValue()) {
long startTime = System.nanoTime();
r.refresh(entry.getKey(), true);
TAB.getInstance().getCPUManager().addTime(r.getFeatureName(), r.getRefreshDisplayName(), System.nanoTime() - startTime);
}
}
for (Entry<TabPlayer, Set<TabFeature>> entry : update.entrySet()) {
for (TabFeature r : entry.getValue()) {
long startTime = System.nanoTime();
@@ -92,6 +80,13 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
TAB.getInstance().getCPUManager().addTime(r.getFeatureName(), r.getRefreshDisplayName(), System.nanoTime() - startTime);
}
}
for (Entry<TabPlayer, Set<TabFeature>> entry : forceUpdate.entrySet()) {
for (TabFeature r : entry.getValue()) {
long startTime = System.nanoTime();
r.refresh(entry.getKey(), true);
TAB.getInstance().getCPUManager().addTime(r.getFeatureName(), r.getRefreshDisplayName(), System.nanoTime() - startTime);
}
}
//subtracting back usage by this method from placeholder refreshing usage, since it is already counted under different name in this method
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.PLACEHOLDER_REFRESHING, startRefreshTime-System.nanoTime());
}
@@ -252,11 +247,11 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
tabExpansion.setPlaceholderValue(all, p.getIdentifier(), p.getLastValue(all));
}
}
if (p.getRefresh() % 50 == 0 && p.getRefresh() > 0) {
if (p.getRefresh() % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL == 0 && p.getRefresh() > 0) {
int refresh = gcd(p.getRefresh(), refreshInterval);
if (refreshInterval != refresh) {
TAB.getInstance().debug("Decreasing refresh interval of placeholder refreshing task to " + refresh + "ms due to placeholder " + identifier);
refreshTask.cancel(true);
if (refreshTask != null) refreshTask.cancel(true);
refreshInterval = refresh;
refreshTask = TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(refreshInterval, this, "Refreshing placeholders", this::refresh);
atomic.set(0);
@@ -301,9 +296,17 @@ public class PlaceholderManagerImpl extends TabFeature implements PlaceholderMan
@Override
public void onJoin(TabPlayer connectedPlayer) {
if (tabExpansion == null) return;
for (Placeholder p : usedPlaceholders) {
if (p instanceof ServerPlaceholder) { // server placeholders don't update on join
if (p instanceof RelationalPlaceholderImpl) {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
((RelationalPlaceholderImpl)p).update(connectedPlayer, all);
((RelationalPlaceholderImpl)p).update(all, connectedPlayer);
}
}
if (p instanceof PlayerPlaceholderImpl) {
((PlayerPlaceholderImpl)p).update(connectedPlayer);
}
if (tabExpansion != null && p instanceof ServerPlaceholder) { // server placeholders don't update on join
tabExpansion.setPlaceholderValue(connectedPlayer, p.getIdentifier(), ((ServerPlaceholder) p).getLastValue());
}
}
@@ -146,12 +146,14 @@ public class PlayerList extends TabFeature implements TablistFormatManager {
public void onServerChange(TabPlayer p, String from, String to) {
onWorldChange(p, null, null);
if (TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION)) return;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (p.getVersion().getMinorVersion() >= 8) p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME,
new PlayerInfoData(getTablistUUID(all, p), getTabFormat(all, p))), this);
if (all.getVersion().getMinorVersion() >= 8) all.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME,
new PlayerInfoData(getTablistUUID(p, all), getTabFormat(p, all))), this);
}
TAB.getInstance().getCPUManager().runTaskLater(300, this, TabConstants.CpuUsageCategory.PLAYER_JOIN, () -> {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (p.getVersion().getMinorVersion() >= 8) p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME,
new PlayerInfoData(getTablistUUID(all, p), getTabFormat(all, p))), this);
if (all.getVersion().getMinorVersion() >= 8) all.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME,
new PlayerInfoData(getTablistUUID(p, all), getTabFormat(p, all))), this);
}
});
}
@Override
@@ -217,11 +219,11 @@ public class PlayerList extends TabFeature implements TablistFormatManager {
@Override
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
if (disabling || !antiOverrideTabList || TabAPI.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.LAYOUT)) return;
if (info.getAction() != EnumPlayerInfoAction.UPDATE_DISPLAY_NAME && info.getAction() != EnumPlayerInfoAction.ADD_PLAYER) return;
if (disabling || !antiOverrideTabList) return;
if (!info.getActions().contains(EnumPlayerInfoAction.UPDATE_DISPLAY_NAME)) return;
for (PlayerInfoData playerInfoData : info.getEntries()) {
TabPlayer packetPlayer = TAB.getInstance().getPlayerByTabListUUID(playerInfoData.getUniqueId());
if (packetPlayer != null && !isDisabledPlayer(packetPlayer)) {
if (packetPlayer != null && !isDisabledPlayer(packetPlayer) && packetPlayer.getTablistUUID() == getTablistUUID(packetPlayer, receiver)) {
playerInfoData.setDisplayName(getTabFormat(packetPlayer, receiver));
}
}
@@ -48,7 +48,7 @@ public class SpectatorFix extends TabFeature {
@Override
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
if (info.getAction() != EnumPlayerInfoAction.UPDATE_GAME_MODE && info.getAction() != EnumPlayerInfoAction.ADD_PLAYER) return;
if (!info.getActions().contains(EnumPlayerInfoAction.UPDATE_GAME_MODE)) return;
for (PlayerInfoData playerInfoData : info.getEntries()) {
if (playerInfoData.getGameMode() != EnumGamemode.SPECTATOR) continue;
if (receiver.hasPermission(TabConstants.Permission.SPECTATOR_BYPASS)) continue;
@@ -62,11 +62,12 @@ public class YellowNumber extends TabFeature {
addDisabledPlayer(loaded);
continue;
}
if (loaded.isBedrockPlayer()) continue;
loaded.sendCustomPacket(new PacketPlayOutScoreboardObjective(0, OBJECTIVE_NAME, TITLE, displayType), this);
loaded.sendCustomPacket(new PacketPlayOutScoreboardDisplayObjective(DISPLAY_SLOT, OBJECTIVE_NAME), this);
}
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()){
if (isDisabledPlayer(viewer)) continue;
if (isDisabledPlayer(viewer) || viewer.isBedrockPlayer()) continue;
for (TabPlayer target : TAB.getInstance().getOnlinePlayers()){
viewer.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, target.getNickname(), getValue(target)), this);
}
@@ -76,7 +77,7 @@ public class YellowNumber extends TabFeature {
@Override
public void unload() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()){
if (isDisabledPlayer(p)) continue;
if (isDisabledPlayer(p) || p.isBedrockPlayer()) continue;
p.sendCustomPacket(new PacketPlayOutScoreboardObjective(OBJECTIVE_NAME), this);
}
}
@@ -88,17 +89,15 @@ public class YellowNumber extends TabFeature {
addDisabledPlayer(connectedPlayer);
return;
}
connectedPlayer.sendCustomPacket(new PacketPlayOutScoreboardObjective(0, OBJECTIVE_NAME, TITLE, displayType), this);
connectedPlayer.sendCustomPacket(new PacketPlayOutScoreboardDisplayObjective(DISPLAY_SLOT, OBJECTIVE_NAME), this);
if (!connectedPlayer.isBedrockPlayer()) {
connectedPlayer.sendCustomPacket(new PacketPlayOutScoreboardObjective(0, OBJECTIVE_NAME, TITLE, displayType), this);
connectedPlayer.sendCustomPacket(new PacketPlayOutScoreboardDisplayObjective(DISPLAY_SLOT, OBJECTIVE_NAME), this);
}
int value = getValue(connectedPlayer);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()){
if (all == connectedPlayer) {
connectedPlayer.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, connectedPlayer.getNickname(), value), this);
continue;
}
if (!isDisabledPlayer(all)) {
all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, connectedPlayer.getNickname(), value), this);
connectedPlayer.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, all.getNickname(), getValue(all)), this);
if (!all.isBedrockPlayer()) all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, connectedPlayer.getNickname(), value), this);
if (!connectedPlayer.isBedrockPlayer()) connectedPlayer.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, all.getNickname(), getValue(all)), this);
}
}
}
@@ -119,7 +118,7 @@ public class YellowNumber extends TabFeature {
removeDisabledPlayer(p);
}
if (disabledNow && !disabledBefore) {
p.sendCustomPacket(new PacketPlayOutScoreboardObjective(OBJECTIVE_NAME), this);
if (!p.isBedrockPlayer()) p.sendCustomPacket(new PacketPlayOutScoreboardObjective(OBJECTIVE_NAME), this);
}
if (!disabledNow && disabledBefore) {
onJoin(p);
@@ -132,7 +131,7 @@ public class YellowNumber extends TabFeature {
public void refresh(TabPlayer refreshed, boolean force) {
int value = getValue(refreshed);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (isDisabledPlayer(all)) continue;
if (isDisabledPlayer(all) || all.isBedrockPlayer()) continue;
all.sendCustomPacket(new PacketPlayOutScoreboardScore(Action.CHANGE, OBJECTIVE_NAME, refreshed.getNickname(), value), this);
}
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
@@ -141,7 +140,7 @@ public class YellowNumber extends TabFeature {
@Override
public void onLoginPacket(TabPlayer packetReceiver) {
if (isDisabledPlayer(packetReceiver)) return;
if (isDisabledPlayer(packetReceiver) || packetReceiver.isBedrockPlayer()) return;
packetReceiver.sendCustomPacket(new PacketPlayOutScoreboardObjective(0, OBJECTIVE_NAME, TITLE, displayType), this);
packetReceiver.sendCustomPacket(new PacketPlayOutScoreboardDisplayObjective(DISPLAY_SLOT, OBJECTIVE_NAME), this);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()){
@@ -1,5 +1,6 @@
package me.neznamy.tab.shared.features.alignedplayerlist;
import me.neznamy.tab.api.Property;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
@@ -72,9 +73,12 @@ public class PlayerView {
public synchronized IChatBaseComponent formatName(TabPlayer target) {
Integer width = playerWidths.get(target);
if (width == null) return null; //in packet reader, not loaded yet, will send packet after loading player
String prefixAndName = target.getProperty(TabConstants.Property.TABPREFIX).getFormat(viewer) +
target.getProperty(TabConstants.Property.CUSTOMTABNAME).getFormat(viewer);
String suffix = target.getProperty(TabConstants.Property.TABSUFFIX).getFormat(viewer);
Property prefixPr = target.getProperty(TabConstants.Property.TABPREFIX);
Property namePr = target.getProperty(TabConstants.Property.CUSTOMTABNAME);
Property suffixPr = target.getProperty(TabConstants.Property.TABSUFFIX);
if (prefixPr == null || namePr == null || suffixPr == null) return null; // no idea why is another check needed
String prefixAndName = prefixPr.getFormat(viewer) + namePr.getFormat(viewer);
String suffix = suffixPr.getFormat(viewer);
if (suffix.length() == 0) return IChatBaseComponent.optimizedComponent(prefixAndName);
if ((target.isVanished() && !canSeeVanished) || width > maxWidth) {
//tab sending packets for vanished players or player just unvanished
@@ -170,7 +170,10 @@ public class BossBarLine implements BossBar {
* @return parsed progress
*/
public float parseProgress(String progress) {
return TAB.getInstance().getErrorManager().parseFloat(progress, 100);
float value = TAB.getInstance().getErrorManager().parseFloat(progress, 100);
if (value < 0) value = 0;
if (value > 100) value = 100;
return value;
}
@Override
@@ -36,7 +36,7 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
private final List<BossBar> announcements = new ArrayList<>();
//players with toggled BossBar
private final List<String> bossBarOffPlayers;
private final List<String> bossBarOffPlayers = rememberToggleChoice ? TAB.getInstance().getConfiguration().getPlayerDataFile().getStringList("bossbar-off", new ArrayList<>()) : Collections.emptyList();
//time when BossBar announce ends, used for placeholder
private long announceEndTime;
@@ -54,11 +54,6 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
if (!line.isAnnouncementBar()) defaultBars.add(bar.toString());
}
lineValues = lines.values().toArray(new BossBar[0]);
if (rememberToggleChoice) {
bossBarOffPlayers = TAB.getInstance().getConfiguration().getPlayerDataFile().getStringList("bossbar-off", new ArrayList<>());
} else {
bossBarOffPlayers = Collections.emptyList();
}
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.COUNTDOWN, 100, () -> (announceEndTime - System.currentTimeMillis()) / 1000);
TAB.getInstance().debug(String.format("Loaded BossBar feature with parameters disabledWorlds=%s, disabledServers=%s, toggleCommand=%s, defaultBars=%s, hiddenByDefault=%s, remember_toggle_choice=%s",
Arrays.toString(disabledWorlds), Arrays.toString(disabledServers), toggleCommand, defaultBars, hiddenByDefault, rememberToggleChoice));
@@ -111,6 +106,7 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
line.removePlayer(p); //remove all BossBars and then resend them again to keep them displayed in defined order
}
showBossBars(p, defaultBars);
showBossBars(p, announcements.stream().map(BossBar::getName).collect(Collectors.toList()));
}
@Override
@@ -178,7 +174,6 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
* list of BossBars to check
*/
private void showBossBars(TabPlayer p, List<String> bars) {
if (bars == null) return;
for (String defaultBar : bars) {
BossBarLine bar = (BossBarLine) lines.get(defaultBar);
if (bar.isConditionMet(p) && !bar.containsPlayer(p)) {
@@ -277,18 +272,19 @@ public class BossBarManagerImpl extends TabFeature implements BossBarManager {
public void announceBossBar(String bossBar, int duration) {
BossBar line = lines.get(bossBar);
if (line == null) throw new IllegalArgumentException("No registered BossBar found with name " + bossBar);
List<TabPlayer> players = Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(
p -> !isDisabledPlayer(p) && hasBossBarVisible(p)).collect(Collectors.toList());
TAB.getInstance().getCPUManager().runTask(() -> {
TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.COUNTDOWN).markAsUsed();
announcements.add(line);
announceEndTime = System.currentTimeMillis() + duration* 1000L;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!hasBossBarVisible(all)) continue;
for (TabPlayer all : players) {
if (((BossBarLine)line).isConditionMet(all)) line.addPlayer(all);
}
});
TAB.getInstance().getCPUManager().runTaskLater(duration*1000,
this, "Removing announced BossBar", () -> {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (!hasBossBarVisible(all)) continue;
for (TabPlayer all : players) {
line.removePlayer(all);
}
announcements.remove(line);
@@ -6,6 +6,7 @@ import java.util.Map.Entry;
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.ServerPlaceholder;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
@@ -25,12 +26,19 @@ public class GlobalPlayerList extends TabFeature {
private final boolean vanishedAsSpectators = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.display-vanished-players-as-spectators", true);
private final boolean isolateUnlistedServers = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.isolate-unlisted-servers", false);
private final boolean fillProfileKey = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.fill-profile-key", false);
private final List<ServerPlaceholder> placeholders = new ArrayList<>();
private final Map<TabPlayer, Long> lastServerSwitch = new WeakHashMap<>();
private final UUID EMPTY_ID = new UUID(0, 0);
public GlobalPlayerList() {
super("Global PlayerList", null);
boolean updateLatency = TAB.getInstance().getConfiguration().getConfig().getBoolean("global-playerlist.update-latency", false);
if (updateLatency) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST_LATENCY, new LatencyRefresher());
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList(TabConstants.Placeholder.VANISHED));
for (Entry<String, List<String>> entry : sharedServers.entrySet()) {
placeholders.add(TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(TabConstants.Placeholder.globalPlayerListGroup(entry.getKey()), -1,
() -> Arrays.stream(TAB.getInstance().getOnlinePlayers()).filter(p -> entry.getValue().contains(p.getServer()) && !p.isVanished()).count()));
}
TAB.getInstance().debug(String.format("Loaded GlobalPlayerList feature with parameters spyServers=%s, sharedServers=%s, displayAsSpectators=%s, vanishedAsSpectators=%s, isolateUnlistedServers=%s, updateLatency=%s",
spyServers, sharedServers, displayAsSpectators, vanishedAsSpectators, isolateUnlistedServers, updateLatency));
}
@@ -43,18 +51,19 @@ public class GlobalPlayerList extends TabFeature {
if (shouldSee(viewer, displayed)) viewer.sendCustomPacket(getAddPacket(displayed, viewer), this);
}
}
placeholders.forEach(pl -> pl.updateValue(pl.request()));
}
public boolean shouldSee(TabPlayer viewer, TabPlayer displayed) {
if (displayed == viewer) return true;
if (displayed.isVanished() && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) return false;
if (spyServers.contains(viewer.getServer())) return true;
if (isSpyServer(viewer.getServer())) return true;
return getServerGroup(viewer.getServer()).equals(getServerGroup(displayed.getServer()));
}
public String getServerGroup(String serverName) {
for (Entry<String, List<String>> group : sharedServers.entrySet()) {
if (group.getValue().contains(serverName)) return group.getKey();
if (group.getValue().stream().anyMatch(serverName::equalsIgnoreCase)) return group.getKey();
}
return isolateUnlistedServers ? "isolated:" + serverName : "DEFAULT";
}
@@ -80,6 +89,7 @@ public class GlobalPlayerList extends TabFeature {
connectedPlayer.sendCustomPacket(getAddPacket(all, connectedPlayer), this);
}
}
placeholders.forEach(pl -> pl.updateValue(pl.request()));
}
@Override
@@ -93,11 +103,14 @@ public class GlobalPlayerList extends TabFeature {
if (all == disconnectedPlayer) continue;
all.sendCustomPacket(remove, this);
}
//update after player is removed
placeholders.forEach(pl -> pl.updateValue(pl.request()));
});
}
@Override
public void onServerChange(TabPlayer p, String from, String to) {
lastServerSwitch.put(p, System.currentTimeMillis());
Runnable r = () -> {
PacketPlayOutPlayerInfo removeChanged = getRemovePacket(p);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
@@ -119,12 +132,12 @@ public class GlobalPlayerList extends TabFeature {
} else {
r.run();
}
placeholders.forEach(pl -> pl.updateValue(pl.request()));
}
public PacketPlayOutPlayerInfo getRemovePacket(TabPlayer p) {
PlayerInfoData data = new PlayerInfoData(p.getTablistUUID());
data.setName(p.getName());
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, data);
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER,
new PlayerInfoData(p.getTablistUUID()), new PlayerInfoData(EMPTY_ID));
}
public PacketPlayOutPlayerInfo getAddPacket(TabPlayer p, TabPlayer viewer) {
@@ -138,9 +151,11 @@ public class GlobalPlayerList extends TabFeature {
p.getName(),
p.getTablistUUID(),
p.getSkin(),
true,
p.getPing(),
vanishedAsSpectators && p.isVanished() ? EnumGamemode.SPECTATOR : EnumGamemode.CREATIVE,
viewer.getVersion().getMinorVersion() >= 8 ? format : null,
fillProfileKey ? p.getChatSessionId() : null,
fillProfileKey ? p.getProfilePublicKey() : null
)
);
@@ -148,11 +163,14 @@ public class GlobalPlayerList extends TabFeature {
@Override
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
if (info.getAction() == EnumPlayerInfoAction.REMOVE_PLAYER) {
if (info.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
boolean packetFromTAB = info.getEntries().stream().anyMatch(data -> data.getUniqueId().equals(EMPTY_ID));
for (PlayerInfoData playerInfoData : info.getEntries()) {
TabPlayer packetPlayer = TAB.getInstance().getPlayerByTabListUUID(playerInfoData.getUniqueId());
//not preventing NPC removals
if (packetPlayer != null && (playerInfoData.getName() == null || playerInfoData.getName().length() == 0) && !packetPlayer.isVanished()) {
if (packetPlayer != null && !packetFromTAB && !packetPlayer.isVanished()
&& (System.currentTimeMillis()-lastServerSwitch.getOrDefault(packetPlayer, 0L) < 2000)
) {
//remove packet not coming from tab
//changing to random non-existing player, the easiest way to cancel the removal
playerInfoData.setUniqueId(UUID.randomUUID());
@@ -160,7 +178,7 @@ public class GlobalPlayerList extends TabFeature {
}
}
if (!displayAsSpectators) return;
if (info.getAction() == EnumPlayerInfoAction.ADD_PLAYER || info.getAction() == EnumPlayerInfoAction.UPDATE_GAME_MODE) {
if (info.getActions().contains(EnumPlayerInfoAction.UPDATE_GAME_MODE)) {
for (PlayerInfoData playerInfoData : info.getEntries()) {
TabPlayer packetPlayer = TAB.getInstance().getPlayerByTabListUUID(playerInfoData.getUniqueId());
if (packetPlayer != null && !receiver.getServer().equals(packetPlayer.getServer())) {
@@ -189,7 +207,7 @@ public class GlobalPlayerList extends TabFeature {
}
}
public List<String> getSpyServers() {
return spyServers;
public boolean isSpyServer(String server) {
return spyServers.stream().anyMatch(server::equalsIgnoreCase);
}
}
@@ -4,6 +4,7 @@ import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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;
@@ -42,15 +43,19 @@ public class Layout extends TabFeature {
groups.forEach(g -> list.addAll(g.getSlots(p)));
for (FixedSlot slot : fixedSlots.values()) {
p.setProperty(slot, slot.getPropertyName(), slot.getText());
list.add(new PlayerInfoData("", slot.getId(), slot.getSkin(), slot.getPing(), EnumGamemode.CREATIVE, IChatBaseComponent.optimizedComponent(p.getProperty(slot.getPropertyName()).updateAndGet()), null));
list.add(new PlayerInfoData(getEntryName(p, slot.getId().getLeastSignificantBits()), slot.getId(), slot.getSkin(), true, slot.getPing(), EnumGamemode.CREATIVE, IChatBaseComponent.optimizedComponent(p.getProperty(slot.getPropertyName()).updateAndGet()), null, null));
}
for (int slot : emptySlots) {
list.add(new PlayerInfoData("", manager.getUUID(slot), manager.getSkinManager().getDefaultSkin(), manager.getEmptySlotPing(), EnumGamemode.CREATIVE, new IChatBaseComponent(""), null));
list.add(new PlayerInfoData(getEntryName(p, slot), manager.getUUID(slot), manager.getSkinManager().getDefaultSkin(), true, manager.getEmptySlotPing(), EnumGamemode.CREATIVE, new IChatBaseComponent(""), null, null));
}
if (p.getVersion().getMinorVersion() < 8 || p.isBedrockPlayer()) return;
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, list), this);
}
public String getEntryName(TabPlayer viewer, long slot) {
return viewer.getVersion().getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId() ? "|slot_" + (10+slot) : "";
}
public void removeFrom(TabPlayer p) {
if (!viewers.contains(p)) return;
viewers.remove(p);
@@ -9,10 +9,10 @@ import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.layout.skin.SkinManager;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.placeholders.conditions.Condition;
public class LayoutManager extends TabFeature {
@@ -24,7 +24,6 @@ public class LayoutManager extends TabFeature {
private final int emptySlotPing = TAB.getInstance().getConfiguration().getLayout().getInt("empty-slot-ping-value", 1000);
private final boolean hideVanishedPlayers = TAB.getInstance().getConfiguration().getLayout().getBoolean("hide-vanished-players", true);
private final SkinManager skinManager = new SkinManager(defaultSkin);
private final Map<Integer, UUID> uuids = new HashMap<Integer, UUID>(){{
for (int slot=1; slot<=80; slot++) {
put(slot, new UUID(0, translateSlot(slot)));
@@ -32,7 +31,9 @@ public class LayoutManager extends TabFeature {
}};
private final Map<String, Layout> layouts = loadLayouts();
private final WeakHashMap<TabPlayer, Layout> playerViews = new WeakHashMap<>();
private final Map<TabPlayer, String> sortedPlayers = Collections.synchronizedMap(new TreeMap<>(Comparator.comparing(TabPlayer::getTeamName)));
private final WeakHashMap<TabPlayer, String> teamNames = new WeakHashMap<>();
private final Map<TabPlayer, String> sortedPlayers = Collections.synchronizedMap(new TreeMap<>(Comparator.comparing(teamNames::get)));
private final Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
public LayoutManager() {
super("Layout", "Switching layouts");
@@ -66,14 +67,18 @@ public class LayoutManager extends TabFeature {
}
for (String fixedSlot : (List<String>)map.getOrDefault("fixed-slots", Collections.emptyList())) {
String[] array = fixedSlot.split("\\|");
int slot = Integer.parseInt(array[0]);
String text = array[1];
String skin = array.length > 2 ? array[2] : "";
int ping = array.length > 3 ? TAB.getInstance().getErrorManager().parseInteger(array[3], emptySlotPing) : emptySlotPing;
FixedSlot f = new FixedSlot(l, slot, text, skin, ping);
fixedSlots.put(slot, f);
emptySlots.remove((Integer)slot);
if (text.length() > 0) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.layoutSlot(layout.getKey().toString(), slot), f);
try {
int slot = Integer.parseInt(array[0]);
String text = array[1];
String skin = array.length > 2 ? array[2] : "";
int ping = array.length > 3 ? TAB.getInstance().getErrorManager().parseInteger(array[3], emptySlotPing) : emptySlotPing;
FixedSlot f = new FixedSlot(l, slot, text, skin, ping);
fixedSlots.put(slot, f);
emptySlots.remove((Integer)slot);
if (text.length() > 0) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.layoutSlot(layout.getKey().toString(), slot), f);
} catch (NumberFormatException e) {
TAB.getInstance().getErrorManager().startupWarn("Layout \"" + layout.getKey() + "\"'s fixed slot line \"" + fixedSlot + "&c\" has invalid number as slot.");
}
}
Map<String, Map<String, Object>> groups = (Map<String, Map<String, Object>>) map.get("groups");
if (groups != null) {
@@ -83,7 +88,7 @@ public class LayoutManager extends TabFeature {
for (String line : (List<String>) group.getValue().get("slots")) {
String[] arr = line.split("-");
int from = Integer.parseInt(arr[0]);
int to = Integer.parseInt(arr[1]);
int to = arr.length == 1 ? from : Integer.parseInt(arr[1]);
for (int i = from; i<= to; i++) {
positions.add(i);
}
@@ -100,12 +105,15 @@ public class LayoutManager extends TabFeature {
@Override
public void onJoin(TabPlayer p) {
sortedPlayers.put(p, p.getTeamName());
teamNames.put(p, sorting.getFullTeamName(p));
sortedPlayers.put(p, sorting.getFullTeamName(p));
Layout highest = getHighestLayout(p);
if (highest != null) highest.sendTo(p);
playerViews.put(p, highest);
layouts.values().forEach(Layout::tick);
// Unformat original entries for players who can see a layout to avoid spaces due to unparsed placeholders and such
if (highest == null) return;
List<PlayerInfoData> data = new ArrayList<>();
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
data.add(new PlayerInfoData(all.getTablistUUID()));
@@ -116,6 +124,7 @@ public class LayoutManager extends TabFeature {
@Override
public void onQuit(TabPlayer p) {
sortedPlayers.remove(p);
teamNames.remove(p);
layouts.values().forEach(Layout::tick);
}
@@ -179,7 +188,7 @@ public class LayoutManager extends TabFeature {
public void updateTeamName(TabPlayer p, String teamName) {
sortedPlayers.remove(p);
((ITabPlayer) p).setTeamName(teamName);
teamNames.put(p, teamName);
sortedPlayers.put(p, teamName);
layouts.values().forEach(Layout::tick);
}
@@ -45,9 +45,9 @@ public class PlayerSlot {
PlayerInfoData data;
TabPlayer player = this.player; //avoiding NPE from concurrent access
if (player != null) {
data = new PlayerInfoData("", id, player.getSkin(), player.getPing(), EnumGamemode.SURVIVAL, playerlist == null ? new IChatBaseComponent(player.getName()) : playerlist.getTabFormat(player, p), null);
data = new PlayerInfoData(layout.getEntryName(p, id.getLeastSignificantBits()), id, player.getSkin(), true, player.getPing(), EnumGamemode.SURVIVAL, playerlist == null ? new IChatBaseComponent(player.getName()) : playerlist.getTabFormat(player, p), null, null);
} else {
data = new PlayerInfoData("", id, layout.getManager().getSkinManager().getDefaultSkin(), layout.getManager().getEmptySlotPing(), EnumGamemode.SURVIVAL, new IChatBaseComponent(text), null);
data = new PlayerInfoData(layout.getEntryName(p, id.getLeastSignificantBits()), id, layout.getManager().getSkinManager().getDefaultSkin(), true, layout.getManager().getEmptySlotPing(), EnumGamemode.SURVIVAL, new IChatBaseComponent(text), null, null);
}
return data;
}
@@ -10,7 +10,6 @@ import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.TabExpansion;
import me.neznamy.tab.shared.features.redis.RedisSupport;
@@ -19,10 +18,10 @@ import me.neznamy.tab.shared.features.sorting.Sorting;
public class NameTag extends TabFeature implements TeamManager {
private final boolean invisibleNameTags = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard-teams.invisible-nametags", false);
protected final boolean invisibleNameTags = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard-teams.invisible-nametags", false);
private final boolean collisionRule = TAB.getInstance().getConfiguration().getConfig().getBoolean("scoreboard-teams.enable-collision", true);
private final boolean canSeeFriendlyInvisibles = TAB.getInstance().getConfig().getBoolean("scoreboard-teams.can-see-friendly-invisibles", false);
private final Sorting sorting = new Sorting(this);
private final Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
private final CollisionManager collisionManager = new CollisionManager(this, collisionRule);
private final Set<TabPlayer> hiddenNameTag = Collections.newSetFromMap(new WeakHashMap<>());
@@ -49,7 +48,6 @@ public class NameTag extends TabFeature implements TeamManager {
public void load(){
TabExpansion expansion = TAB.getInstance().getPlaceholderManager().getTabExpansion();
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
((ITabPlayer) all).setTeamName(getSorting().getTeamName(all));
updateProperties(all);
hiddenNameTagFor.put(all, new ArrayList<>());
if (isDisabled(all.getServer(), all.getWorld())) {
@@ -64,7 +62,7 @@ public class NameTag extends TabFeature implements TeamManager {
@Override
public void unload() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (!isDisabledPlayer(p)) unregisterTeam(p);
if (!isDisabledPlayer(p)) unregisterTeam(p, sorting.getShortTeamName(p));
}
}
@@ -93,7 +91,7 @@ public class NameTag extends TabFeature implements TeamManager {
@Override
public void onJoin(TabPlayer connectedPlayer) {
((ITabPlayer) connectedPlayer).setTeamName(getSorting().getTeamName(connectedPlayer));
sorting.constructTeamNames(connectedPlayer);
updateProperties(connectedPlayer);
hiddenNameTagFor.put(connectedPlayer, new ArrayList<>());
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
@@ -116,7 +114,7 @@ public class NameTag extends TabFeature implements TeamManager {
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
if (!isDisabledPlayer(disconnectedPlayer) && !hasTeamHandlingPaused(disconnectedPlayer)) {
PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam(disconnectedPlayer.getTeamName());
PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam(sorting.getShortTeamName(disconnectedPlayer));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer == disconnectedPlayer) continue; //player who just disconnected
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.NAMETAGS_TEAM_UNREGISTER);
@@ -146,7 +144,7 @@ public class NameTag extends TabFeature implements TeamManager {
}
boolean changed = updateProperties(p);
if (disabledNow && !disabledBefore) {
unregisterTeam(p);
unregisterTeam(p, sorting.getShortTeamName(p));
} else if (!disabledNow && disabledBefore) {
registerTeam(p);
} else if (changed) {
@@ -195,7 +193,7 @@ public class NameTag extends TabFeature implements TeamManager {
@Override
public void pauseTeamHandling(TabPlayer player) {
if (teamHandlingPaused.contains(player)) return;
if (!isDisabledPlayer(player)) unregisterTeam(player);
if (!isDisabledPlayer(player)) unregisterTeam(player, sorting.getShortTeamName(player));
teamHandlingPaused.add(player); //adding after, so unregisterTeam method runs
}
@@ -215,12 +213,12 @@ public class NameTag extends TabFeature implements TeamManager {
public void forceTeamName(TabPlayer player, String name) {
if (Objects.equals(forcedTeamName.get(player), name)) return;
if (name != null && name.length() > 16) throw new IllegalArgumentException("Team name cannot be more than 16 characters long.");
unregisterTeam(player);
unregisterTeam(player, sorting.getShortTeamName(player));
forcedTeamName.put(player, name);
registerTeam(player);
if (name != null) ((ITabPlayer)player).setTeamNameNote("Set using API");
if (name != null) sorting.setTeamNameNote(player, "Set using API");
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
if (redis != null) redis.updateTeamName(player, player.getTeamName());
if (redis != null) redis.updateTeamName(player, sorting.getShortTeamName(player));
}
@Override
@@ -246,7 +244,7 @@ public class NameTag extends TabFeature implements TeamManager {
String currentPrefix = tagPrefix.getFormat(viewer);
String currentSuffix = tagSuffix.getFormat(viewer);
boolean visible = getTeamVisibility(p, viewer);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), getTeamOptions()), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(sorting.getShortTeamName(p), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), getTeamOptions()), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
}
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
if (redis != null) redis.updateNameTag(p, p.getProperty(TabConstants.Property.TAGPREFIX).get(), p.getProperty(TabConstants.Property.TAGSUFFIX).get());
@@ -256,13 +254,13 @@ public class NameTag extends TabFeature implements TeamManager {
boolean visible = getTeamVisibility(p, viewer);
String currentPrefix = p.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
String currentSuffix = p.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), getTeamOptions()), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(sorting.getShortTeamName(p), currentPrefix, currentSuffix, translate(visible), translate(collisionManager.getCollision(p)), getTeamOptions()), TabConstants.PacketCategory.NAMETAGS_TEAM_UPDATE);
}
public void unregisterTeam(TabPlayer p) {
if (hasTeamHandlingPaused(p) || p.getTeamName() == null) return;
public void unregisterTeam(TabPlayer p, String teamName) {
if (hasTeamHandlingPaused(p) || sorting.getShortTeamName(p) == null) return;
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName()), TabConstants.PacketCategory.NAMETAGS_TEAM_UNREGISTER);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName), TabConstants.PacketCategory.NAMETAGS_TEAM_UNREGISTER);
}
}
@@ -276,23 +274,22 @@ public class NameTag extends TabFeature implements TeamManager {
if (hasTeamHandlingPaused(p)) return;
String replacedPrefix = p.getProperty(TabConstants.Property.TAGPREFIX).getFormat(viewer);
String replacedSuffix = p.getProperty(TabConstants.Property.TAGSUFFIX).getFormat(viewer);
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName(), replacedPrefix, replacedSuffix, translate(getTeamVisibility(p, viewer)),
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(sorting.getShortTeamName(p), replacedPrefix, replacedSuffix, translate(getTeamVisibility(p, viewer)),
translate(collisionManager.getCollision(p)), Collections.singletonList(p.getNickname()), getTeamOptions()), TabConstants.PacketCategory.NAMETAGS_TEAM_REGISTER);
}
private void updateTeam(TabPlayer p) {
if (p.getTeamName() == null) return; //player not loaded yet
String newName = getSorting().getTeamName(p);
if (p.getTeamName().equals(newName)) {
String oldName = getSorting().getShortTeamName(p);
getSorting().constructTeamNames(p);
if (oldName.equals(sorting.getShortTeamName(p))) {
updateTeamData(p);
} else {
unregisterTeam(p);
unregisterTeam(p, oldName);
LayoutManager layout = (LayoutManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
if (layout != null) layout.updateTeamName(p, newName);
((ITabPlayer) p).setTeamName(newName);
if (layout != null) layout.updateTeamName(p, sorting.getFullTeamName(p));
registerTeam(p);
RedisSupport redis = (RedisSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
if (redis != null) redis.updateTeamName(p, p.getTeamName());
if (redis != null) redis.updateTeamName(p, sorting.getShortTeamName(p));
}
}
@@ -36,6 +36,11 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
public NameTagX(BiFunction<NameTagX, TabPlayer, ArmorStandManager> armorStandFunction) {
this.armorStandFunction = armorStandFunction;
Collections.reverse(dynamicLines);
if (invisibleNameTags) {
TAB.getInstance().getErrorManager().startupWarn("Unlimited nametag mode is enabled as well as invisible nametags. These 2 options are mutually exclusive.");
TAB.getInstance().getErrorManager().startupWarn("If you want nametags to be invisible, you don't need unlimited nametag mode at all.");
TAB.getInstance().getErrorManager().startupWarn("If you want enhanced nametags without limits, making them invisible would defeat the purpose.");
}
TAB.getInstance().debug(String.format("Loaded Unlimited NameTag feature with parameters markerFor18x=%s, disableOnBoats=%s, disabledUnlimitedServers=%s, disabledUnlimitedWorlds=%s",
markerFor18x, disableOnBoats, disabledUnlimitedServers, disabledUnlimitedWorlds));
}
@@ -350,7 +355,7 @@ public abstract class NameTagX extends NameTag implements UnlimitedNametagManage
public void pauseTeamHandling(TabPlayer player) {
Preconditions.checkLoaded(player);
if (teamHandlingPaused.contains(player)) return;
if (!isDisabledPlayer(player)) unregisterTeam(player);
if (!isDisabledPlayer(player)) unregisterTeam(player, getSorting().getShortTeamName(player));
teamHandlingPaused.add(player); //adding after, so unregisterTeam method runs
pauseArmorStands(player);
}
@@ -73,7 +73,7 @@ public class RedisPlayer {
private static String checkTeamName(RedisSupport redis, String currentName15, int id) {
String potentialTeamName = currentName15 + (char)id;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.getTeamName() != null && all.getTeamName().equals(potentialTeamName)) {
if (redis.getSorting() != null && redis.getSorting().getShortTeamName(all).equals(potentialTeamName)) {
return checkTeamName(redis, currentName15, id+1);
}
}
@@ -110,7 +110,7 @@ public class RedisPlayer {
if (p.getProperty(TabConstants.Property.YELLOW_NUMBER) != null) {
json.put("yellow-number", p.getProperty(TabConstants.Property.YELLOW_NUMBER).get());
}
json.put("teamname", p.getTeamName());
json.put("teamname", redis.getSorting() == null ? null : redis.getSorting().getShortTeamName(p));
json.put("vanished", p.isVanished());
json.put("staff", p.hasPermission(TabConstants.Permission.STAFF));
if (p.getSkin() != null) {
@@ -121,8 +121,8 @@ public class RedisPlayer {
}
public PacketPlayOutPlayerInfo getAddPacket() {
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, new PlayerInfoData(nickname, uniqueId, skin, 0, EnumGamemode.SURVIVAL,
disabledPlayerList ? null : IChatBaseComponent.optimizedComponent(tabFormat), null));
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, new PlayerInfoData(nickname, uniqueId, skin, true, 0, EnumGamemode.SURVIVAL,
disabledPlayerList ? null : IChatBaseComponent.optimizedComponent(tabFormat), null, null));
}
public PacketPlayOutPlayerInfo getUpdatePacket() {
@@ -10,6 +10,7 @@ import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.PlayerList;
import me.neznamy.tab.shared.features.globalplayerlist.GlobalPlayerList;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.sorting.Sorting;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
@@ -39,6 +40,13 @@ public abstract class RedisSupport extends TabFeature {
/** NameTag feature */
private final NameTag nameTags = (NameTag) TAB.getInstance().getTeamManager();
/** Sorting feature */
private final Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
private final UUID EMPTY_ID = new UUID(0, 0);
private final Map<RedisPlayer, Long> lastServerSwitch = new WeakHashMap<>();
/**
* Constructs new instance
*/
@@ -226,6 +234,7 @@ public abstract class RedisSupport extends TabFeature {
target.setServer(server);
return;
}
lastServerSwitch.put(target, System.currentTimeMillis());
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
boolean before = shouldSee(viewer, target.getServer(), target.isVanished());
@@ -331,7 +340,7 @@ public abstract class RedisSupport extends TabFeature {
private boolean shouldSee(TabPlayer viewer, String viewerServer, String server, boolean targetVanished) {
if (targetVanished && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) return false;
if (global.getSpyServers().contains(viewerServer)) return true;
if (global.isSpyServer(viewerServer)) return true;
return global.getServerGroup(viewerServer).equals(global.getServerGroup(server));
}
@@ -413,21 +422,24 @@ public abstract class RedisSupport extends TabFeature {
@Override
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
if (info.getAction() == PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER && global != null) {
if (info.getActions().contains(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER) && global != null) {
boolean packetFromTAB = info.getEntries().stream().anyMatch(data -> data.getUniqueId().equals(EMPTY_ID));
for (PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData : info.getEntries()) {
RedisPlayer packetPlayer = redisPlayers.get(playerInfoData.getUniqueId().toString());
if (packetPlayer != null && (playerInfoData.getName() == null || playerInfoData.getName().length() == 0) && !packetPlayer.isVanished()) {
if (packetPlayer != null && !packetFromTAB && !packetPlayer.isVanished() &&
(System.currentTimeMillis()-lastServerSwitch.getOrDefault(packetPlayer, 0L) < 500)) {
//remove packet not coming from tab
//changing to random non-existing player, the easiest way to cancel the removal
playerInfoData.setUniqueId(UUID.randomUUID());
}
}
}
if (info.getAction() != PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME && info.getAction() != PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER) return;
for (PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData : info.getEntries()) {
RedisPlayer packetPlayer = redisPlayers.get(playerInfoData.getUniqueId().toString());
if (packetPlayer != null && !packetPlayer.hasDisabledPlayerlist()) {
playerInfoData.setDisplayName(IChatBaseComponent.optimizedComponent(packetPlayer.getTabFormat()));
if (info.getActions().contains(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_GAME_MODE)) {
for (PacketPlayOutPlayerInfo.PlayerInfoData playerInfoData : info.getEntries()) {
RedisPlayer packetPlayer = redisPlayers.get(playerInfoData.getUniqueId().toString());
if (packetPlayer != null && !packetPlayer.hasDisabledPlayerlist()) {
playerInfoData.setDisplayName(IChatBaseComponent.optimizedComponent(packetPlayer.getTabFormat()));
}
}
}
}
@@ -440,4 +452,8 @@ public abstract class RedisSupport extends TabFeature {
}
}
}
public Sorting getSorting() {
return sorting;
}
}
@@ -170,7 +170,7 @@ public class ScoreboardImpl extends TabFeature implements Scoreboard {
@Override
public void refresh(TabPlayer refreshed, boolean force) {
if (refreshed.getProperty(TabConstants.Property.SCOREBOARD_TITLE) == null) return;
if (!players.contains(refreshed)) return;
refreshed.sendCustomPacket(new PacketPlayOutScoreboardObjective(2, ScoreboardManagerImpl.OBJECTIVE_NAME,
refreshed.getProperty(TabConstants.Property.SCOREBOARD_TITLE).updateAndGet(), EnumScoreboardHealthDisplay.INTEGER), TabConstants.PacketCategory.SCOREBOARD_TITLE);
}
@@ -240,4 +240,8 @@ public class ScoreboardImpl extends TabFeature implements Scoreboard {
}
}
}
public void removePlayerFromSet(TabPlayer player) {
players.remove(player);
}
}
@@ -380,7 +380,7 @@ public class ScoreboardManagerImpl extends TabFeature implements ScoreboardManag
otherPluginScoreboard.remove(packetReceiver);
ScoreboardImpl scoreboard = activeScoreboard.get(packetReceiver);
if (scoreboard != null) {
scoreboard.removePlayer(packetReceiver);
scoreboard.removePlayerFromSet(packetReceiver);
scoreboard.addPlayer(packetReceiver);
}
}
@@ -6,6 +6,8 @@ import java.util.stream.Collectors;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
@@ -27,7 +29,8 @@ import me.neznamy.tab.shared.features.sorting.types.SortingType;
*/
public class Sorting extends TabFeature {
private final NameTag nameTags;
private NameTag nameTags;
private LayoutManager layout;
//map of all registered sorting types
private final Map<String, BiFunction<Sorting, String, SortingType>> types = new LinkedHashMap<>();
@@ -37,16 +40,17 @@ public class Sorting extends TabFeature {
//active sorting types
private final SortingType[] usedSortingTypes;
//team names and notes
private final WeakHashMap<TabPlayer, String> shortTeamNames = new WeakHashMap<>();
private final WeakHashMap<TabPlayer, String> fullTeamNames = new WeakHashMap<>();
private final WeakHashMap<TabPlayer, String> teamNameNotes = new WeakHashMap<>();
/**
* Constructs new instance, loads data from configuration and starts repeating task
*
* @param nameTags
* NameTag feature
* Constructs new instance and loads config options
*/
public Sorting(NameTag nameTags) {
public Sorting() {
super("Team name refreshing", "Refreshing team name");
this.nameTags = nameTags;
types.put("GROUPS", Groups::new);
types.put("PERMISSIONS", Permissions::new);
types.put("PLACEHOLDER", Placeholder::new);
@@ -54,34 +58,36 @@ public class Sorting extends TabFeature {
types.put("PLACEHOLDER_Z_TO_A", PlaceholderZtoA::new);
types.put("PLACEHOLDER_LOW_TO_HIGH", PlaceholderLowToHigh::new);
types.put("PLACEHOLDER_HIGH_TO_LOW", PlaceholderHighToLow::new);
usedSortingTypes = compile(TAB.getInstance().getConfiguration().getConfig().getStringList("scoreboard-teams.sorting-types", new ArrayList<>()));
usedSortingTypes = compile(TAB.getInstance().getConfig().getStringList("scoreboard-teams.sorting-types", new ArrayList<>()));
}
@Override
public void refresh(TabPlayer p, boolean force) {
if (nameTags != null && (nameTags.getForcedTeamName(p) != null || nameTags.hasTeamHandlingPaused(p))) return;
String newName = getTeamName(p);
if (!p.getTeamName().equals(newName)) {
if (nameTags != null) nameTags.unregisterTeam(p);
LayoutManager layout = (LayoutManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
if (layout != null) layout.updateTeamName(p, newName);
((ITabPlayer) p).setTeamName(newName);
if (nameTags != null) nameTags.registerTeam(p);
String previousShortName = shortTeamNames.get(p);
constructTeamNames(p);
if (!shortTeamNames.get(p).equals(previousShortName)) {
if (nameTags != null && nameTags.getForcedTeamName(p) == null && !nameTags.hasTeamHandlingPaused(p)) {
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(previousShortName), TabConstants.PacketCategory.SORTING_CHANGING_TEAM_NAME);
}
nameTags.registerTeam(p);
}
if (layout != null) layout.updateTeamName(p, fullTeamNames.get(p));
}
}
@Override
public void load(){
if (nameTags != null) return; //handled by NameTag feature
public void load() {
nameTags = (NameTag) TAB.getInstance().getTeamManager();
layout = (LayoutManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.LAYOUT);
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
((ITabPlayer) all).setTeamName(getTeamName(all));
constructTeamNames(all);
}
}
@Override
public void onJoin(TabPlayer connectedPlayer) {
if (nameTags != null) return; //handled by NameTag feature
((ITabPlayer) connectedPlayer).setTeamName(getTeamName(connectedPlayer));
constructTeamNames(connectedPlayer);
}
/**
@@ -89,7 +95,7 @@ public class Sorting extends TabFeature {
*
* @return list of compiled sorting types
*/
private SortingType[] compile(List<String> options){
private SortingType[] compile(List<String> options) {
List<SortingType> list = new ArrayList<>();
for (String element : options) {
String[] arr = element.split(":");
@@ -103,24 +109,32 @@ public class Sorting extends TabFeature {
}
/**
* Constructs team name for specified player
* Constructs short team names, both short (up to 16 characters long)
* and full for specified player
*
* @param p
* player to build team name for
* @return unique up to 16 character long sequence that sorts the player
*/
public String getTeamName(TabPlayer p) {
((ITabPlayer) p).setTeamNameNote("");
StringBuilder sb = new StringBuilder();
public void constructTeamNames(TabPlayer p) {
teamNameNotes.put(p, "");
StringBuilder shortName = new StringBuilder();
for (SortingType type : usedSortingTypes) {
sb.append(type.getChars((ITabPlayer) p));
shortName.append(type.getChars((ITabPlayer) p));
}
if (sb.length() > 15) {
sb.setLength(15);
StringBuilder fullName = new StringBuilder(shortName);
if (TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.LAYOUT)) {
//layout is enabled, start with max character to fix compatibility with plugins
//which add empty player into a team such as LibsDisguises
shortName.insert(0, Character.MAX_VALUE);
}
return checkTeamName(p, sb, 65);
if (shortName.length() > 15) {
shortName.setLength(15);
}
String finalShortName = checkTeamName(p, shortName, 65);
shortTeamNames.put(p, finalShortName);
fullTeamNames.put(p, fullName.append(finalShortName.charAt(finalShortName.length() - 1)).toString());
}
/**
* Checks if team name is available and proceeds to try new values until free name is found
*
@@ -133,12 +147,10 @@ public class Sorting extends TabFeature {
* @return first available full team name
*/
private String checkTeamName(TabPlayer p, StringBuilder currentName, int id) {
String potentialTeamName = currentName.toString();
if (!caseSensitiveSorting) potentialTeamName = potentialTeamName.toLowerCase();
potentialTeamName += (char)id;
String potentialTeamName = currentName.toString() + (char)id;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all == p) continue;
if (all.getTeamName() != null && all.getTeamName().equals(potentialTeamName)) {
if (shortTeamNames.get(all) != null && shortTeamNames.get(all).equals(potentialTeamName)) {
return checkTeamName(p, currentName, id+1);
}
}
@@ -159,6 +171,28 @@ public class Sorting extends TabFeature {
* @return user-friendly representation of sorting types
*/
public String typesToString() {
return Arrays.stream(usedSortingTypes).map(Object::toString).collect(Collectors.joining(" then "));
return Arrays.stream(usedSortingTypes).map(Object::toString).collect(Collectors.joining(" -> "));
}
public String getShortTeamName(TabPlayer p) {
TeamManager teams = TAB.getInstance().getTeamManager();
if (teams != null && teams.getForcedTeamName(p) != null) return teams.getForcedTeamName(p);
return shortTeamNames.get(p);
}
public String getFullTeamName(TabPlayer p) {
return fullTeamNames.get(p);
}
public String getTeamNameNote(TabPlayer p) {
return teamNameNotes.get(p);
}
public void setTeamNameNote(TabPlayer p, String note) {
teamNameNotes.put(p, note);
}
public boolean isCaseSensitiveSorting() {
return caseSensitiveSorting;
}
}
@@ -12,31 +12,27 @@ import me.neznamy.tab.shared.features.sorting.Sorting;
public class Groups extends SortingType {
//map of sorted groups in config
private final LinkedHashMap<String, String> sortedGroups;
private final LinkedHashMap<String, Integer> sortedGroups;
/**
* Constructs new instance
*/
public Groups(Sorting sorting, String options) {
super(sorting, TabConstants.Placeholder.GROUP);
super(sorting, "GROUPS", TabConstants.Placeholder.GROUP);
sortedGroups = convertSortingElements(options.split(","));
}
@Override
public String getChars(ITabPlayer p) {
String group = p.getGroup();
String chars = sortedGroups.get(group.toLowerCase());
if (chars == null) {
chars = String.valueOf(sortedGroups.size()+1);
p.setTeamNameNote(p.getTeamNameNote() + "&cPlayer's primary group is not in sorting list. &r");
String group = p.getGroup().toLowerCase();
int position;
if (!sortedGroups.containsKey(group)) {
position = sortedGroups.size() + 1;
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> &cPrimary group (&e" + p.getGroup() + "&c) is not in sorting list. &r");
} else {
p.setTeamNameNote(p.getTeamNameNote() + String.format("Primary group is #%s in sorting list", Integer.parseInt(chars)) + ". &r");
position = sortedGroups.get(group);
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> Primary group (&e" + p.getGroup() + "&r) is &a#" + position + "&r in sorting list.");
}
return chars;
}
@Override
public String toString() {
return "GROUPS";
return String.valueOf((char) (position + 47));
}
}
@@ -10,17 +10,18 @@ import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.sorting.Sorting;
/**
* Sorting by permission nodes tab.sort.&lt;name&gt;, where names are defined in sorting list
* Sorting by permission nodes
*/
public class Permissions extends SortingType {
//map of sorted groups in config
private final LinkedHashMap<String, String> sortedGroups;
//map of permissions
private final LinkedHashMap<String, Integer> sortedGroups;
/**
* Constructs new instance
*/
public Permissions(Sorting sorting, String options) {
super(sorting, "PERMISSIONS");
sortedGroups = convertSortingElements(options.split(","));
List<String> placeholders = new ArrayList<>();
for (String permission : sortedGroups.keySet()) {
@@ -33,26 +34,21 @@ public class Permissions extends SortingType {
@Override
public String getChars(ITabPlayer p) {
String chars = null;
int position = 0;
for (String permission : sortedGroups.keySet()) {
if (p.hasPermission(permission)) {
chars = sortedGroups.get(permission.toLowerCase());
p.setTeamNameNote("Highest sorting permission: &e" + permission + " &a(#" + Integer.parseInt(chars) + " in list). &r");
position = sortedGroups.get(permission.toLowerCase());
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> Highest sorting permission: &e" + permission + " &a(#" + position + " in list). &r");
if (p.hasPermission(TabConstants.Permission.TEST_PERMISSION)) {
p.setTeamNameNote(p.getTeamNameNote() + "&cThis user appears to have all permissions. Is he OP? &r");
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "&cThis user appears to have all permissions. Are they OP? &r");
}
break;
}
}
if (chars == null) {
chars = String.valueOf(sortedGroups.size()+1);
p.setTeamNameNote(p.getTeamNameNote() + "&cPlayer does not have any of the defined permissions. &r");
if (position == 0) {
position = sortedGroups.size()+1;
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> &cPlayer does not have any of the defined permissions. &r");
}
return chars;
}
@Override
public String toString() {
return "PERMISSIONS";
return String.valueOf((char) (position + 47));
}
}
@@ -13,7 +13,7 @@ import java.util.Locale;
public class Placeholder extends SortingType {
//map Value-Number where number is used in team name based on value
private final LinkedHashMap<String, String> sortingMap;
private final LinkedHashMap<String, Integer> sortingMap;
/**
* Constructs new instance with given parameters
@@ -24,7 +24,7 @@ public class Placeholder extends SortingType {
* options used by this sorting type
*/
public Placeholder(Sorting sorting, String options) {
super(sorting, getPlaceholder(options));
super(sorting, "PLACEHOLDER", getPlaceholder(options));
String[] args = options.split(":");
if (args.length > 1)
sortingMap = convertSortingElements(args[args.length-1].split(","));
@@ -50,19 +50,15 @@ public class Placeholder extends SortingType {
@Override
public String getChars(ITabPlayer p) {
String output = EnumChatFormat.color(setPlaceholders(p));
p.setTeamNameNote(p.getTeamNameNote() + sortingPlaceholder + " returned \"" + output + "\"");
String sortingValue = sortingMap.get(output.toLowerCase(Locale.US));
if (sortingValue == null) {
sortingValue = String.valueOf(sortingMap.size()+1);
p.setTeamNameNote(p.getTeamNameNote() + "&c (not in list)&r. ");
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder + " returned \"&e" + output + "&r\"");
int position;
if (!sortingMap.containsKey(output.toLowerCase(Locale.US))) {
position = sortingMap.size()+1;
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "&c (not in list)&r. ");
} else {
p.setTeamNameNote(p.getTeamNameNote() + "&r (#" + Integer.parseInt(sortingValue) + " in list). &r");
position = sortingMap.get(output.toLowerCase(Locale.US));
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "&r &a(#" + position + " in list). &r");
}
return sortingValue;
}
@Override
public String toString() {
return "PLACEHOLDER";
return String.valueOf((char) (position + 47));
}
}
@@ -15,18 +15,13 @@ public class PlaceholderAtoZ extends SortingType {
* placeholder to sort by
*/
public PlaceholderAtoZ(Sorting sorting, String sortingPlaceholder) {
super(sorting, sortingPlaceholder);
super(sorting, "PLACEHOLDER_A_TO_Z", sortingPlaceholder);
}
@Override
public String getChars(ITabPlayer p) {
String output = setPlaceholders(p);
p.setTeamNameNote(p.getTeamNameNote() + sortingPlaceholder + " returned \"" + output + "\". &r");
return output;
}
@Override
public String toString() {
return "PLACEHOLDER_A_TO_Z";
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder + " returned \"&e" + output + "&r\". &r");
return sorting.isCaseSensitiveSorting() ? output : output.toLowerCase();
}
}
@@ -16,20 +16,15 @@ public class PlaceholderHighToLow extends SortingType {
* placeholder to sort by
*/
public PlaceholderHighToLow(Sorting sorting, String sortingPlaceholder) {
super(sorting, sortingPlaceholder);
super(sorting, "PLACEHOLDER_HIGH_TO_LOW", sortingPlaceholder);
}
@Override
public String getChars(ITabPlayer p) {
String output = setPlaceholders(p);
p.setTeamNameNote(p.getTeamNameNote() + sortingPlaceholder + " returned \"" + output + "\". &r");
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder + " returned \"&e" + output + "&r\". &r");
double doubleValue = TAB.getInstance().getErrorManager().parseDouble(output, 0);
String string = String.valueOf(DEFAULT_NUMBER - doubleValue);
return string.length() > 10 ? string.substring(0, 10) : string;
}
@Override
public String toString() {
return "PLACEHOLDER_HIGH_TO_LOW";
}
}
@@ -16,20 +16,15 @@ public class PlaceholderLowToHigh extends SortingType {
* placeholder to sort by
*/
public PlaceholderLowToHigh(Sorting sorting, String sortingPlaceholder) {
super(sorting, sortingPlaceholder);
super(sorting, "PLACEHOLDER_LOW_TO_HIGH", sortingPlaceholder);
}
@Override
public String getChars(ITabPlayer p) {
String output = setPlaceholders(p);
p.setTeamNameNote(p.getTeamNameNote() + sortingPlaceholder + " returned \"" + output + "\". &r");
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder + " returned \"&e" + output + "&r\". &r");
double doubleValue = TAB.getInstance().getErrorManager().parseDouble(output, 0);
String string = String.valueOf(DEFAULT_NUMBER + doubleValue);
return string.length() > 10 ? string.substring(0, 10) : string;
}
@Override
public String toString() {
return "PLACEHOLDER_LOW_TO_HIGH";
}
}
@@ -15,13 +15,13 @@ public class PlaceholderZtoA extends SortingType {
* placeholder to sort by
*/
public PlaceholderZtoA(Sorting sorting, String sortingPlaceholder) {
super(sorting, sortingPlaceholder);
super(sorting, "PLACEHOLDER_Z_TO_A", sortingPlaceholder);
}
@Override
public String getChars(ITabPlayer p) {
char[] chars = setPlaceholders(p).toCharArray();
p.setTeamNameNote(p.getTeamNameNote() + sortingPlaceholder + " returned \"" + new String(chars) + "\". &r");
sorting.setTeamNameNote(p, sorting.getTeamNameNote(p) + "\n-> " + sortingPlaceholder + " returned \"&e" + new String(chars) + "&r\". &r");
for (int i=0; i<chars.length; i++) {
char c = chars[i];
if (c >= 65 && c <= 90) {
@@ -31,11 +31,7 @@ public class PlaceholderZtoA extends SortingType {
chars[i] = (char) (219 - c);
}
}
return new String(chars);
}
@Override
public String toString() {
return "PLACEHOLDER_Z_TO_A";
String s = new String(chars);
return sorting.isCaseSensitiveSorting() ? s : s.toLowerCase();
}
}
@@ -11,13 +11,19 @@ import me.neznamy.tab.shared.features.sorting.Sorting;
public abstract class SortingType {
private final String displayName;
//number to add to / subtract from to prevent incorrect sorting with negative values
protected final int DEFAULT_NUMBER = 5000000;
//placeholder to sort by, if sorting type uses it
protected String sortingPlaceholder;
protected SortingType(){
protected Sorting sorting;
protected SortingType(Sorting sorting, String displayName) {
this.sorting = sorting;
this.displayName = displayName;
}
/**
@@ -26,7 +32,9 @@ public abstract class SortingType {
* @param sortingPlaceholder
* placeholder to sort by
*/
protected SortingType(Sorting sorting, String sortingPlaceholder){
protected SortingType(Sorting sorting, String displayName, String sortingPlaceholder){
this.sorting = sorting;
this.displayName = displayName;
if (!sortingPlaceholder.startsWith("%") || !sortingPlaceholder.endsWith("%")) {
TAB.getInstance().getErrorManager().startupWarn("\"" + sortingPlaceholder + "\" is not a valid placeholder for " + this + " sorting type");
} else {
@@ -47,25 +55,21 @@ public abstract class SortingType {
return TAB.getInstance().getPlaceholderManager().getPlaceholder(sortingPlaceholder).set(sortingPlaceholder, player);
}
protected LinkedHashMap<String, String> convertSortingElements(String[] elements) {
LinkedHashMap<String, String> sortedGroups = new LinkedHashMap<>();
protected LinkedHashMap<String, Integer> convertSortingElements(String[] elements) {
LinkedHashMap<String, Integer> sortedGroups = new LinkedHashMap<>();
int index = 1;
int charCount = String.valueOf(elements.length).length()+1; //1 char for <10 values, 2 chars for <100 etc
for (String element : elements){
StringBuilder sb = new StringBuilder();
sb.append(index);
while (sb.length() < charCount) {
sb.insert(0, "0");
}
for (String element0 : element.split("\\|")) {
while (element0.startsWith(" ")) element0 = element0.substring(1);
while (element0.endsWith(" ")) element0 = element0.substring(0, element0.length()-1);
sortedGroups.put(EnumChatFormat.color(element0.toLowerCase()), sb.toString());
sortedGroups.put(EnumChatFormat.color(element0.trim().toLowerCase()), index++);
}
index++;
}
return sortedGroups;
}
@Override
public final String toString() {
return displayName;
}
/**
* Returns current sorting characters of this sorting type for specified player
@@ -37,7 +37,7 @@ public class Animation {
/**
* Constructs new instance with given arguments which are fixed if necessary, such as when
* refresh is not divisible by 50
* refresh is not divisible by {@link me.neznamy.tab.api.TabConstants.Placeholder#MINIMUM_REFRESH_INTERVAL}
*
* @param name
* animation's name
@@ -31,7 +31,8 @@ public class PlayerPlaceholderImpl extends TabPlaceholder implements PlayerPlace
* @param identifier
* placeholder's identifier, must start and end with %
* @param refresh
* refresh interval in milliseconds, must be divisible by 50 or equal to -1 for trigger placeholders
* refresh interval in milliseconds, must be divisible by {@link me.neznamy.tab.api.TabConstants.Placeholder#MINIMUM_REFRESH_INTERVAL}
* or equal to -1 to disable automatic refreshing
* @param function
* refresh function which returns new up-to-date output on request
*/
@@ -27,7 +27,8 @@ public class RelationalPlaceholderImpl extends TabPlaceholder implements Relatio
* @param identifier
* placeholder identifier, must start with {@code %rel_} and end with {@code %}
* @param refresh
* refresh interval in milliseconds, must be divisible by 50 or equal to -1 for trigger placeholders
* refresh interval in milliseconds, must be divisible by {@link me.neznamy.tab.api.TabConstants.Placeholder#MINIMUM_REFRESH_INTERVAL}
* or equal to -1 to disable automatic refreshing
* @param function
* refresh function which returns new up-to-date output on request
*/
@@ -25,7 +25,8 @@ public class ServerPlaceholderImpl extends TabPlaceholder implements ServerPlace
* @param identifier
* placeholder's identifier, must start and end with %
* @param refresh
* refresh interval in milliseconds, must be divisible by 50 or equal to -1 for trigger placeholders
* refresh interval in milliseconds, must be divisible by {@link me.neznamy.tab.api.TabConstants.Placeholder#MINIMUM_REFRESH_INTERVAL}
* or equal to -1 to disable automatic refreshing
* @param supplier
* supplier returning fresh output on request
*/
@@ -1,6 +1,7 @@
package me.neznamy.tab.shared.placeholders;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.placeholder.Placeholder;
import me.neznamy.tab.shared.TAB;
@@ -52,11 +53,14 @@ public abstract class TabPlaceholder implements Placeholder {
* @param identifier
* placeholder's identifier, must start and end with %
* @param refresh
* refresh interval in milliseconds, must be divisible by 50 or equal to -1 for trigger placeholders
* refresh interval in milliseconds, must be divisible by {@link me.neznamy.tab.api.TabConstants.Placeholder#MINIMUM_REFRESH_INTERVAL}
* or equal to -1 to disable automatic refreshing
*/
protected TabPlaceholder(String identifier, int refresh) {
if (refresh % 50 != 0 && refresh != -1) throw new IllegalArgumentException("Refresh interval must be divisible by 50");
if (!identifier.startsWith("%") || !identifier.endsWith("%")) throw new IllegalArgumentException("Identifier must start and end with %");
if (refresh % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL != 0 && refresh != -1)
throw new IllegalArgumentException("Refresh interval must be divisible by " + TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL);
if (!identifier.startsWith("%") || !identifier.endsWith("%"))
throw new IllegalArgumentException("Identifier must start and end with % (attempted to use \"" + identifier + "\")");
this.identifier = identifier;
this.refresh = refresh;
replacements = new PlaceholderReplacementPattern(identifier, TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("placeholder-output-replacements." + identifier));
@@ -53,6 +53,9 @@ public class UniversalPlaceholderRegistry implements PlaceholderRegistry {
if (plugin instanceof LuckPerms) {
manager.registerPlayerPlaceholder(TabConstants.Placeholder.LUCKPERMS_PREFIX, 1000, ((LuckPerms)plugin)::getPrefix);
manager.registerPlayerPlaceholder(TabConstants.Placeholder.LUCKPERMS_SUFFIX, 1000, ((LuckPerms)plugin)::getSuffix);
} else {
manager.registerServerPlaceholder(TabConstants.Placeholder.LUCKPERMS_PREFIX, -1, () -> "");
manager.registerServerPlaceholder(TabConstants.Placeholder.LUCKPERMS_SUFFIX, -1, () -> "");
}
for (Object s : TAB.getInstance().getConfiguration().getAnimationFile().getValues().keySet()) {
Animation a = new Animation(s.toString(), TAB.getInstance().getConfiguration().getAnimationFile().getStringList(s + ".texts"),
@@ -76,6 +79,7 @@ public class UniversalPlaceholderRegistry implements PlaceholderRegistry {
Condition c = new Condition(!"OR".equals(type), condition.getKey(), list, yes, no);
manager.registerPlayerPlaceholder(TabConstants.Placeholder.condition(c.getName()), c.getRefresh(), c::getText);
}
Condition.finishSetups();
}
/**
@@ -3,8 +3,6 @@ package me.neznamy.tab.shared.placeholders.conditions;
import java.util.*;
import java.util.function.Function;
import com.google.common.collect.Lists;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.placeholder.Placeholder;
import me.neznamy.tab.shared.TAB;
@@ -58,10 +56,15 @@ public class Condition {
*/
private int refresh = -1;
/** List of all placeholders used inside this condition */
private final List<String> placeholdersInConditions = new ArrayList<>();
/**
* Constructs new instance with given parameters and registers
* this condition to list as well as the placeholder.
*
* @param type
* type of condition, {@code true} for AND type and {@code false} for OR type
* @param name
* name of condition
* @param conditions
@@ -90,9 +93,7 @@ public class Condition {
}
}
subConditions = list.toArray(new SimpleCondition[0]);
//adding placeholders in conditions to the map, so they are actually refreshed if not used anywhere else
PlaceholderManagerImpl pm = TAB.getInstance().getPlaceholderManager();
List<String> placeholdersInConditions = new ArrayList<>();
for (String subCondition : conditions) {
if (subCondition.startsWith("permission:")) {
if (refresh > 1000 || refresh == -1) refresh = 1000; //permission refreshing will be done every second
@@ -102,6 +103,13 @@ public class Condition {
}
placeholdersInConditions.addAll(pm.detectPlaceholders(yes));
placeholdersInConditions.addAll(pm.detectPlaceholders(no));
registeredConditions.put(name, this);
}
/**
* Configures refresh interval and registers nested placeholders
*/
public void finishSetup() {
for (String placeholder : placeholdersInConditions) {
TAB.getInstance().getPlaceholderManager().getPlaceholder(placeholder).addParent(TabConstants.Placeholder.condition(name));
Placeholder pl = TAB.getInstance().getPlaceholderManager().getPlaceholder(placeholder);
@@ -109,8 +117,7 @@ public class Condition {
refresh = pl.getRefresh();
}
}
pm.addUsedPlaceholders(placeholdersInConditions);
registeredConditions.put(name, this);
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(placeholdersInConditions);
}
/**
@@ -177,7 +184,17 @@ public class Condition {
if (registeredConditions.containsKey(string)) {
return registeredConditions.get(string);
} else {
Condition c = new Condition(true, "AnonymousCondition[" + string + "]", Lists.newArrayList(string.split(";")), "true", "false");
boolean type;
List<String> conditions;
if (string.contains(";")) {
type = true;
conditions = Arrays.asList(string.split(";"));
} else {
type = false;
conditions = Arrays.asList(string.split("\\|"));
}
Condition c = new Condition(type, "AnonymousCondition[" + string + "]", conditions, "true", "false");
c.finishSetup();
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.condition(c.getName()), c.getRefresh(), c::getText);
return c;
}
@@ -198,4 +215,13 @@ public class Condition {
public static Map<String, Function<String, SimpleCondition>> getConditionTypes() {
return conditionTypes;
}
/**
* Marks all placeholders used in the condition as used and registers them.
* Using a separate method to avoid premature registration of nested conditional placeholders
* before they are registered properly.
*/
public static void finishSetups() {
registeredConditions.values().forEach(Condition::finishSetup);
}
}
@@ -14,6 +14,7 @@ import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.nametags.unlimited.ProxyNameTagX;
import me.neznamy.tab.shared.features.redis.RedisPlayer;
import me.neznamy.tab.shared.features.redis.RedisSupport;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.PermissionPlugin;
import me.neznamy.tab.shared.permission.VaultBridge;
@@ -106,6 +107,7 @@ public abstract class ProxyPlatform extends Platform {
TAB tab = TAB.getInstance();
new UniversalPlaceholderRegistry().registerPlaceholders(tab.getPlaceholderManager());
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.SORTING, new Sorting());
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false)) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, new ProxyNameTagX());
} else {
@@ -123,9 +125,4 @@ public abstract class ProxyPlatform extends Platform {
tab.getPlaceholderManager().setTabExpansion(new ProxyTabExpansion());
}
}
@Override
public String getConfigName() {
return "proxyconfig.yml";
}
}
@@ -4,10 +4,24 @@ import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.TabExpansion;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
public class ProxyTabExpansion implements TabExpansion {
/** Map holding all values for all players for resending on server switch */
private final Map<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
@Override
public void setValue(TabPlayer player, String key, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler().sendMessage(player, "Expansion", key, value);
}
public void resendAllValues(TabPlayer player) {
for (Map.Entry<String, String> entry : values.computeIfAbsent(player, p -> new HashMap<>()).entrySet()) {
((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler().sendMessage(player, "Expansion", entry.getKey(), entry.getValue());
}
}
}
@@ -60,10 +60,11 @@ public abstract class ProxyTabPlayer extends ITabPlayer {
* joined, containing all plugin configuration data.
*/
public void sendJoinPluginMessage() {
ProxyTabExpansion expansion = (ProxyTabExpansion) TAB.getInstance().getPlaceholderManager().getTabExpansion();
List<Object> args = Lists.newArrayList("PlayerJoin", getVersion().getNetworkId(),
TAB.getInstance().getGroupManager().getPlugin() instanceof VaultBridge && !TAB.getInstance().getGroupManager().isGroupsByPermissions(),
TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.PET_FIX),
TAB.getInstance().getPlaceholderManager().getTabExpansion() != null);
expansion != null);
ProxyPlatform platform = (ProxyPlatform) TAB.getInstance().getPlatform();
Map<String, Integer> placeholders = platform.getBridgePlaceholders();
args.add(placeholders.size());
@@ -90,6 +91,9 @@ public abstract class ProxyTabPlayer extends ITabPlayer {
}
}
((ProxyPlatform)TAB.getInstance().getPlatform()).getPluginMessageHandler().sendMessage(this, args.toArray());
if (expansion != null) {
expansion.resendAllValues(this);
}
}
/**
+3 -46
View File
@@ -1,35 +1,3 @@
MyAnimation1:
change-interval: 100
texts:
- '&d-&3--------------'
- '&3-&d-&3-------------'
- '&3--&d-&3------------'
- '&3---&d-&3-----------'
- '&3----&d-&3----------'
- '&3-----&d-&3---------'
- '&3------&d-&3--------'
- '&3-------&d-&3-------'
- '&3--------&d-&3------'
- '&3---------&d-&3-----'
- '&3----------&d-&3----'
- '&3-----------&d-&3---'
- '&3------------&d-&3--'
- '&3-------------&d-&3-'
- '&3--------------&d-'
- '&3-------------&d-&3-'
- '&3------------&d-&3--'
- '&3-----------&d-&3---'
- '&3----------&d-&3----'
- '&3---------&d-&3-----'
- '&3--------&d-&3------'
- '&3-------&d-&3-------'
- '&3------&d-&3--------'
- '&3-----&d-&3---------'
- '&3----&d-&3----------'
- '&3---&d-&3-----------'
- '&3--&d-&3------------'
- '&3-&d-&3-------------'
- '&d-&3--------------'
ServerName:
change-interval: 300
texts:
@@ -97,21 +65,10 @@ Welcome:
- '&7&lW_'
- '&7&lW_'
time:
change-interval: 900
change-interval: 3000
texts:
- '&7Time &b%time%'
- '&7> Time &b%time% &7<'
- '&7>> Time &b%time% &7<<'
- '&7>>> Time &b%time% &7<<<'
- '&7Date &b%date%'
- '&7> Date &b%date% &7<'
- '&7>> Date &b%date% &7<<'
- '&7>>> Date &b%date% &7<<<'
ping-tps:
change-interval: 5000
texts:
- '&aPing: &2&n%ping%&r &ams'
- '&bServer TPS: &8%tps%'
- '&7Time &b%time%'
- '&7Date &b%date%'
barcolors:
change-interval: 1000
texts:
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff. Show more