Compare commits

..
1659 Commits
Author SHA1 Message Date
NotAlexNoyle 0f5f7e48dd Update canonical Gitea links 2026-10-30 14:19:35 -04:00
NotAlexNoyle 35e5791926 Update canonical Gitea links 2026-10-30 14:19:35 -04:00
NotAlexNoyle 6ca711435f Exclude CODEOWNERS from language statistics 2026-10-29 16:19:04 -04:00
NotAlexNoyle e8be71a34a Add CODEOWNERS (one staff approval) 2026-10-29 16:19:04 -04:00
NotAlexNoyle 7d0beb6ff2 Add Unions-OG tag 2026-08-09 12:02:07 -04:00
NotAlexNoyleandClaude Fable 5 68d0cdcbe7 TrueOG Network fork of TAB
Soft fork of upstream TAB at 93cfd74, versioned 5.9.9 for Purpur 1.19.4
serving 1.8-1.21.11 clients through ViaVersion/ViaBackwards/ViaRewind.

- Build only Bukkit v1_19_R3, BungeeCord, and Velocity; drop Fabric,
  Forge, NeoForge, and unused NMS modules
- LuckPerms 5.5 (Vault removed), GraalVM JDK 17, one shaded JAR
- Vanish-OG and AFK-OG compatibility, legacy scoreboard team colors,
  disabled scoreboard number formats
- Legacy (1.8-1.12.2) floating nametags: rank brackets dropped, AFK
  badge bolded everywhere, bold rank kept only when the whole tag can
  render bold (legacy width calc counts post-paragraph-l glyphs as bold
  and never receives paragraph-r, else phantom pixels pad the tag end);
  needs ViaBackwards add-teamcolor-to-prefix: false
- Ship live TrueOG Network config values

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N82WDRsXtPi7iRxx1wypXW
2026-07-12 17:58:53 -04:00
NEZNAMY 93cfd74fc0 [Bukkit] Refactor NMS implementation finding 2026-04-03 16:17:09 +02:00
NEZNAMY 79eb6a9078 [Bukkit] Rename classes and add console print 2026-04-03 12:54:07 +02:00
NEZNAMY 2e9e5042e1 Improve handling of IO errors on startup 2026-04-03 12:34:59 +02:00
NEZNAMY bb520edd53 [Bukkit] Catch correct exception and improve error message 2026-03-31 19:13:57 +02:00
NEZNAMY 2beb9563bb [BossBar] Forward UUID to platforms to prepare for 26.1 changes 2026-03-31 17:02:28 +02:00
NEZNAMY 87c15f7611 Bump dependencies 2026-03-31 16:35:52 +02:00
NEZNAMY 655bc176fa [Scoreboard] Fix line score recalculate issue (#1632) 2026-03-30 19:43:59 +02:00
NEZNAMY 66cb30d235 [Commands] Register debug command back to inform users about dump command 2026-03-28 19:32:43 +01:00
NEZNAMY cbe0b25b5f Java 25 and Spigot 26.1 support 2026-03-28 18:22:08 +01:00
NEZNAMY a8ccf4e1c9 Properly initialize minor version for 26.1 2026-03-28 18:17:04 +01:00
NEZNAMY cce29c51e3 Add 26.1 protocol constant 2026-03-25 17:24:49 +01:00
NEZNAMY 242015b0cc [Per world playerlist] Further improve compatibility with vanish plugins (#1621) 2026-03-24 17:41:56 +01:00
NEZNAMY f209e1b0f9 [Per world playerlist] Add vanish check for better compatibility with other plugins (#1621) 2026-03-21 16:16:06 +01:00
NEZNAMY 8a1ab06585 [Bukkit] Update bStats chart 2026-03-21 15:33:21 +01:00
NEZNAMY 50e817a86d [Per world playerlist] Use modern showPlayer/hidePlayer methods with plugin argument for better compatibility with other plugins (#1621) 2026-03-21 10:45:14 +01:00
NEZNAMY dbd596f079 [Belowname / Playerlist objective] Improve parity between the features 2026-03-09 18:43:34 +01:00
NEZNAMY f8e3a07e6f [Belowname / Playerlist objective] Finish vanish support checks after #1629 2026-03-09 17:49:47 +01:00
NEZNAMY ccd4ac3271 Merge pull request #1629 from c0bl/fix/vanished_score_packets
Consider canSee in YellowNumber.java setScore
2026-03-09 17:46:54 +01:00
c m fce1925448 consider canSee when sending scoreboard score packets in yellownumber feature 2026-03-08 21:14:14 -04:00
NEZNAMY 335eb5ef4d [Wiki] Monthly wiki backup for changes made in February 2026 2026-03-08 08:37:45 +01:00
NEZNAMY 654c40b892 [MySQL] Include MySQL driver in the jar for it to work on Velocity and modded platforms (#1620) 2026-03-07 11:46:09 +01:00
NEZNAMY a937243cdf [Dump command] Add proxy players to nametag player table 2026-03-03 17:17:13 +01:00
NEZNAMY 665d6baf1c [Configuration] Add config conversion for the new option (#1626) 2026-03-02 18:41:53 +01:00
NEZNAMY b4d326b52c Merge pull request #1626 from BlvckBytes/placeholders_locale
[Placeholders] Added locale of date-/time-format to config.yml
2026-03-02 18:26:36 +01:00
BlvckBytes 3aacec826b [Placeholders] Added locale of date-/time-format to config.yml 2026-03-01 22:42:27 +01:00
NEZNAMY 3261856385 [Forge/NeoForge] Remove redundant content for easier backporting 2026-02-28 18:05:51 +01:00
NEZNAMY 8cf479bf3b [Forge/NeoForge] Workaround to fix tablist tracking not working properly caused by events being fired too late (#1623) 2026-02-28 11:21:34 +01:00
NEZNAMY 3c3ef636eb [Code style] Remove redundant calls 2026-02-21 21:49:06 +01:00
NEZNAMY 3bd0b13740 [Bukkit] Fix error on Folia 2026-02-15 13:48:34 +01:00
NEZNAMY 92349227a2 Merge pull request #1617 from Arava-0/feat/scoreboard-api-setLines
feat(scoreboard): add setLines method for dynamic line updates
2026-02-13 16:47:01 +01:00
Arava-0 4dfc2a638a feat(scoreboard): add setLines method for dynamic line updates
- Introduces a new method to replace all current scoreboard lines with a provided list.
- Prevents unnecessary updates if the new list is identical to the current one.
2026-02-13 14:58:36 +01:00
NEZNAMY d7c73a8072 [Tablist Formatting] Add information about proxy players to dump output 2026-02-10 17:44:55 +01:00
NEZNAMY 297acc0322 [Belowname & Playerlist objectives] Add information about proxy players to dump output 2026-02-10 17:14:44 +01:00
NEZNAMY 1126b3b471 [Global playerlist] Do not remove players from the same server on quit, let backend handle it (#1611) 2026-02-08 18:07:59 +01:00
NEZNAMY ac59973283 [Tablist] Use ConcurrentHashMap as WeakHashMap was not used the intended way anyway (also may have been causing an issue) 2026-02-08 16:00:15 +01:00
NEZNAMY 4b130ed71d Update Gradle 9.1.0 -> 9.3.1 2026-02-07 18:13:00 +01:00
NEZNAMY ce90a33ce9 [BossBar] Fix unfreezing issue on Velocity and rename some methods (#1610) 2026-02-07 10:09:42 +01:00
NEZNAMY 50fee32218 [Velocity] Bump minimum required Velocity build to 546 (after the bossbar bug was partially fixed) to avoid my head exploding when trying to fix the issue in TAB (and having to make it support the older versions as well) (#1610) 2026-02-06 20:02:51 +01:00
NEZNAMY 39334a4cd9 [Dump command] Add placeholders sent by bridge on proxy installation 2026-02-04 18:26:12 +01:00
NEZNAMY fec2131ba8 [Wiki] Monthly wiki backup for changes made in January 2026 2026-02-02 17:57:02 +01:00
NEZNAMY 7c63436da4 Fix error 2026-02-01 15:05:41 +01:00
NEZNAMY de8fe63bd0 [Components] Partially implement click event and use it in dump command to make the link clickable 2026-01-31 14:40:07 +01:00
NEZNAMY 1c22f2387b [Sorting] Remove redundant variable 2026-01-31 11:16:49 +01:00
NEZNAMY e1b277582a [Sorting] Remove team name note tracking as debug command was deleted and dump already contains this information in a better way 2026-01-31 10:44:11 +01:00
NEZNAMY 94acc4f356 [Placeholders] Create class for holding placeholders to fix issues with re-registering placeholders 2026-01-31 10:39:58 +01:00
NEZNAMY 9afb62a6a4 [Placeholders] Simplify internal placeholder refresh interval handling and make it technically closer to simply pretending they are in the config 2026-01-30 18:58:15 +01:00
NEZNAMY 95ed957d54 [Configuration] Remove comment from default config that is no longer true 2026-01-30 17:10:24 +01:00
NEZNAMY 58814bc2da [API] [Layout] Add method for fixed slot ping as string 2026-01-29 16:07:13 +01:00
NEZNAMY 9e7556c3c1 [Dump Command] Do not show anonymous conditions as they are ugly in the output 2026-01-27 17:42:43 +01:00
NEZNAMY abaf723fb7 [Tablist Formatting] Fix tracked name not being assigned if target player cannot be seen, breaking the feature on server switch 2026-01-26 20:18:51 +01:00
NEZNAMY f52e99c86e [Conditions] Fix conditions not refreshing 2026-01-26 19:05:06 +01:00
NEZNAMY d6e70c35d8 [Dump Command] [Nametags] Dump all display values for all players in a table 2026-01-26 17:55:11 +01:00
NEZNAMY 4c91e73f0e [API] [Layout] Add method for specifying slot count when creating a new layout 2026-01-26 17:16:38 +01:00
NEZNAMY d083fc6e43 [Dump Command] Add table of tablist formatting for all players into dump 2026-01-25 19:47:35 +01:00
NEZNAMY ff2df25e3b [Commands] Delete debug command and update references to dump command 2026-01-25 17:10:40 +01:00
NEZNAMY 86c2342097 Replace most minor version checks with protocol version to prepare for new versioning system. 2026-01-25 16:49:28 +01:00
NEZNAMY 6dbd176683 [Dump Command] Include bridge info about player on proxy installation 2026-01-25 16:06:36 +01:00
NEZNAMY 141aab1c22 [Dump Command] Include list of downloaded PlaceholderAPI expansions 2026-01-25 15:29:10 +01:00
NEZNAMY 126ea4f456 [Dump Command] Print values for all players in belowname and playerlist objectives instead of just the selected player 2026-01-25 15:14:43 +01:00
NEZNAMY a0e5a97594 [Dump Command] Add general config settings to output 2026-01-25 14:54:19 +01:00
NEZNAMY 0420c11937 [Dump Command] Add placeholder-related config sections 2026-01-25 13:46:57 +01:00
NEZNAMY 27472fd387 [Dump Command] Format placeholder values in a table 2026-01-25 12:31:19 +01:00
NEZNAMY 104bdc646f [Dump Command] Improve sorting feature output 2026-01-25 11:44:34 +01:00
NEZNAMY cbc4c275d1 [Description] Update badges 2026-01-24 14:18:17 +01:00
NEZNAMY 372d51f873 Merge branch 'master' of https://github.com/NEZNAMY/TAB
# Conflicts:
#	build.gradle.kts
2026-01-24 14:16:40 +01:00
NEZNAMY 0dfd404215 5.5.0 2026-01-24 11:31:27 +01:00
NEZNAMY 21b7393274 [Dump command] Add more features to output 2026-01-23 20:03:14 +01:00
NEZNAMY dcd5f5a3e6 [Dump command] Improve output and create a class for features with display condition to avoid code duplication 2026-01-23 19:42:29 +01:00
NEZNAMY bf313b3ba1 [Commands] Use better error logging than e.printStackTrace() 2026-01-21 19:50:57 +01:00
NEZNAMY 5b4e3c479c [Commands] Add player info to dump command 2026-01-21 19:40:56 +01:00
NEZNAMY 6dedb05c29 [Commands] Drastically improve the new dump command 2026-01-21 18:18:31 +01:00
NEZNAMY 5f78e01342 [Commands] Initial implementation of /tab dump command 2026-01-18 17:38:07 +01:00
NEZNAMY 881506a3d5 [Tab expansion] Refactor the way data is stored. 2026-01-18 13:59:27 +01:00
NEZNAMY e26fc661b2 [Scoreboard] Execute scoreboard commands in scoreboard thread to avoid error 2026-01-17 20:50:19 +01:00
NEZNAMY f921758ec0 [Placeholders] Small optimization to replacing 2026-01-17 13:21:42 +01:00
NEZNAMY 01d65ba780 [Placeholders] Start rework of placeholder retrieving for better performance and dynamic placeholders 2026-01-17 12:29:55 +01:00
NEZNAMY d77e2d6f45 [Belowname] Clean up and use a different check for vanished players 2026-01-11 18:49:18 +01:00
NEZNAMY 7c30f55f31 [Bukkit] Remove support for several old MC versions 2026-01-11 16:48:47 +01:00
NEZNAMY c2d63865d5 [Bukkit] Repackage paper classes 2026-01-11 15:09:17 +01:00
NEZNAMY f406b518c0 [Bukkit] Add bStats chart 2026-01-11 15:01:42 +01:00
NEZNAMY 78f8c99f11 6.0.0-SNAPSHOT 2026-01-11 14:03:24 +01:00
NEZNAMY f97b9a419f [Performance] Clean up after PR #1600 2026-01-11 12:56:18 +01:00
NEZNAMY f2b88b8cd6 Merge pull request #1600 from groundbreakingmc/master
perf: replace String.format with StringBuilder for faster placeholder…
2026-01-11 12:20:22 +01:00
GroundbreakingMC cd06700c82 perf: implement element-based text parsing for cleaner placeholder replacement 2026-01-11 12:03:50 +01:00
NEZNAMY fa69040e00 Increase thread shutdown timeout to 5000 2026-01-11 11:19:48 +01:00
GroundbreakingMC fd88c1c0bb perf: skip StringBuilder allocation when no placeholders present 2026-01-11 07:22:23 +01:00
GroundbreakingMC cae7d8d061 perf: replace String.format with StringBuilder for faster placeholder replacement 2026-01-11 06:56:11 +01:00
NEZNAMY 2e1e24c225 Merge pull request #1599 from byPixelTV/master
feat: better wildcard support and enhanced pettern matching w/ regex for global player list
2026-01-10 18:47:56 +01:00
Lukas da0307b14f docs(global player list): describe new regex support in the wiki +
old wildcard support
2026-01-10 18:13:26 +01:00
Lukas 082c94ac07 fix(dependencies): change libs disguises repo because the old one is returning a 404 when trying to access it 2026-01-10 18:08:25 +01:00
Lukas 2ed4b1b6fc feat(global player list): enhance wildcard support and add advanced pattern matching w/ regex 2026-01-10 18:06:40 +01:00
NEZNAMY 8c7c8042e5 Merge pull request #1598 from Mattrolol/master
Layout: allow placeholder-based ping values for fixed slots
2026-01-07 18:47:06 +01:00
Mattrolol cca15a2b04 Fix fixed-slot ping validation and warnings 2026-01-07 11:43:05 -06:00
NEZNAMY 5f40411be0 [Debug] Add more messages unload 2026-01-07 18:00:30 +01:00
NEZNAMY 191273a728 [Debug] Add more messages on load/unload 2026-01-07 17:09:05 +01:00
Mattrolol 9eb9e9589f Allow placeholder-based ping values for fixed slots 2026-01-07 02:05:59 -06:00
NEZNAMY fdd45952c7 [Placeholders] Make %online_<server>% work on backend as well 2026-01-06 13:16:52 +01:00
NEZNAMY 98378b222d [Bukkit] Add relational placeholders into tab expansion and parse nested placeholders in normal placeholders 2026-01-03 09:45:52 +01:00
NEZNAMY 15a0446d70 [Conditions] Add warning text if condition is used in the wrong rel / non-rel way 2026-01-02 17:55:12 +01:00
NEZNAMY 7bd1b7c670 [Wiki] Monthly wiki backup for changes made in December 2025 2026-01-01 11:46:28 +01:00
NEZNAMY 46aaa47fdd [Forge] Add hook into a random Vanish mod for checking vanish status 2025-12-31 20:29:04 +01:00
NEZNAMY 2027e9f58e [Forge] Add support for ForgePlaceholderAPI 2025-12-31 17:51:32 +01:00
NEZNAMY 162e4690e9 [Scoreboard] Remove old options from valid key list 2025-12-30 21:06:22 +01:00
NEZNAMY c360575dec [Commands] Support args in dynamic commands 2025-12-28 17:45:56 +01:00
NEZNAMY 5b0354c2c0 [Layout] Rename "condition" to "display-condition" for better consistency with other features 2025-12-27 18:41:13 +01:00
NEZNAMY 52b0f21bd9 [Layout] Small refactor to layout code 2025-12-27 13:39:46 +01:00
NEZNAMY 1e3cbde3b3 [Layout] Merge both fake player layout types due to how similar they are 2025-12-27 12:33:02 +01:00
NEZNAMY 267fb727c7 [Layout] Only mark relational condition as used if actually used as relational 2025-12-26 21:02:50 +01:00
NEZNAMY 68904f58ec [Configuration] Reorder config options for more consistency 2025-12-24 11:54:12 +01:00
NEZNAMY 5cbb1bb5dd [Commands] Return list of messages to send when plugin is disabled as components instead of strings, also fix error when printing them on BungeeCord 2025-12-23 15:21:26 +01:00
NEZNAMY 038d221206 [Performance] Fix conditions being parsed as relational placeholders when not needed 2025-12-23 10:13:55 +01:00
NEZNAMY 22d6ebd0d4 [Performance] Small optimization to condition parsing 2025-12-23 09:21:51 +01:00
NEZNAMY e8dd46c701 [Commands] Modify console vs player check to account for other types of command sender (#1594) 2025-12-22 17:49:54 +01:00
NEZNAMY 1150d07dbe [Configuration] Update config version in default config 2025-12-22 15:52:34 +01:00
NEZNAMY 5c294f4ad3 [Header/Footer] Fix condition update not being processed in feature's thread 2025-12-22 15:51:34 +01:00
NEZNAMY 1b5bf0aab7 [Proxy support] Make channel name partially configurable 2025-12-21 14:13:39 +01:00
NEZNAMY 8d8a03602e [Scoreboard] Make red numbers fully configurable and remove config options related to it as they are redundant now (#1592) 2025-12-21 09:59:23 +01:00
NEZNAMY df7bdb7a0c [Velocity] Change bossbar bug compensation code as it wasn't actually fully fixed yet (#1593) 2025-12-20 10:03:58 +01:00
NEZNAMY 00a5eb2588 [Velocity] Remove header/footer packet modifying since it doesn't work anyway (they are never decoded when coming from backend) 2025-12-19 16:08:39 +01:00
NEZNAMY 90631c66e0 [Velocity] Use internals to inject into pipeline and listen to and modify packets. 2025-12-19 12:52:53 +01:00
NEZNAMY c1ba692a79 Bump version to 5.5.0-SNAPSHOT considering the massive upcoming changes and additions 2025-12-19 12:50:06 +01:00
NEZNAMY 8f3324e0d8 [Layout] Add new slot-count option to default config 2025-12-18 16:01:37 +01:00
NEZNAMY 5a09a9ce78 [Layout] Add support for layouts with less than 4 columns for 1.19.3+ (does not work on Velocity (yet?)) 2025-12-18 15:36:45 +01:00
NEZNAMY 33fcee7641 [Layout] Add support for new relational conditions and relational placeholders 2025-12-17 12:22:45 +01:00
NEZNAMY 12b91940c7 [Conditions] Add support for relational conditions and specifying who to parse placeholders for using %viewer:placeholder% and %target:placeholder% (optional) 2025-12-17 11:57:20 +01:00
NEZNAMY 0e6246f044 [Forge] Update to 1.21.11 2025-12-12 19:01:27 +01:00
NEZNAMY 7f7cf97408 [Tablist] Avoid sending tablist packets of entries which are not in the tablist (yet, on join), which caused a warning message in the client. 2025-12-12 15:46:39 +01:00
NEZNAMY 090fe9ccb6 [Tablist] Start improved tracking of tablist entries 2025-12-10 20:40:54 +01:00
NEZNAMY ffae161282 5.4.1-SNAPSHOT 2025-12-10 18:00:47 +01:00
NEZNAMY 1443850344 [Description] Update badges 2025-12-10 16:58:58 +01:00
NEZNAMY 63f9daf334 5.4.0 2025-12-10 16:45:20 +01:00
NEZNAMY b277d0973a Update to 1.21.11 (except Forge, which is broken) 2025-12-10 16:44:56 +01:00
NEZNAMY 33c0f1672a [Fabric] Update to 1.21.11-rc2 2025-12-05 20:15:39 +01:00
NEZNAMY 4224922ebb [Bukkit] Fix HEARTS render type not working on 1.8 - 1.12 2025-12-04 19:10:40 +01:00
NEZNAMY 6eb30d84b4 [Fabric] Update to 1.21.11-pre4 2025-12-01 18:27:07 +01:00
NEZNAMY 592a75b53f [Wiki] Monthly wiki backup for December 2025 2025-12-01 18:12:35 +01:00
NEZNAMY 8e08ea4e44 [Tablist] Fix gamemode being changed in packets when it shouldn't be (#1568) 2025-11-29 16:19:34 +01:00
NEZNAMY cff1e831d0 [Components] Fix packet decoding error due to too many nested levels caused by disabling MM compacting + resetting magic codes before every color 2025-11-29 09:34:17 +01:00
NEZNAMY 83b5a0f88a [NameTags] Fix nametag being forcibly hidden when player is invisible, making them invisible even in spectator gamemode 2025-11-26 18:25:09 +01:00
NEZNAMY 2ba535111e [Fabric] Update to 1.21.11-pre3 2025-11-26 18:10:48 +01:00
NEZNAMY 4bbf0bb0f1 [Configuration] Change default config to use true/false instead of yes/no to avoid confusion (#1581) 2025-11-24 16:33:51 +01:00
NEZNAMY 1e80070d2d [Fabric] Update to 1.21.11-pre2 2025-11-22 10:59:53 +01:00
NEZNAMY 0afe0f28e1 [Fabric] Update to 1.21.11-pre1 (atlas sprites broke, probably a client bug that will be fixed) 2025-11-20 18:28:50 +01:00
NEZNAMY fbc04b1183 [Commands] Finish custom toggle command registration on Bukkit and remove old command preprocess code (#1578) 2025-11-16 10:58:23 +01:00
NEZNAMY bdd2a139e1 Remove sponge support and bump version to 5.4.0-SNAPSHOT 2025-11-15 18:14:29 +01:00
NEZNAMY e956e59888 [Commands] Turn custom toggle commands into real registered commands for most platforms (bukkit and sponge are left) (#1578) 2025-11-15 17:35:21 +01:00
NEZNAMY 1debc54db6 Update loom to 1.13-SNAPSHOT 2025-11-15 12:36:34 +01:00
NEZNAMY 37786935c3 [NameTags] Fix random error 2025-11-15 10:38:52 +01:00
NEZNAMY 425748af12 [Placeholders] Change int to long to fix overflow (#1576) 2025-11-11 20:19:55 +01:00
NEZNAMY c0deaa40ed [Components] Fix last component style detection, causing team color to not be decided correctly sometimes (again) (#1575) 2025-11-10 17:35:10 +01:00
NEZNAMY 70be098f8a [Components] Fix last component style detection, causing team color to not be decided correctly sometimes 2025-11-06 19:23:01 +01:00
NEZNAMY 838ef437d8 [Wiki] Monthly wiki backup for November 2025 2025-11-02 09:05:52 +01:00
NEZNAMY 44d7473c71 [Placeholders] Change player count related placeholders to check for proxy players and remove overriding attempt (which did not even work with %online_<server>% placeholders 2025-10-31 18:57:56 +01:00
NEZNAMY b8a7dfe543 [Placeholders] Change internal error value to avoid issues when a placeholder actually returned that value 2025-10-31 18:55:21 +01:00
NEZNAMY 4c4bf79371 Merge pull request #1563 from JT122406/master
Gradle 9
2025-10-19 13:54:37 +02:00
NEZNAMY a4b4bd0146 [Velocity] Make bossbar bug compensation dynamic, as it was fixed in build 546 2025-10-19 12:56:51 +02:00
Joseph T. McQuigg 9553e310e4 Disable ShadowJar tasks on Modded platforms 2025-10-18 16:40:44 -04:00
Joseph T. McQuigg e00caddb1e Gradle 9 2025-10-18 11:08:41 -04:00
NEZNAMY 898887225d [Commands] Add offline UUID of player to debug command 2025-10-18 12:28:02 +02:00
NEZNAMY 8eef805418 [Configuration] Avoid console warning during header/footer config conversion 2025-10-13 19:16:39 +02:00
NEZNAMY 8509718d32 [Header/Footer] Remove anti-override logging due to false positives when pipeline lags and fast animations are used 2025-10-11 20:46:40 +02:00
NEZNAMY f2cb7c564f [Adventure hook] Fix object component conversion missing "return" statement 2025-10-10 17:10:29 +02:00
NEZNAMY 9335e5af65 [Velocity] Disable header/footer anti-override, because it does not work properly due to velocity internal logic 2025-10-10 17:01:15 +02:00
NEZNAMY 5a5eadac1f [Bukkit] Use correct method to get gamemode by id to fix error <1.17 and generally avoid risks on all versions 2025-10-09 20:16:37 +02:00
NEZNAMY bcd015b6eb [Header/Footer] Add anti-override to the feature 2025-10-09 20:02:45 +02:00
NEZNAMY 91c6b8f08e 5.3.3-SNAPSHOT 2025-10-09 19:29:26 +02:00
NEZNAMY c7251295a1 [Description] Update badges 2025-10-08 18:47:28 +02:00
NEZNAMY db2110b11e 5.3.2 2025-10-08 18:33:02 +02:00
NEZNAMY be1122ce50 Update to 1.21.10 2025-10-08 17:08:59 +02:00
NEZNAMY cf265987db [Bukkit] Remove override for %player_ping%, since people have probably updated their expansion by now. 2025-10-07 17:54:05 +02:00
NEZNAMY cd82e75514 [Teams] Improve anti-override message to contain source of blocked team and how to fix it. 2025-10-07 17:13:38 +02:00
NEZNAMY 74d36f1ab5 [MySQL] Fix error on upload command and message saying success despite operation not being successful 2025-10-06 20:38:05 +02:00
NEZNAMY 67da0ff32b [GitHub] Add an item to CONTRIBUTING.md 2025-10-06 18:42:03 +02:00
NEZNAMY aabad04e28 [MySQL] Delete data in database on upload first, instead of just overwriting / adding new content 2025-10-05 15:05:20 +02:00
NEZNAMY 9236da40dd [Spectator fix] Rewrite packets to avoid the same packet identity from being sent to all players, causing wrong result from modifying it for another player 2025-10-05 14:51:29 +02:00
NEZNAMY 88ff78b7e3 [Commands] Fix debug command not displaying server name correctly 2025-10-05 11:44:25 +02:00
NEZNAMY 21ebd68607 [API] Restore TabPlayer#getWorld and TabPlayer#getServer 2025-10-05 11:39:31 +02:00
NEZNAMY 9adad40aa9 [Pipeline injection] Change return type of onPacketSend to Object to allow changing the packet 2025-10-05 11:17:46 +02:00
NEZNAMY f63b3de63a [Bukkit] Add Header/Footer sending on 1.7.10 if the method is available (modded server) (#1557) 2025-10-04 19:03:21 +02:00
NEZNAMY 767933e522 [Bukkit] Add craftbukkit package version into debug command output 2025-10-04 18:49:20 +02:00
NEZNAMY 0fb86ddf6c 5.3.2-SNAPSHOT 2025-10-04 17:36:02 +02:00
NEZNAMY d52969597f [Description] Update badges 2025-10-04 17:31:03 +02:00
NEZNAMY 70e1537bc0 5.3.1 2025-10-04 16:49:23 +02:00
NEZNAMY 605a09b290 [Bukkit] Fix error on 1.8 - 1.19.2 2025-10-04 16:11:51 +02:00
NEZNAMY 8e2f8d67f3 Use a separate field for player's version network id for better results on unknown versions 2025-10-04 13:31:58 +02:00
NEZNAMY d00764e548 [Commands] Add more info to debug command 2025-10-04 13:21:46 +02:00
NEZNAMY 953472a41a [Velocity] Add flag for compensating for a bossbar bug that might be fixed soon (and was fixed in a fork) 2025-10-04 11:43:26 +02:00
NEZNAMY 6be5bb4757 [Conditions] Fix anonymous conditions not refreshing 2025-10-03 20:17:50 +02:00
NEZNAMY b48a9e4f22 [Layout] Add support for per-layout default-skin (#1555) 2025-10-03 20:06:26 +02:00
NEZNAMY 6d42e252a1 [Teams] Remove old forgotten code causing nametag visibility to not refresh on proxies (also refresh it properly on join) 2025-10-03 16:22:51 +02:00
NEZNAMY ef98b15c86 5.3.1-SNAPSHOT 2025-10-02 19:59:52 +02:00
NEZNAMY 6945e8ee81 [Description] Update badges 2025-10-02 18:57:13 +02:00
NEZNAMY 9c9edbff75 5.3.0 2025-10-02 18:41:25 +02:00
NEZNAMY b8e3a07980 [Configuration] Change warning message in config conversion 2025-10-02 18:40:05 +02:00
NEZNAMY 06279e3708 [Forge] Update to 1.21.9 2025-10-02 18:39:00 +02:00
NEZNAMY 9812345f71 [MiniMessage support] Add component conversion for object components 2025-10-01 12:45:10 +02:00
NEZNAMY e1f667079a [MiniMessage support] Fix error when object components were used 2025-10-01 11:40:53 +02:00
NEZNAMY 6edc3babc8 Update Bukkit, Paper, BungeeCord, Fabric and NeoForge to 1.21.9 2025-10-01 11:03:15 +02:00
NEZNAMY 309c67969a [Wiki] Monthly wiki backup for October 2025 2025-10-01 09:05:32 +02:00
NEZNAMY bd681229db [Conditions] Recode conditions, add new types and add disable-condition conversion for header/footer 2025-10-01 08:54:41 +02:00
NEZNAMY 4f0a4feb03 [Components] Micro optimization 2025-09-28 19:31:08 +02:00
NEZNAMY 2d84a60c84 [NameTags] Rename caches 2025-09-28 19:29:22 +02:00
NEZNAMY 0f66bc7264 [NameTags] Split cache into prefix and suffix caches 2025-09-28 16:17:57 +02:00
NEZNAMY 9283f969c4 [Header/Footer] Fix display-condition updating and change way the feature is resent on server switch (to also prepare for a future improvement) 2025-09-28 11:05:17 +02:00
NEZNAMY dbcad7b11d [Multi-server support] Fix error on reload 2025-09-27 14:07:24 +02:00
NEZNAMY 8e96fc1595 [Multi-server support] Add debug message about current channel name 2025-09-27 11:37:15 +02:00
NEZNAMY fa46427b1f [Multi-server support] Change channel name to avoid errors when using mixed tab versions 2025-09-27 11:28:29 +02:00
NEZNAMY 5a112bff22 [Header/Footer] Correctly resend the feature on server switch 2025-09-26 20:14:42 +02:00
NEZNAMY 1a8bc5ee34 [Configuration] Change config conversion warning message 2025-09-25 19:05:04 +02:00
NEZNAMY 1001bcd507 [Error Logging] Fix warnings no longer having color 2025-09-25 18:48:47 +02:00
NEZNAMY 2f6f5f4958 [ProxySupport] Add compensation for packets being received in the wrong order 2025-09-25 18:42:37 +02:00
NEZNAMY 863cceb175 [Fabric] Update to 1.21.9-rc1 2025-09-25 18:24:25 +02:00
NEZNAMY d07d604f7c [Configuration] Fix per-server settings using * symbol not working anymore 2025-09-24 18:52:31 +02:00
NEZNAMY 9035f8b57f [Backend] Add %deaths% placeholder (default refresh 1000) 2025-09-23 16:33:57 +02:00
NEZNAMY 43bc22b8be [Fabric] Update to 1.21.9-pre3 2025-09-22 18:02:29 +02:00
NEZNAMY 1d59eb5787 Merge pull request #1550 from GG-MD/master
Packet Handling Safety Enhancement - Null Packet Protection
2025-09-21 18:28:44 +02:00
MGG 5383b87c3f Packet Handling Safety Enhancement - Null Packet Protection 2025-09-21 19:00:47 +03:00
NEZNAMY 5c3815aee0 [BungeeCord / Velocity] Add try/catch to RedisBungee hook 2025-09-21 09:09:27 +02:00
NEZNAMY 69b9acdc83 [Scoreboard] Do not actually unregister scoreboard if another plugin sends its own, just stop slotting it 2025-09-21 09:04:57 +02:00
NEZNAMY 3b68fe9bdd [Layout] Adapt to mineskin no longer supporting skins by id 2025-09-20 18:46:02 +02:00
NEZNAMY 4d9dd1cf83 [GitHub] Add new issue template 2025-09-20 13:48:22 +02:00
NEZNAMY 0854fd674f [Fabric] Update to 1.21.9-pre2 2025-09-20 13:26:36 +02:00
NEZNAMY 75478b59ae [Bukkit] Take advantage of new Java features and remove unnecessary reflection 2025-09-20 13:11:58 +02:00
NEZNAMY d39f6fd061 [Bukkit] [Code style] Use TYPE instead of Object for components 2025-09-17 20:30:34 +02:00
NEZNAMY a532cd4fc1 [Code style] Rename some component classes to avoid conflict with platform classes 2025-09-17 18:39:28 +02:00
NEZNAMY a6afd9de0a [Fabric] Update to 1.21.9-pre1 (thanks Mojang) 2025-09-16 19:43:59 +02:00
NEZNAMY 9a8df8c890 [Configuration] Add missing config conversion of "*" symbol in header/footer 2025-09-15 19:24:32 +02:00
NEZNAMY cd435cc716 [Code style] Remove pointless world/server instance creations 2025-09-15 19:10:56 +02:00
NEZNAMY b0bdc36b59 [Tablist formatting] [Performance] Only try to format players on the same server (or global playerlist group) 2025-09-15 18:58:53 +02:00
NEZNAMY 1971513acc Fix issue from previous commit 2025-09-15 18:56:09 +02:00
NEZNAMY 99646f2113 [Global playerlist] Refactor server groups for future use in other features 2025-09-15 18:07:14 +02:00
NEZNAMY 78349176d6 [Global playerlist] Optimize shouldSee method to reduce canSee calls to other plugins 2025-09-15 13:28:40 +02:00
NEZNAMY 92115ed493 [Global playerlist] Store "spy-server" attribute in Server class instead of player data 2025-09-15 13:04:11 +02:00
NEZNAMY 4ef8508c65 [Code style] Extract project variables from TabConstants class and move it back to source folder 2025-09-15 12:37:25 +02:00
NEZNAMY 1feafa884e [Code style] Create a class for holding servers and worlds and clear it on reload 2025-09-15 12:22:44 +02:00
NEZNAMY 02bff23b1f [Code style] Separate nested class into a regular class 2025-09-15 11:37:16 +02:00
NEZNAMY d80742bf20 [Code style] Separate nested class into a regular class 2025-09-15 11:28:57 +02:00
NEZNAMY 7bde3d9996 [Code style] Separate nested class into a regular class 2025-09-15 11:19:55 +02:00
NEZNAMY 4d86beb268 [Code style] Separate nested class into a regular class 2025-09-15 11:10:16 +02:00
NEZNAMY b039cbb7b6 [Code style] Separate nested class into a regular class 2025-09-15 11:00:29 +02:00
NEZNAMY 7d8af7efc9 [Code style] Separate nested class into a regular class 2025-09-15 10:51:48 +02:00
NEZNAMY 7019044837 [Nametags] Upgrade invisible-nametags option to support conditions 2025-09-14 18:09:17 +02:00
NEZNAMY 5916ec5d07 [Javadoc] Fix typo 2025-09-14 11:50:51 +02:00
NEZNAMY 4508fcf4b3 [Javadoc] Add some missing docs 2025-09-14 11:01:44 +02:00
NEZNAMY 2de69557d2 [Code style] Remove whitespace 2025-09-14 10:51:37 +02:00
NEZNAMY b01e793a26 [Javadoc] Add some missing docs 2025-09-14 10:49:40 +02:00
NEZNAMY 1f67ab7bdb [Code style] Rename variable to avoid shadowing outer scope variable 2025-09-14 10:45:39 +02:00
NEZNAMY 43ae5add60 [Code style] Remove redundant suppression 2025-09-14 10:43:14 +02:00
NEZNAMY 971620818b [Placeholders] No longer show last value of a placeholder when identifier was returned, it caused more harm than good (most notably on proxy installation when not all PAPI expansions are downloaded and therefore placeholders "freezing") 2025-09-13 18:53:55 +02:00
NEZNAMY c997b99935 [Bukkit] Add missing shadowColor implementation on spigot 2025-09-13 18:27:41 +02:00
NEZNAMY ae83ce1f42 [Code style] Remove unnecessary check 2025-09-13 17:38:28 +02:00
NEZNAMY 2410717510 [Code style] Remove no longer needed variable 2025-09-13 17:06:59 +02:00
NEZNAMY 5270a156b3 [Code style] Remove unused classes 2025-09-13 17:03:00 +02:00
NEZNAMY 07d807a7bd [Header/Footer] Rework the feature to allow conditional designs 2025-09-13 15:56:14 +02:00
NEZNAMY 5fb80f5661 5.3.0-SNAPSHOT 2025-09-13 15:47:23 +02:00
NEZNAMY ce6bef7f19 [Fabric] Update to 25w37a 2025-09-10 19:04:56 +02:00
NEZNAMY facedfdea7 [Components] Add config option to disable MiniMessage support (also introduce new config converter) 2025-09-07 12:44:14 +02:00
NEZNAMY e8fa66dac0 [Configuration] Removed forced lowercasing of server names 2025-09-06 19:08:54 +02:00
NEZNAMY 997d824d12 [Code style] Remove redundant call 2025-09-06 14:25:28 +02:00
NEZNAMY 86baf27ca0 [Nametags] Fix empty line being sent to player when toggle message is empty 2025-09-06 14:21:54 +02:00
NEZNAMY 2b8b5303c8 [Components] Properly set team color to used magic codes if both color and magic codes are used 2025-09-06 11:10:09 +02:00
NEZNAMY 24bd58130b [Components] Fix team color affecting prefix/suffix 2025-09-06 09:53:51 +02:00
NEZNAMY 907bbdb52b [Fabric] Update to 25w36b 2025-09-03 15:58:20 +02:00
NEZNAMY 26c491ce22 [Nick compatibility] Add debug message when an API request is made to change expected profile name 2025-09-01 17:26:43 +02:00
NEZNAMY 73def64743 [Wiki] Monthly wiki backup for September 2025 2025-09-01 17:14:40 +02:00
NEZNAMY d021d213f6 [Components] Extend player sprite object component to support player names the intended way and add UUID support 2025-08-30 19:29:44 +02:00
NEZNAMY dfd5ccf525 [Placeholders] Add %uuid% placeholder showing player's UUID 2025-08-30 18:48:07 +02:00
NEZNAMY a4cfe9f81b [Configuration] Replace \n with newline sooner, resulting in them being replaced even inside placeholders like animations 2025-08-30 15:52:56 +02:00
NEZNAMY 8402088cba [MiniMessage hook] Properly handle uppercased color codes and do not throw an error if text contains the § symbol 2025-08-27 20:50:25 +02:00
NEZNAMY 2adcc0d45b [Components] Allow player heads using <head:DEFINITION> using same definition syntax as layout 2025-08-27 19:52:58 +02:00
NEZNAMY da25752b73 [Configuration] Always load skin cache 2025-08-27 19:36:30 +02:00
NEZNAMY ac348cf207 [Components] Add a comment 2025-08-27 18:44:41 +02:00
NEZNAMY 6afe8f0081 [Components] Add PlayerSprite support 2025-08-27 18:24:41 +02:00
NEZNAMY 86c294592f Delete component module and move content to shared 2025-08-27 17:38:45 +02:00
NEZNAMY 3deba4a0f3 [Fabric] Fix error on 25w35a 2025-08-27 16:55:09 +02:00
NEZNAMY cfb3d10a7d [Bukkit] Remove unused dependency 2025-08-26 20:16:29 +02:00
NEZNAMY 458fa4e416 Update Fabric to 25w35a and prepare for new object components 2025-08-26 20:08:24 +02:00
NEZNAMY 6e7d2338a4 [Tablist] Remove unused class 2025-08-24 16:23:17 +02:00
NEZNAMY d0bd1b4b0b [Tablist] Fix possible issue from previous commit not applying forced display name 2025-08-24 12:35:28 +02:00
NEZNAMY 2076b589b4 [Tablist] Avoid sending tablist update packets of entries which are not present (currently the new check only works on BungeeCord, Velocity already worked) 2025-08-24 11:47:59 +02:00
NEZNAMY a4b8c2b730 [Components] Change sprite syntax to <sprite:ATLAS:SPRITE> to match MiniMessage 2025-08-24 09:43:29 +02:00
NEZNAMY 0cc83325bc [Components] Replace spaces with underscores in atlas and sprite 2025-08-23 19:10:31 +02:00
NEZNAMY f2fa63315e [Components] Remove toRawText method and replace its usage with the intended method 2025-08-23 18:58:43 +02:00
NEZNAMY 9631abf430 [Components] Force lowercase atlas and sprite 2025-08-23 18:35:51 +02:00
NEZNAMY 084123132a [Bukkit] Remove fallback using Bukkit API on unsupported versions (this also drops 1.5 - 1.6 support) 2025-08-23 18:18:55 +02:00
NEZNAMY 4e9d2df0b2 [Bukkit] Fix 1.9.4 and 1.10.x 2025-08-23 17:45:35 +02:00
NEZNAMY e3a3a65987 Revert unintended change 2025-08-23 16:39:03 +02:00
NEZNAMY 17feac0501 [Ping Spoof] Rework the feature for a faster check 2025-08-23 16:37:58 +02:00
NEZNAMY 84e280a562 [Bukkit] Include 1.7 in supported versions list 2025-08-23 10:40:02 +02:00
NEZNAMY 906e304018 [Bukkit] Add NMS Implementations for 1.7.x 2025-08-23 10:39:35 +02:00
NEZNAMY 48cdeb7283 [Bukkit] Add NMS Implementations for 1.8.x and 1.9.x 2025-08-23 09:43:24 +02:00
NEZNAMY 4efb55f685 [Code style] Add NMS Implementations for 1.10.x and 1.11.x 2025-08-21 18:08:32 +02:00
NEZNAMY 3ed6f8158d [Code style] Remove unused methods 2025-08-21 17:39:11 +02:00
NEZNAMY c89a38b0da [Bukkit] Add NMS Implementations for 1.13.x and 1.14.x 2025-08-20 18:55:14 +02:00
NEZNAMY eb40cf873f [Bukkit] Check for ObjectComponent in another method 2025-08-20 16:53:37 +02:00
NEZNAMY 892c626312 [Components] Add atlas-sprite support to text parsing 2025-08-20 16:28:47 +02:00
NEZNAMY 7e0d447e75 [Fabric] Update to 25w34b 2025-08-20 15:56:21 +02:00
NEZNAMY 49b2723c19 [Components] Introducing ObjectComponent for future 1.21.9 content 2025-08-20 14:54:55 +02:00
NEZNAMY 5140d912e1 [Bukkit] Add NMS Implementation for 1.15.x 2025-08-20 13:51:03 +02:00
NEZNAMY 0fa1165652 [Bukkit] Add NMS Implementation for 1.16 - 1.16.3 2025-08-20 13:42:57 +02:00
NEZNAMY 375d2297e7 [Bukkit] Remove unused code 2025-08-20 13:23:50 +02:00
NEZNAMY fbd089d87a [Bukkit] Add NMS Implementation for 1.18 - 1.18.1 2025-08-20 13:16:23 +02:00
NEZNAMY 5076c80e2c [Bukkit] Remove reflection entirely now that adapters cover everything reflection did 2025-08-20 13:13:50 +02:00
NEZNAMY 4fcbec7a4a [Spectator fix] Fix memory leak (introduced in fix for #1537) 2025-08-20 12:32:36 +02:00
NEZNAMY cb82d3ad13 [Bukkit] Add NMS Implementation for 1.21.6 - 1.21.8 2025-08-20 12:14:24 +02:00
NEZNAMY a6172c705d [Bukkit] Add NMS Implementation for 1.21.5 2025-08-20 11:44:54 +02:00
NEZNAMY 3c2c480c11 [Bukkit] Add NMS Implementation for 1.21.4 2025-08-20 11:23:00 +02:00
NEZNAMY da1a839014 [Bukkit] Add NMS Implementation for 1.21.2 - 1.21.3 2025-08-20 11:04:24 +02:00
NEZNAMY bf6628c6a2 [Bukkit] Add NMS Implementation for 1.21 - 1.21.1 2025-08-20 10:43:59 +02:00
NEZNAMY 42674bbfdd [Bukkit] Clean up implementation detection 2025-08-20 10:20:48 +02:00
NEZNAMY 225164b7b6 [Bukkit] Add NMS Implementation for 1.20.5 - 1.20.6 2025-08-20 10:16:32 +02:00
NEZNAMY 35ce37acf8 [Bukkit] Add NMS Implementation for 1.20.3 - 1.20.4 2025-08-20 09:48:42 +02:00
NEZNAMY 2df5b00e07 [Bukkit] Add NMS Implementation for 1.20.2 2025-08-20 09:35:40 +02:00
NEZNAMY 3fa25c0662 [Bukkit] Add NMS Implementation for 1.20 - 1.20.1 2025-08-20 09:00:06 +02:00
NEZNAMY b7e233834e [Bukkit] Add NMS Implementation for 1.19.4 2025-08-19 20:35:52 +02:00
NEZNAMY ff80a4807f [Bukkit] Add NMS Implementation for 1.19.3 2025-08-19 20:16:28 +02:00
NEZNAMY c56592c19a [Bukkit] Add constants for enums for better readability 2025-08-19 19:42:55 +02:00
NEZNAMY e93df8e38d [Fabric] Add Fabric API version for 25w33a 2025-08-19 18:57:50 +02:00
NEZNAMY 15664bc9fb Update loom to 1.11 2025-08-19 18:57:24 +02:00
NEZNAMY 9cedbabd75 [Nametags] Fix memory leak 2025-08-19 17:58:33 +02:00
NEZNAMY a98ecee8f3 [Spectator Fix] Force gamemode always, even if not currently in spectator (#1537) 2025-08-19 17:19:52 +02:00
NEZNAMY f64d0d7b41 Merge pull request #1539 from tooobiiii/master
Fix: Compilation errors showing generated src instead of actual src -> introducing blossom 2.1.0
2025-08-19 16:50:30 +02:00
TobisuchtetXD 7a1f1ed484 introducing new blossom version 2.1.0 fixes weird compilation behaviour 2025-08-19 12:32:43 +02:00
NEZNAMY 85ade0dd15 [Spectator Fix] Use packet / periodic detection to make the feature more reliable instead of delays (#1537) 2025-08-18 18:06:05 +02:00
NEZNAMY 9c70f5a187 [Code style] Rename "expectedDisplayName" to "forcedDisplayName" 2025-08-17 18:23:11 +02:00
NEZNAMY f622d0c8b3 [Scoreboard / Bossbar] Fix announce command syntax in docs 2025-08-07 17:09:29 +02:00
NEZNAMY a3c4362a90 [MiniMessage Support] Reset magic codes before every color code to match vanilla behavior 2025-08-03 19:20:54 +02:00
NEZNAMY 05609ca436 [Code style] Remove case insensitivity from spy-servers option for safety due to rewrite 2025-08-03 17:10:05 +02:00
NEZNAMY fde7fefe9f [Performance] Use Server object for storing player servers instead of Strings 2025-08-03 15:23:37 +02:00
NEZNAMY 19f45aeb4d [Code style] Use World object for storing player worlds instead of Strings 2025-08-03 14:43:34 +02:00
NEZNAMY 540eafc41b [Wiki] Monthly wiki backup for August 2025 2025-08-02 13:35:43 +02:00
NEZNAMY 1ba62e0ae9 [Descriptions] Update badges 2025-07-24 17:20:37 +02:00
NEZNAMY 07b213d0af [Playerlist Objective] [Configuration] Fix warning when setting value to 0 (without quotes, resulting in it being an Integer) 2025-07-24 17:18:34 +02:00
NEZNAMY 0528baaa64 [Belowname] [Configuration] Fix warning when setting value to 0 (without quotes, resulting in it being an Integer) 2025-07-22 18:42:29 +02:00
NEZNAMY 264b8e69d8 [Configuration] Add missing config conversion to add register-tab-expansion option (#1524) 2025-07-22 17:31:23 +02:00
NEZNAMY b4c6da418d [Configuration] Fix misinformation and missing link in default config.yml 2025-07-20 15:09:00 +02:00
NEZNAMY 3811f942a1 5.2.6-SNAPSHOT 2025-07-20 15:05:17 +02:00
NEZNAMY 6d295960d5 5.2.5 2025-07-19 17:53:04 +02:00
NEZNAMY 983d6a055b [Sponge] Fix 1.21.5+ error caused by a Sponge bug 2025-07-19 11:48:43 +02:00
NEZNAMY 9de3fc4bbb Update to 1.21.8 2025-07-19 11:31:29 +02:00
NEZNAMY f4c986f0de [Sponge] Set listed tablist entry value 2025-07-19 11:21:27 +02:00
NEZNAMY 434f629c0b [BungeeCord] Update to build 1990 and up 2025-07-15 19:10:35 +02:00
NEZNAMY dac32b6daf [bStats] Fix "1.99.x" version being sent for unknown server versions 2025-07-06 19:44:59 +02:00
NEZNAMY dd845013ce [Layout] [Performance] replace removeAll with removeIf for massively improved performance 2025-07-03 18:02:18 +02:00
NEZNAMY b9bad50dbd 5.2.5-SNAPSHOT 2025-07-03 18:00:59 +02:00
NEZNAMY 56540df419 [Wiki] Monthly wiki backup for July 2025 2025-07-02 17:44:18 +02:00
NEZNAMY d0b0c55ab8 [Description] Update badges 2025-07-01 20:49:11 +02:00
NEZNAMY 6f2b92e044 5.2.4 2025-07-01 20:38:47 +02:00
NEZNAMY 5da16e8055 Update to 1.21.7 2025-07-01 20:33:36 +02:00
NEZNAMY a522ea9902 [Proxy Support] Add try/catch for message decoding 2025-07-01 20:32:55 +02:00
NEZNAMY 36801184a5 5.2.4-SNAPSHOT 2025-06-30 16:39:37 +02:00
NEZNAMY d212eb108d [Bukkit] Remove unused empty lists from plugin.yml to avoid Polymart bug breaking them 2025-06-26 18:32:51 +02:00
NEZNAMY cbc1de0f33 [Description] Update badges 2025-06-23 18:49:25 +02:00
NEZNAMY fcdc04e228 5.2.3 2025-06-23 18:36:49 +02:00
NEZNAMY 890f1ee131 [Velocity] Improve message telling users to install VelocityScoreboardAPI 2025-06-23 18:36:37 +02:00
NEZNAMY f3aec47ac3 [BungeeCord] Send header/footer using API since it no longer throws an error 2025-06-22 10:48:31 +02:00
NEZNAMY bcf1c828b5 [BungeeCord] Update to build 1980 (older versions are no longer supported) 2025-06-22 10:40:34 +02:00
NEZNAMY 5fd319a152 [BossBar] Fix %tab_bossbar_visible% not loading on join when player has bossbar disabled and toggling is saved 2025-06-22 10:04:38 +02:00
NEZNAMY abecb58a8d [API] Add methods for removing bossbars and scoreboards (#1507) 2025-06-21 20:35:22 +02:00
NEZNAMY 135166b01e [Bukkit] Remove ViaVersion hook for sending packets due to many unfixable problems 2025-06-21 18:13:46 +02:00
NEZNAMY 0770293452 [Placeholders] Fix placeholder updating when placeholder returned its identifier not being skipped properly (#1502) 2025-06-21 13:57:16 +02:00
NEZNAMY f3c40ad582 [Bukkit] Disable ViaVersion hook for sending raw packets to newer players due to issues 2025-06-21 13:09:44 +02:00
NEZNAMY aacc115a36 [API] Add functions for getting original prefix/suffix values with placeholders parsed 2025-06-21 12:51:03 +02:00
NEZNAMY b7ba07b78d [Bukkit] Remove redundant SneakyThrows annotations 2025-06-21 10:45:27 +02:00
NEZNAMY e13445f55d [Bukkit] Fix skin writing in ViaVersion hook 2025-06-21 10:41:11 +02:00
NEZNAMY bd527d3868 [Configuration] Fix false positive in configuration checking (#1506) 2025-06-20 18:34:24 +02:00
NEZNAMY f288112eb2 [Bukkit] Fix error in ViaVersion hook 2025-06-20 16:18:03 +02:00
NEZNAMY 6b415e64a8 5.2.3-SNAPSHOT 2025-06-19 17:29:10 +02:00
NEZNAMY cfb2cb6b0a [Description] Update badges 2025-06-18 19:20:23 +02:00
NEZNAMY 622deb6fd7 5.2.2 2025-06-18 18:48:14 +02:00
NEZNAMY 7f41421d65 [Bukkit] Add 1.21.6 to supported versions 2025-06-18 17:34:51 +02:00
NEZNAMY 73e706465a Add 1.21.6 protocol constant 2025-06-18 17:31:54 +02:00
NEZNAMY a8c543ca9e [Paper] Update paperweight to 2.0.0-beta.17 2025-06-18 17:23:38 +02:00
NEZNAMY 9594ff452b Update Gradle to 8.14.2 2025-06-18 17:18:09 +02:00
NEZNAMY d5c81298dd [Modded platforms] Update to 1.21.6 2025-06-18 16:50:36 +02:00
NEZNAMY 88faacb240 [Proxy support] Properly handle out of order redis messages (#1465) 2025-06-17 21:00:49 +02:00
NEZNAMY 0d2a04b3a5 [Proxy support] Make method synchronized to try to avoid concurrent access 2025-06-17 18:06:38 +02:00
NEZNAMY f8a90b28b5 [Code style] Create static methods for initializing components 2025-06-16 19:57:58 +02:00
NEZNAMY a948febb18 [Belowname & Playerlist objective] Suggest a PlaceholderAPI expansion might be missing when a placeholder did not parse 2025-06-16 18:44:58 +02:00
NEZNAMY 8e33481b97 [Proxy support] Massive fixes to handling players who switch servers and temporarily cause the player to be loaded twice (#1465) 2025-06-15 19:03:50 +02:00
NEZNAMY f54be214f9 [Vanish compatibility] Assume a player can always see themselves 2025-06-15 15:03:14 +02:00
NEZNAMY d50b47acb5 [Configuration] Remove case sensitivity when checking for valid properties / configuration option names 2025-06-14 19:35:55 +02:00
NEZNAMY 31011c243a [Fabric] Update to 1.21.6-rc1 2025-06-12 18:52:20 +02:00
NEZNAMY f4aa03f20d [Conditions] Fix issue from recent commits 2025-06-12 18:51:56 +02:00
NEZNAMY 28aacaea45 [Fabric] Update to 1.21.6-pre4 2025-06-10 19:52:30 +02:00
NEZNAMY d4fd9a2b78 [Sorting] Fix PLACEHOLDER type not loading properly when ":" symbol is used in values (#1504) 2025-06-09 18:41:06 +02:00
NEZNAMY 663e0be357 [Tablist] Do not send update packets of vanished players, exposing them to players 2025-06-07 18:43:17 +02:00
NEZNAMY 14d29577c5 [Conditions] Fix condition compiling when delimiter is a part of used placeholder (#1503) 2025-06-07 15:22:48 +02:00
NEZNAMY 951f8072c1 [Placeholders] Add %luckperms-prefixes% and %luckperms-suffixes% placeholders listing all values instead of just the ones with the highest weight 2025-06-07 14:54:13 +02:00
NEZNAMY 05f042eda0 [Conditions] Improve condition type detection (#1503) 2025-06-07 11:37:32 +02:00
NEZNAMY b00c2fbcb4 [Fabric] Update to 1.21.6-pre3 2025-06-07 10:29:37 +02:00
NEZNAMY dde4866e50 [Belowname] Improve warning message 2025-06-05 19:17:42 +02:00
NEZNAMY 9fa6b21c08 [Code style] Extract code into a method 2025-06-03 19:51:04 +02:00
NEZNAMY e95e6fc20c Remove anti-override from config and make it permanently enabled 2025-06-03 17:14:18 +02:00
NEZNAMY c7a3b00c3c Merge pull request #1499 from R00tB33rMan/master
Remove printline when testing TPS & MSPT getter
2025-06-03 07:11:07 +02:00
R00tB33rMan 74bc0e0cb7 Remove printline when testing TPS & MSPT getter 2025-06-02 23:30:02 -04:00
NEZNAMY 6248008b4d [Fabric] Update to 1.21.6-pre2 2025-06-02 18:32:42 +02:00
NEZNAMY 2d91e165ce [Wiki] Monthly wiki backup for June 2025 2025-06-01 18:00:37 +02:00
NEZNAMY b1220550eb [Multi-server support] Code improvements for future issue debugging 2025-06-01 16:52:12 +02:00
NEZNAMY 680ff3412f [Bukkit] Fix wrong supported version range of a module 2025-06-01 16:09:45 +02:00
NEZNAMY 799f6b8215 [Folia] Implement %tps% and %mspt% (#1498) 2025-05-31 16:52:18 +02:00
NEZNAMY 1be224b7de [Fabric] Update to 1.21.6-pre1 2025-05-29 17:56:03 +02:00
NEZNAMY a32adfbfc7 [Bukkit] Add implementations for all paper versions with mojang mappings 2025-05-27 19:48:01 +02:00
NEZNAMY dda415351a Revert "[Fabric] Downgrade to 1.21.5 for release"
This reverts commit 6873c59505.
2025-05-23 17:08:57 +02:00
NEZNAMY 685e4b9faa 5.2.2-SNAPSHOT 2025-05-23 17:08:48 +02:00
NEZNAMY 1f842563b3 [Bukkit] Hotfix error 2025-05-22 21:45:09 +02:00
NEZNAMY f237b61065 [Description] Update badge to 5.2.1 2025-05-22 17:56:44 +02:00
NEZNAMY 19d6d06791 5.2.1 2025-05-22 17:38:48 +02:00
NEZNAMY 6873c59505 [Fabric] Downgrade to 1.21.5 for release 2025-05-22 17:35:43 +02:00
NEZNAMY e7ae62db7b [Playerlist objective] Change default title 2025-05-21 15:51:52 +02:00
NEZNAMY d4362f2ed2 [Bukkit] Only get methods declared by the class, not superclasses (#1492) 2025-05-20 17:21:49 +02:00
NEZNAMY 9247f3d4fb [Fabric] Update to 25w21a 2025-05-20 16:37:09 +02:00
NEZNAMY b4993b657e [Paper] Downgrade to 1.21.4 to fix compilation (code is the same) 2025-05-20 16:32:40 +02:00
NEZNAMY b73aa93d01 [Commands] Ignore cancelled command events 2025-05-20 16:28:50 +02:00
NEZNAMY bce689e672 [Modded platforms] Use reflection instead of access transforming which may fail 2025-05-15 20:10:32 +02:00
NEZNAMY f4468261cf [Fabric] Update to 25w20a 2025-05-13 16:50:24 +02:00
NEZNAMY cc926ff974 [Modded platforms] Decrease required Java version to 17 (where possible) 2025-05-07 17:39:33 +02:00
NEZNAMY 6c3831ded4 [Fabric] Update to 25w19a 2025-05-07 17:30:29 +02:00
NEZNAMY 3c55866d46 [Code style] Move 1.8+ check to avoid duplicating it everywhere 2025-05-01 18:26:56 +02:00
NEZNAMY dfc9359f04 [Plugin messages] Do not write protocol version anymore, since it is not used by bridge anymore 2025-05-01 18:18:09 +02:00
NEZNAMY ea0897db31 [Bukkit] Remove redundant client version usage 2025-05-01 18:12:53 +02:00
NEZNAMY 25cf6c95e8 [Configuration] Print exception class when a file fails to save 2025-05-01 14:49:33 +02:00
NEZNAMY f5e2910e0b [Bukkit] Remove forgotten field from previous commit 2025-05-01 14:36:50 +02:00
NEZNAMY fda35986a4 [Bukkit] Use getters instead of reflection where possible and fix wrong field on 1.16.5 2025-05-01 14:35:59 +02:00
NEZNAMY b97e86c2ed [Wiki] Monthly wiki backup for May 2025 2025-05-01 13:43:03 +02:00
NEZNAMY ebb02717b5 [Scoreboard internals] Fix duplicated team / objective register actually overwriting existing value, causing NPE later (#1481) 2025-05-01 13:16:22 +02:00
NEZNAMY 1b82ebfd25 [Paper] Update version to fix compilation error 2025-05-01 13:10:59 +02:00
NEZNAMY 3fb788ef6d [Fabric] Update to 25w18a 2025-04-29 18:53:55 +02:00
NEZNAMY 11bc06182c [Scoreboard] Use available getter instead of reflection / access transforming (which may not work) 2025-04-27 13:57:22 +02:00
NEZNAMY 2fe5dc833d [Proxy support] Add more debug messages (#1465) 2025-04-27 10:02:56 +02:00
NEZNAMY 317cb975ce [Threading] Avoid throwing error 2025-04-27 09:31:21 +02:00
NEZNAMY 9bb2a97e50 [Threading] Increase thread shutdown timeout from 500ms to 2000ms 2025-04-27 09:28:36 +02:00
NEZNAMY 53dcc60587 [Code style] Use java 21 switches in modded platforms 2025-04-26 17:51:57 +02:00
NEZNAMY db347bc192 [Modded platforms] Create component style with setters to avoid using access transforming 2025-04-26 13:13:38 +02:00
NEZNAMY 608b8307dd [Velocity] Fix error in compatibility check 2025-04-26 12:56:58 +02:00
NEZNAMY 4c027da093 [Modded platforms] Use reflection to access tablist entries field instead of direct access (since access transforming sometimes doesn't work) 2025-04-26 12:52:10 +02:00
NEZNAMY bef60e688c [Bukkit] Fix prefix used in suffix field 2025-04-24 16:56:34 +02:00
NEZNAMY b987ad99f4 [Fabric] Update to 25w17a 2025-04-22 20:56:17 +02:00
NEZNAMY 3036e2d523 [Bukkit] Fix anti-override not working when using a ViaVersion implementation instead of server (#1478) 2025-04-19 11:33:34 +02:00
NEZNAMY 1bab2d0025 [Fabric] Update to 25w16a 2025-04-15 16:56:27 +02:00
NEZNAMY 4cac6c659e [Bukkit] Add v1_19_R1 submodule and remove reflection support for <1.19.3 2025-04-13 20:08:45 +02:00
NEZNAMY c7c59238e1 [Bukkit] Refactor feature implementation loading 2025-04-13 18:16:05 +02:00
NEZNAMY f51e74c066 [Bukkit] Update supported version list 2025-04-13 11:37:08 +02:00
NEZNAMY 52f8801638 [Bukkit] Add 1.17.1 and 1.18.2 submodules and delete the respective NMS reflection 2025-04-13 11:20:18 +02:00
NEZNAMY 4e5c5a7073 [Bukkit] [Code style] Change bossbar presence check 2025-04-13 09:03:49 +02:00
NEZNAMY 436f3879c4 [Bukkit] Remove redundant check 2025-04-13 08:36:56 +02:00
NEZNAMY c1395e26d4 [Fabric] Update to 25w15a 2025-04-09 17:32:03 +02:00
NEZNAMY 3750d6ddb1 [Commands] Fix error when running a property command 2025-04-07 19:02:02 +02:00
NEZNAMY 45c3601b49 [Conditions] Add "!permission:<value>" condition type 2025-04-07 18:29:24 +02:00
NEZNAMY 957da1d040 [Code style] Convert some NotNull annotations to NonNull to throw errors sooner 2025-04-07 18:25:46 +02:00
NEZNAMY 43932eb6b3 [Compilation] More changes to make backporting of mod platforms easier 2025-04-06 19:07:54 +02:00
NEZNAMY 024ddeefb4 [Compilation] Create maps for mod platform versions for easier backporting 2025-04-06 12:30:37 +02:00
NEZNAMY 94190b5cd7 [Velocity] Use new setShowHat tablist feature (and bump minimum required build to 485) 2025-04-05 10:15:55 +02:00
NEZNAMY 5d72f90a0f [Code style] Remove redundant code 2025-04-04 17:36:37 +02:00
NEZNAMY 90c30023c2 [Code style] Simplify 2025-04-02 20:27:59 +02:00
NEZNAMY d39a89659b [Bukkit] Remove reflection NMS support for 1.16.x and use direct 1.16.5 NMS hook 2025-04-02 20:23:36 +02:00
NEZNAMY 79f4386b62 [Bukkit] Remove reflection NMS support for versions below 1.16 (1.8.8 and 1.12.2 are supported directly) 2025-04-02 18:47:09 +02:00
NEZNAMY 39c4d8ae96 [Bukkit] Only send compatibility error messages relevant to server version 2025-04-02 18:12:33 +02:00
NEZNAMY c8b2abbb3e Merge pull request #1470 from JT122406/master
add curseforge link
2025-04-02 18:10:57 +02:00
NEZNAMY 06b7bbd711 [Bukkit] Fix startup error on 1.7.10 2025-04-02 18:05:20 +02:00
NEZNAMY 11afd15610 [Placeholder output replacements] Remove color replacing in output (may not be wanted if used in tab expansion) 2025-04-02 18:04:30 +02:00
Joseph T. McQuigg 4951025e61 add curseforge link 2025-04-02 09:54:48 -04:00
NEZNAMY 2337a122da [Wiki] Monthly wiki backup for April 2025 2025-04-01 17:23:23 +02:00
NEZNAMY 072333bf48 [Description] Use .svg instead of .png 2025-04-01 16:01:38 +02:00
NEZNAMY 2bd8924df7 [Description] Save badges for easier modification in only one place (will be linked to in all descriptions) 2025-04-01 15:58:28 +02:00
NEZNAMY 23cdc3721d [Global playerlist] Fix offline players being added into tablist when disconnecting immediately after switching server 2025-03-31 20:17:35 +02:00
NEZNAMY 957e4ba0c0 5.2.1-SNAPSHOT 2025-03-31 20:16:28 +02:00
NEZNAMY 71a19c63d8 5.2.0 2025-03-31 17:45:05 +02:00
NEZNAMY 657327e7ee [Bukkit] Move viaversion presence check to fix class loader error when it's not present 2025-03-31 17:44:50 +02:00
NEZNAMY efeea7cf0f Merge pull request #1460 from JT122406/master
Forge and NeoForge Support
2025-03-31 17:20:48 +02:00
Joseph T. McQuigg 7a4637368d Hard Versions 2025-03-31 11:18:36 -04:00
NEZNAMY 2e6ccd078b 5.2.0-SNAPSHOT 2025-03-31 17:09:19 +02:00
NEZNAMY 827197f916 5.1.1 2025-03-31 16:52:47 +02:00
Joseph T. McQuigg c8454ec0c1 Add MC versions to mods.toml 2025-03-31 10:52:34 -04:00
Joseph T. McQuigg 7127a77d92 Remove Versions from mods.toml, Fix register 2025-03-31 10:30:31 -04:00
Joseph T. McQuigg b23912b52f Forge/NeoForge Luckperms Hook 2025-03-30 18:57:32 -04:00
Joseph T. McQuigg 4b21148a03 Adjust Scoreboard classes for setDisplaySlot 2025-03-30 15:06:37 -04:00
Joseph T. McQuigg 2d5ef95e54 Update Forge to 1.21.5 2025-03-30 14:52:00 -04:00
Joseph T. McQuigg b5e2d376dd Update NeoForge to 1.21.5 2025-03-30 14:52:00 -04:00
Joseph T. McQuigg c40b337edc Fix Credits 2025-03-30 14:52:00 -04:00
Joseph T. McQuigg f8583642a8 Correct License 2025-03-30 14:52:00 -04:00
Joseph T. McQuigg 707f28df16 Permission check 2025-03-30 14:52:00 -04:00
Joseph T. McQuigg dd78da089d Use Records for platform 2025-03-30 14:51:59 -04:00
Joseph T. McQuigg c4ea65ed70 Forge Port 2025-03-30 14:51:59 -04:00
Joseph T. McQuigg aabba806c4 Drop Parchment 2025-03-30 14:51:59 -04:00
Joseph T. McQuigg 88167fc42d Default to EmptyTabExpansion 2025-03-30 14:51:59 -04:00
Joseph T. McQuigg e4d7005e46 moddedPlatforms List 2025-03-30 14:51:59 -04:00
Joseph T. McQuigg 5ee27f7ed7 Add Credits 2025-03-30 14:51:59 -04:00
Joseph T. McQuigg 9a4492c401 Add Parchment to Fabric, Use LogUtils instead of AW 2025-03-30 14:51:59 -04:00
Joseph T. McQuigg 39a2aee5a0 NeoForge Port 2025-03-30 14:51:58 -04:00
Joseph T. McQuigg 5184ef170f Update to new com.gradleup.shadow plugin
Gradle 8.13, Fabric Loom 1.10
2025-03-30 14:51:56 -04:00
NEZNAMY 29a35cf1cc [ViaVersion hook] Decrease check interval from 100 to 50 2025-03-30 18:51:39 +02:00
NEZNAMY f26516ecae [Code style] Separate objective register and set display slot functions 2025-03-30 18:47:39 +02:00
NEZNAMY f26a7ad2b1 [Bukkit] Add direct NMS hook for v1_8_R3 and v1_12_R1 2025-03-30 17:46:23 +02:00
NEZNAMY 96e0a29573 Merge pull request #1464 from MineLAT/fix/viaversion-delay
Fix ViaVersion packets delay
2025-03-29 17:12:29 +01:00
NEZNAMY f30d667ca8 [Code style] Add return value annotations and remove unused methods in ConfigurationFile 2025-03-29 17:05:53 +01:00
Rubén 8db7a1d6d9 Fix ViaVersion packets delay 2025-03-29 10:04:46 -06:00
NEZNAMY d285a0512e [Multi-server support] Provide some feedback about connection status (success/fail) (doesn't work for redis) 2025-03-29 16:52:13 +01:00
NEZNAMY 256d0d5c84 Revert "[Placeholders] Parse nested PAPI placeholders using {}"
This reverts commit 6501480980.
2025-03-29 09:57:02 +01:00
NEZNAMY b808e1f9d1 [Bukkit] Refactor scoreboard / tablist implementation finding for players 2025-03-29 09:55:52 +01:00
NEZNAMY 07cd781ec1 [Sponge] Drop Sponge 7 module 2025-03-28 15:43:37 +01:00
NEZNAMY 9115b687f4 [Sponge] Drop support for sponge versions 1.16.5 - 1.20.5 2025-03-27 18:51:28 +01:00
NEZNAMY 7c51377203 [Bukkit] Update paper submodule for 1.21.5 and allow it there 2025-03-27 18:24:02 +01:00
NEZNAMY 193cd02072 [Configuration] Fix startup error when some config options were missing 2025-03-27 16:49:01 +01:00
NEZNAMY dbb6d91f0c [Configuration] Add missing playerlist-objective.render-type option to default config 2025-03-26 19:48:20 +01:00
NEZNAMY a82714e811 [BungeeCord] Fix error (#1462) 2025-03-26 19:00:53 +01:00
NEZNAMY bcd1bbac62 5.1.1-SNAPSHOT 2025-03-26 18:53:58 +01:00
NEZNAMY 2fde0e68ca 5.1.0 2025-03-26 17:53:56 +01:00
NEZNAMY e6e9b1c5ae Add 1.21.5 protocol constant 2025-03-25 18:46:53 +01:00
NEZNAMY 48b789c51d [Fabric] Update to 1.21.5 2025-03-25 18:36:41 +01:00
NEZNAMY 433b39209d [Fabric] Remove server version detection as it is no longer used 2025-03-24 18:26:56 +01:00
NEZNAMY b6df7a9f33 [Code style] Improve parity between Fabric and Paper Scoreboard implementations 2025-03-24 17:16:54 +01:00
NEZNAMY ad1d55537f [Code style] Improve parity between Fabric and Paper TabList implementations 2025-03-24 17:02:35 +01:00
NEZNAMY 9982d2c6ef [Fabric] Update to 1.21.5-rc2 2025-03-24 16:32:17 +01:00
NEZNAMY 1e3f622a98 Remove server version checks for new content as everything is now supported thanks to new ViaVersion hook 2025-03-24 16:26:31 +01:00
NEZNAMY ce05d5f6a6 Merge pull request #1443 from MineLAT/feat/viaversion-packets
Add ViaVersion packets support
2025-03-24 16:15:50 +01:00
NEZNAMY 19378e2095 [RGB] Fix gradient pattern for <#RRGGBB></#RRGGBB> to not capture a newer color used inside of it (caused by using one of MiniMessage's RGB syntaxes) 2025-03-23 18:47:57 +01:00
Rubén 0d992f1e50 Execute delayed tasks at once 2025-03-23 10:39:10 -06:00
Rubén de7cabfd54 Add missing method call 2025-03-23 09:31:10 -06:00
Rubén cfe73a00ee Fix early packet send on viaversion decoder 2025-03-23 09:06:11 -06:00
Rubén 35a7acbbd9 Merge remote-tracking branch 'origin/master' into feat/viaversion-packets
# Conflicts:
#	bukkit/src/main/java/me/neznamy/tab/platforms/bukkit/BukkitTabPlayer.java
#	bukkit/src/main/java/me/neznamy/tab/platforms/bukkit/platform/BukkitPlatform.java
2025-03-23 08:53:00 -06:00
NEZNAMY 20eebca395 [Belowname & Playerlist objective] Add startup warn if configured "value" will never evaluate to a number 2025-03-23 15:47:32 +01:00
NEZNAMY 7c2a4deb1a [Code style] Move getSkin() method to TabList 2025-03-23 13:40:27 +01:00
NEZNAMY e227e6a0f4 [Bukkit] Add back Paper submodule, but only for the latest MC version instead of an actual supported range 2025-03-23 13:09:30 +01:00
NEZNAMY e015c29a3d [Code style] Move legacy color rgb codes to TextColor class 2025-03-23 11:26:13 +01:00
NEZNAMY a2859937e2 [Bukkit] [Performance] Avoid using reflection for getting online players on 1.8+ 2025-03-22 19:53:59 +01:00
NEZNAMY cd26252444 [Scoreboard] Fix error when more than 21 lines are defined (will now error at 100) 2025-03-22 10:42:03 +01:00
NEZNAMY 47894a6e74 [Performance] Tiny performance boost of %luckperms-prefix% and suffix placeholders 2025-03-20 18:37:37 +01:00
NEZNAMY bcf94180d5 [Fabric] Update Fabric to 1.21.5-rc1 2025-03-20 18:33:00 +01:00
NEZNAMY 6d2eeda28c Merge pull request #1457 from Radu-Voinea/master2
Added %luckperms-weight% as a built-in placeholder
2025-03-20 12:09:57 +01:00
Radu Voinea 049c768013 Moved player lookup from LP to TabPlayer 2025-03-20 13:05:53 +02:00
Radu Voinea ffae488d1e Removed auto-formatted text 2025-03-20 12:54:36 +02:00
NEZNAMY 3ea0ae6e5b [Performance] Create a constant for empty component to avoid recreating it over and over 2025-03-19 17:54:56 +01:00
NEZNAMY 530c25655c [Error logging] Add missing " into error message 2025-03-19 17:44:03 +01:00
NEZNAMY ca830fd64c [Playerlist Objective] Make render type explicitly configurable 2025-03-19 17:42:16 +01:00
NEZNAMY 4272476230 [Playerlist Objective] Make title configurable (#1454) 2025-03-19 17:05:26 +01:00
NEZNAMY b7e44246ce [Fabric] Update to 1.21.5-pre3 2025-03-18 16:54:18 +01:00
NEZNAMY 33b9578fd1 [BossBar] Add %bossbar_announce_time_left_<bossbar>% and %bossbar_announce_time_total_<bossbar>% placeholders 2025-03-16 15:03:59 +01:00
NEZNAMY df0a96843d [Error Logging] Fix typo 2025-03-16 15:00:32 +01:00
NEZNAMY 5b5f7dfc12 [Code style] Remove unused fields 2025-03-15 16:49:21 +01:00
NEZNAMY 8b094adcca [Nametags] Add a lot more subcommands for manipulating nametag visibility 2025-03-15 16:48:34 +01:00
NEZNAMY de9d9ab844 [Nametags] Refactor classes and create enum for nametag invisibility reason 2025-03-13 18:08:17 +01:00
NEZNAMY 2fd6d2f50b [Fabric] Update to 1.21.5-pre2 2025-03-13 16:35:37 +01:00
Rubén 349c9eca4b Rename classes for more consistency 2025-03-12 18:18:09 -06:00
Rubén 1f119233d7 Move via module into bukkit module 2025-03-11 18:47:46 -06:00
Rubén 17c2d63892 Use instance method 2025-03-11 14:06:13 -06:00
NEZNAMY 79c76e913d [Fabric] Update to 1.21.5-pre1 2025-03-11 17:06:48 +01:00
NEZNAMY 6501480980 [Placeholders] Parse nested PAPI placeholders using {} 2025-03-08 13:40:58 +01:00
NEZNAMY 5ad9bbba37 [Global playerlist] Simplify expression (servers are never equal) 2025-03-08 09:44:31 +01:00
NEZNAMY f012448758 [Description] Improve image generator script 2025-03-07 20:25:11 +01:00
Rubén 99b6d635d7 Remove unchecked object handling 2025-03-07 08:37:14 -06:00
Rubén 5f742627e9 Avoid component italic format being reset 2025-03-07 07:58:19 -06:00
Rubén a9e725002a Use simple text component 2025-03-07 07:51:16 -06:00
Rubén 49150f9b1e Remove ViaVersion configuration support 2025-03-06 22:43:49 -06:00
Rubén 89aa1ba94c Fix class cast 2025-03-06 22:32:14 -06:00
Rubén 6aca313bd8 Use dummy object 2025-03-06 22:31:49 -06:00
Rubén f76d5a958f Fix component writing on newer versions 2025-03-06 22:15:51 -06:00
NEZNAMY b833a10de4 [Fabric] Update to 25w10a 2025-03-05 17:14:38 +01:00
Rubén f3ce5632fb Add safe check for ViaVersion internals usage 2025-03-05 07:54:09 -06:00
Rubén f5bf4c3831 Merge component conversion methods 2025-03-04 07:27:01 -06:00
Rubén 08a3acc5e0 Use direct return for viaversion providers 2025-03-04 07:26:13 -06:00
Rubén 5b63034607 Apply component style on compatible components 2025-03-03 07:36:33 -06:00
Rubén d4543032d9 Use the correct annotation 2025-03-02 09:25:52 -06:00
Rubén 254a91c3bd Fix legacy text check 2025-03-02 09:18:46 -06:00
NEZNAMY 4c01ddaaef [Description] Remove now no longer used images 2025-03-02 14:10:06 +01:00
NEZNAMY 2993d1450a [Description] Change text into code-generated images 2025-03-02 13:49:37 +01:00
Rubén 582c3a4c1a Initial ViaVersion packets support 2025-03-01 16:31:24 -06:00
NEZNAMY dd6b964405 [Fabric] Remove support for all versions below 1.21.4 2025-03-01 18:31:50 +01:00
NEZNAMY 16827bfbac Update references to version 5.1.0 2025-03-01 18:05:53 +01:00
NEZNAMY 3c4d72bb71 Bump version to 5.1.0-SNAPSHOT 2025-03-01 17:26:30 +01:00
NEZNAMY 004036e019 [BungeeCord / Velocity] Read server list from configuration for %online_<server>% placeholders instead of dynamically registering them when used 2025-03-01 16:15:03 +01:00
NEZNAMY ba06f80baa [BungeeCord / Velocity] Block bridge communication messages from player to avoid exploits 2025-03-01 14:07:50 +01:00
NEZNAMY ee47428a80 [Floodgate hook] Remove warning when floodgate is not configured correctly 2025-03-01 09:10:23 +01:00
NEZNAMY 1e40cae27b [Wiki] Monthly wiki backup for March 2025 2025-03-01 08:46:19 +01:00
NEZNAMY f5b7bb5549 [Belowname] Rename messages to use belowname "value" instead of "number" 2025-02-28 16:58:57 +01:00
NEZNAMY 115883f8d0 [Fabric] Update to 25w09b 2025-02-28 16:57:37 +01:00
NEZNAMY a73446a1c5 [Placeholders] Make LP placeholders also use configured value instead of permission refresh interval 2025-02-27 18:45:37 +01:00
NEZNAMY 7501c4f76b [Commands] Make placeholder refresh interval color in cpu output different if it was overridden in config 2025-02-27 18:40:46 +01:00
NEZNAMY 9f038fae63 [Placeholders] Allow placeholder refresh intervals to define overrides for internal placeholders 2025-02-27 18:21:49 +01:00
NEZNAMY a61a520570 [Fabric] Update to 25w09a 2025-02-26 18:27:51 +01:00
NEZNAMY 885783cd9a [Bukkit] Refactor feature provider loaders 2025-02-25 17:16:54 +01:00
NEZNAMY 181f850c58 Fix compilation 2025-02-23 17:39:25 +01:00
NEZNAMY fe8742b580 Merge pull request #1435 from MineLAT/feat/proxy-support
Expand multi-server support
2025-02-23 17:11:33 +01:00
Rubén daeba7caa9 Remove unused belowname code 2025-02-23 10:01:29 -06:00
NEZNAMY 1340e6dc1b [Code style] [Bukkit] Refactor team packet data 2025-02-23 09:24:27 +01:00
NEZNAMY cba7655f0d [Code style] Move method into a different class 2025-02-22 18:32:31 +01:00
NEZNAMY ac7e2a9aef [RGB] Fully support magic codes inside gradients, they no longer must all be at the beginning 2025-02-22 18:27:33 +01:00
Rubén fcc0bfbf32 Optimize dependencies 2025-02-22 10:40:58 -06:00
NEZNAMY a7e86bdb64 [Fabric] Update to 25w08a 2025-02-19 17:57:19 +01:00
Rubén c3eecd8d14 Remove duplicated check 2025-02-18 10:39:48 -06:00
Rubén 25e6da3ac5 Add safe check for already connected players 2025-02-18 10:37:49 -06:00
Rubén a7e5786fee Use global player list instead 2025-02-18 09:56:48 -06:00
Rubén a6bccc3dd3 Add missing config values 2025-02-17 18:12:19 -06:00
Rubén d8cd39dc3e Remove duplicated yellow number send 2025-02-17 14:13:14 -06:00
Rubén 6eacf833f5 Remove duplicated name tag send 2025-02-17 14:11:54 -06:00
Rubén dbdd417e0d Avoid bad configuration update 2025-02-17 14:11:30 -06:00
Rubén 4c8dd52f28 Ignore same player 2025-02-17 13:56:46 -06:00
Rubén 31a2d8a02b Add supported types 2025-02-17 12:49:38 -06:00
Rubén f246d57a7e Add backend messaging 2025-02-17 12:37:15 -06:00
Rubén 4ae856630f Add proxy support via messenger 2025-02-17 12:02:27 -06:00
Rubén 8652baf2ae Rename redis feature 2025-02-17 11:50:18 -06:00
NEZNAMY 4a18853e8c [BungeeCord] Remove warning about a waterfall option as it is now long abandoned 2025-02-16 19:58:03 +01:00
NEZNAMY a60be9d8c1 [Spectator fix] Fix not updating on proxies (#1434) 2025-02-16 19:21:07 +01:00
NEZNAMY e16b47409c [Code style] Keep expected tablist display names as TabComponent instead of platform components 2025-02-16 17:45:57 +01:00
NEZNAMY 6f14cbe746 5.0.8-SNAPSHOT 2025-02-16 16:32:10 +01:00
NEZNAMY ea79c9c5dd 5.0.7 2025-02-16 11:27:35 +01:00
NEZNAMY 650d13ed71 [Reloading] Fix plugin messages being ignored on Velocity on reload due to a check not working correctly 2025-02-15 09:11:50 +01:00
NEZNAMY 169b8b8e94 [Scoreboard] Fix scoreboard chain misconfiguration detection not working correctly 2025-02-14 16:57:15 +01:00
NEZNAMY 8cef4a5bcb [Fabric] Update to 25w07a 2025-02-13 19:16:32 +01:00
NEZNAMY 70b40802cc [Fabric] Remove redundant function split based on server version 2025-02-11 17:45:53 +01:00
NEZNAMY 416e707cda Revert "[Code style] Create a separate module for bossbar API"
This reverts commit 042ab6850b.
2025-02-11 16:32:05 +01:00
NEZNAMY 12578293bc [Bukkit] Delete paper submodule as it was not worth it 2025-02-10 17:14:46 +01:00
NEZNAMY 042ab6850b [Code style] Create a separate module for bossbar API 2025-02-09 20:03:17 +01:00
NEZNAMY 09fc042630 [Chat components] Fix magic codes not being assigned correctly when converting from an adventure component 2025-02-09 14:55:58 +01:00
NEZNAMY c73d351bf1 [Configuration] Update default config to avoid unwanted bold text caused by MiniMessage 2025-02-09 12:42:52 +01:00
NEZNAMY 4d4e0a9c2a [BungeeCord & Velocity] Make compatibility error message more visible 2025-02-08 13:03:25 +01:00
NEZNAMY 2fd29b2347 [Configuration] Disable SnakeYAML's max file size limit (layout skincache can exceed the default 3MB) 2025-02-08 12:45:19 +01:00
NEZNAMY f332068318 [Fabric] Fix 1.16.5 support 2025-02-08 12:28:52 +01:00
NEZNAMY 186c79f93c [Bukkit] Split component converter into multiple classes based on server version interval for better maintainability 2025-02-08 10:19:06 +01:00
NEZNAMY 3770066485 [Code style] Move component folder into a new separate module 2025-02-07 16:19:48 +01:00
NEZNAMY 17b0ffe52b [Bukkit] Fix component style magic codes conversion 2025-02-07 15:35:23 +01:00
NEZNAMY d259b5bb78 5.0.7-SNAPSHOT 2025-02-06 18:05:23 +01:00
NEZNAMY f6c8d24177 5.0.6 2025-02-06 17:54:47 +01:00
NEZNAMY ab3bd61d3c [Bukkit] Fix error 2025-02-06 17:27:46 +01:00
NEZNAMY 6a5cee8f0e [Fabric] Update to 25w06a 2025-02-05 20:15:43 +01:00
NEZNAMY 549732fc8b [Chat components] No longer track modern/legacy converted components, only handle them internally in bungeecord module 2025-02-05 19:13:56 +01:00
NEZNAMY 09d400c6fb [GitHub] Bump version to fix actions 2025-02-05 18:33:49 +01:00
NEZNAMY 7e1cdafa78 [Bukkit] Only enable packetevents bug compensation <1.13 where it's actually needed to avoid scoreboard gradient bug due to forced splitting when the option is enabled and doesn't need to be 2025-02-04 18:39:25 +01:00
NEZNAMY 434c0216d5 [Adventure hook] Fix startup error when an older version of the library is present 2025-02-04 18:28:34 +01:00
NEZNAMY d5a0ae8e71 [Scoreboard] Fix memory leak 2025-02-03 16:56:04 +01:00
NEZNAMY 79e56c6a30 [Chat components] Implement shadowColor field (currently only used for converting components created using MiniMessage) 2025-02-02 17:54:57 +01:00
NEZNAMY 11cc1c3f25 [Code style] Remove unused constructor 2025-02-02 16:12:53 +01:00
NEZNAMY 8b343bcfb6 [Chat components] Fix NPE 2025-02-02 15:36:25 +01:00
NEZNAMY ecd83beb7d [Performance] Replace stream with for loop in vanish check 2025-02-02 14:14:57 +01:00
NEZNAMY 438f21fb11 [BungeeCord] Update to build 1899 (and require it) 2025-02-01 20:43:05 +01:00
NEZNAMY dd1524cf31 [Dependencies] Bump and remove unnecessary 2025-02-01 18:40:02 +01:00
NEZNAMY 0f85aa5384 Fix broken messages from previous commit 2025-02-01 18:23:04 +01:00
NEZNAMY 4644a0ae96 [Code style] Further reduce references to EnumChatFormat class 2025-02-01 18:18:38 +01:00
NEZNAMY 84b3efe0fc [Code style] Simplify some component creations 2025-02-01 17:07:48 +01:00
NEZNAMY 511acbd5ff [Chat components] Rework component creation and converters 2025-02-01 16:45:20 +01:00
NEZNAMY 8749b3b354 [Wiki] Monthly wiki backup for February 2025 2025-02-01 13:09:27 +01:00
NEZNAMY b66e63d242 [Fabric] Implement adventure component conversion to MC components (#1425) 2025-02-01 09:43:03 +01:00
NEZNAMY ba951238c3 [Fabric] Update to 25w05a 2025-01-29 20:08:49 +01:00
NEZNAMY a4eb76f1cf 5.0.6-SNAPSHOT 2025-01-29 20:08:17 +01:00
NEZNAMY c486899dcf 5.0.5 2025-01-29 17:37:10 +01:00
NEZNAMY b212f0d5be [Velocity] Fix some console messages not being colored correctly 2025-01-27 18:44:55 +01:00
NEZNAMY caa534ff7f [MySQL] Fix upload command using wrong file path 2025-01-26 12:15:41 +01:00
NEZNAMY 9c2fac6749 [MiniMessage] Fix critical error when an ancient MM version is present 2025-01-25 19:12:47 +01:00
NEZNAMY 2a4f918a10 [RGB] Convert all RGB/Gradients for MiniMessage parser (#1408, #1414, #1421) 2025-01-25 08:55:22 +01:00
NEZNAMY b45e2afe4c [Code style] Move classes to a different package 2025-01-24 19:16:07 +01:00
NEZNAMY 2c7237e5d9 [RGB] Remove early gradient conversion (for future commits) 2025-01-24 19:10:59 +01:00
NEZNAMY 26477e3150 5.0.5-SNAPSHOT 2025-01-23 18:27:20 +01:00
NEZNAMY cba8cb57d5 5.0.4 2025-01-23 17:24:16 +01:00
NEZNAMY 599b4822e5 [Fabric] Update to 25w04a 2025-01-22 18:31:03 +01:00
NEZNAMY fbd150b353 [API] Expose TabPlayer#getServer, TabPlayer#getWorld and TabPlayer#isBedrockPlayer 2025-01-21 17:33:10 +01:00
NEZNAMY 10b252e934 [Placeholders] Fix placeholders staying incorrectly marked as bridge placeholders under very specific conditions (#1416) 2025-01-19 21:36:27 +01:00
NEZNAMY e9dfcdeb10 [Layout] If trying to use skin of an online player in fixed slot, try to get it from the player's profile if online 2025-01-19 18:16:04 +01:00
NEZNAMY 9cb2f27754 [Error management] Do not print full stack traces when a skin fails to download 2025-01-19 17:41:48 +01:00
NEZNAMY a090a83b7d [Debug] Replace debug messages about downloading skin with downloaded and include time it took 2025-01-19 17:04:44 +01:00
NEZNAMY 992e661e4c [Placeholders] Fix bridge overriding placeholders if they did not exist before a reload (such as conditions) (#1416) 2025-01-19 15:59:51 +01:00
NEZNAMY 3b993d114e [Velocity] Fix errors when plugin is disabled due to a broken configuration file 2025-01-19 15:21:34 +01:00
NEZNAMY d181d62466 [Code style] Call EnumChatFormat#getFormat instead of toString 2025-01-19 14:55:46 +01:00
NEZNAMY 968cef7a12 [Scoreboard & Nametags] Add a config option to enforce <1.13 limits even for 1.13+ players (#1412, #1413) 2025-01-19 13:48:39 +01:00
NEZNAMY 390c2395eb Fix compilation error from previous commits 2025-01-19 13:34:18 +01:00
NEZNAMY adc9444794 [Code style] Make code more readable 2025-01-19 13:25:20 +01:00
NEZNAMY f086318082 [Chat components] Remove redundant checks for player's version 2025-01-19 13:21:18 +01:00
NEZNAMY 1cef4400b9 [Scoreboard] Fix ConcurrentModificationException when using API (#1410) 2025-01-18 14:23:13 +01:00
NEZNAMY 1f879ac91e [Scoreboard] Fix setText API method breaking a line if setting it to empty value (#1410) 2025-01-18 14:12:00 +01:00
NEZNAMY bec129d98e [Scoreboard & Bossbar] Save toggled players by UUID instead of names 2025-01-18 10:44:22 +01:00
NEZNAMY 152c04e68f [Configuration] Remove file header restoration on file modification 2025-01-18 10:41:13 +01:00
NEZNAMY d6cdacef43 [Layout] Fix skins not being saved into memory when downloaded 2025-01-16 19:02:38 +01:00
NEZNAMY 09891a075d [Layout] Add debug messages when a skin is about to be downloaded 2025-01-16 19:02:00 +01:00
NEZNAMY 9500bab5b7 [Scoreboard] Fix resetScoreboard not applying scoreboard correctly (#1411) 2025-01-16 18:15:31 +01:00
NEZNAMY 231edd4bd4 [Chat components] Fix magic code null values being converted to false 2025-01-15 18:55:03 +01:00
NEZNAMY 433572e759 [Fabric] Update to 25w03a 2025-01-15 18:06:51 +01:00
NEZNAMY 3ec772e87f [Feature management] Load all features in plugin's main thread to avoid API calls from load event before a feature actually loads (#1409) 2025-01-10 21:55:55 +01:00
NEZNAMY ae8287157c [Scoreboard] Do not add API scoreboards to the chain of scoreboards to go through (#1409) 2025-01-10 21:32:42 +01:00
NEZNAMY f005907b41 [Fabric] Update to 25w02a 2025-01-09 18:44:55 +01:00
NEZNAMY 8ea16044f1 [Debug] Send a correct message if player does not have any group permission 2025-01-08 18:34:06 +01:00
NEZNAMY e787a28353 [Tablist formatting] Fix formatting applying even if disabled with condition sometimes 2025-01-06 18:59:32 +01:00
NEZNAMY 6925d9564c [Code style] Create Platform#getCommand method instead of storing it in TabConstants 2025-01-04 18:43:37 +01:00
NEZNAMY ccd4ea785a [Logging] Fix outdated value in a message 2025-01-04 18:27:29 +01:00
NEZNAMY 63e88c09d4 [MySQL] Add a startup warn message if database is empty 2025-01-04 18:24:51 +01:00
NEZNAMY 296191b502 [Code style] Replace uses of EnumChatFormat for color codes with the codes directly 2025-01-02 19:20:49 +01:00
NEZNAMY af0151f446 [RGB] Remove now redundant check for modern/legacy colors (partially) 2025-01-02 16:26:06 +01:00
NEZNAMY 2bdfaa167e [RGB] Remove support for |L for manually defining legacy colors 2025-01-02 15:56:49 +01:00
NEZNAMY cd96476f9e [Belowname] Improve a warning message 2025-01-01 19:16:12 +01:00
NEZNAMY 9b0545aa1a [Layout] Fix players on top of tablist if team implementation is not available 2025-01-01 18:57:49 +01:00
NEZNAMY 63823123f8 [BungeeCord] Simplify code 2025-01-01 14:27:43 +01:00
NEZNAMY e91728be98 [Sponge 8] Update sponge gradle plugin to fix warning 2025-01-01 14:02:00 +01:00
NEZNAMY e7ecbf1245 [Velocity] Remove usage of deprecated API 2025-01-01 13:54:17 +01:00
NEZNAMY 28c2363b67 [Wiki] Monthly wiki backup for January 2025 2025-01-01 13:12:21 +01:00
NEZNAMY 21a3d4fb55 [Paper] Fix mistake in magic codes (#1404) 2024-12-30 14:10:52 +01:00
NEZNAMY 4b459a3ed1 [Commands] Add a red message to debug command if assigning groups by permissions and the player has all permissions 2024-12-29 20:09:31 +01:00
NEZNAMY ebc6a1b842 Fix paper compilation 2024-12-29 20:08:10 +01:00
NEZNAMY b72b0f269a [Code style] Remove redundant warning suppression 2024-12-21 17:54:06 +01:00
NEZNAMY 4cbcd69ed2 [Code style] Remove redundant constructor 2024-12-21 17:45:13 +01:00
NEZNAMY 6ef8b7748c [Code style] Remove redundant double coloring 2024-12-21 17:05:35 +01:00
NEZNAMY 8b567cb976 [Code style] Optimize last color of component calculation 2024-12-21 16:15:04 +01:00
NEZNAMY d8c231b9ce [Code style] Further reduce usage of EnumChatFormat class 2024-12-21 15:49:33 +01:00
NEZNAMY cc4347c881 [Code style] Make TabComponent#fetchLastColor Nullable 2024-12-21 13:19:20 +01:00
NEZNAMY 7cc572feb5 [Code style] Replace some references to EnumChatFormat with the actual color codes 2024-12-21 11:58:37 +01:00
NEZNAMY 96d1d1feb4 [Code style] Use TextColor as team color internally to prepare for a potential future MC update 2024-12-21 11:39:36 +01:00
NEZNAMY a7d9bd9ab5 [Code style] Simplify TabComponent#toRawText implementation 2024-12-21 11:15:47 +01:00
NEZNAMY 1cdfdf1d43 [Code style] Simplify 2024-12-18 17:32:54 +01:00
NEZNAMY 0762a5e689 [Code style] Remove redundant method call 2024-12-18 17:22:55 +01:00
NEZNAMY 3e2906ccf8 [Dependencies] Version bump and remove unused 2024-12-18 17:18:09 +01:00
NEZNAMY 13761e032c [Bukkit] Set shadowColor to null instead of 0 (no shadow) (#1400) 2024-12-16 18:51:02 +01:00
NEZNAMY fb8fa3fa54 [MiniMessage] Implement Keybind component support as well (on 1.19+) 2024-12-14 11:34:02 +01:00
NEZNAMY b89bf5933b [MiniMessage] Add support for its translatable components (paper / velocity only) (#1399) 2024-12-13 22:40:20 +01:00
NEZNAMY 1c4f404fd2 [Code style] Remove unused method 2024-12-13 22:22:51 +01:00
NEZNAMY 9fe1560f1a [Sorting] Remove runtime warn spam when configured sorting placeholder is invalid (only keep startup warn) 2024-12-12 18:58:49 +01:00
NEZNAMY 6a9cca37c6 [Scoreboard] Fix toggle command being completely ignored when another plugin has sent scoreboard instead of processing it (to some degree) 2024-12-10 21:30:20 +01:00
NEZNAMY bfde75d4b1 [Configuration] Fix startup error when group name is a number (#1398) 2024-12-09 18:12:03 +01:00
NEZNAMY a83f98580d [Bukkit] Update Paper submodule to 1.21.4 2024-12-07 13:58:47 +01:00
NEZNAMY 673542d8e4 [Code style] Move canSee method into TabPlayer 2024-12-07 12:23:51 +01:00
NEZNAMY 81c5d89a25 Fix broken PR 2024-12-07 12:08:26 +01:00
NEZNAMY bf0bcb0b96 Merge pull request #1394 from WheelerShigley/master
Fabric now has a getTPS() approximation.
2024-12-07 09:57:25 +01:00
WheelerShigley ac7a664303 Fabric now has a getTPS() approximation.
Added Fabric TPS approximation, based on mspt.
2024-12-06 19:58:38 -06:00
NEZNAMY 182dc67a49 5.0.4-SNAPSHOT 2024-12-03 20:27:24 +01:00
NEZNAMY 27ee82d17c 5.0.3 2024-12-03 20:11:13 +01:00
NEZNAMY 9405e4e45d [Bukkit] Add 1.21.4 support 2024-12-03 20:06:48 +01:00
NEZNAMY 8616769e32 [Bukkit] Print component converter initialization error if print flag is enabled 2024-12-03 19:12:37 +01:00
NEZNAMY 5d85c4697b [Bukkit] Fix error when a plugin shades adventure library without relocating it 2024-12-03 19:09:55 +01:00
NEZNAMY c54a2b7353 Mark 1.21.4 as the latest supported version 2024-12-03 18:24:17 +01:00
NEZNAMY 35075e88e3 [Fabric] Update to 1.21.4 2024-12-03 18:18:09 +01:00
NEZNAMY 1266d34176 [Wiki] Monthly wiki backup for December 2024 2024-12-01 20:05:59 +01:00
NEZNAMY 66ddf0e3ca [Fabric] Update to 1.21.4-rc3 2024-11-30 14:37:59 +01:00
NEZNAMY 88ab129737 [Fabric] Update to 1.21.4-rc2 2024-11-29 17:18:43 +01:00
NEZNAMY bcd293a255 [Fabric] Update to 1.21.4-rc1 2024-11-28 17:50:24 +01:00
NEZNAMY 4a5ebe6dbd [Fabric] Mark 1.21.4-pre3 as compatible 2024-11-26 18:23:13 +01:00
NEZNAMY ef1c3b1b3b [BungeeCord] Use new features added in build 1885 (no longer supports older builds) 2024-11-26 18:16:04 +01:00
NEZNAMY f7e68ffb6b [Fabric] Update to 1.21.4-pre3 2024-11-26 18:06:10 +01:00
NEZNAMY 2cf2153c4d 5.0.3-SNAPSHOT 2024-11-26 17:43:19 +01:00
NEZNAMY 7e7d0025fa 5.0.2 2024-11-26 17:27:16 +01:00
NEZNAMY 8252b6486e [Fabric] Update to 1.21.4-pre2 2024-11-25 20:03:28 +01:00
NEZNAMY 378041cb16 Merge pull request #1388 from Syrent/master
fix: avoid using TAB's internal vanish checks when a registered vanish integration is available
2024-11-24 19:57:41 +01:00
syrent ee5ff07565 chore: move pv try/catch inside vanish integration if statement 2024-11-24 20:48:02 +03:30
syrent 80c63dd414 chore: don't check for vanished meta if there's a registered vanish integration 2024-11-24 19:55:57 +03:30
NEZNAMY e182366566 [Scoreboard] Fix detection of scoreboards from other plugins not resetting on server switch 2024-11-23 21:18:31 +01:00
NEZNAMY acee89cce3 [BungeeCord] Add TODO 2024-11-23 20:59:45 +01:00
NEZNAMY 3ef3d14c10 [Vanish detection] Properly hook into vanish plugins on Velocity just like on BungeeCord 2024-11-23 20:58:15 +01:00
NEZNAMY 74365780a7 [Code style] Minor fixes from PR 2024-11-23 14:27:26 +01:00
NEZNAMY f6ad80370f Merge pull request #1387 from Syrent/master
feat: add VanishIntegration api
2024-11-23 11:13:12 +01:00
syrent 87eb544936 feat: add VanishIntegration api 2024-11-23 09:20:39 +03:30
NEZNAMY cde0389380 [Fabric] Update to 1.21.4-pre1 2024-11-20 17:46:25 +01:00
NEZNAMY 9e46ffc4e1 [Nametags] Process API calls in feature's thread instead of caller thread 2024-11-17 10:53:42 +01:00
NEZNAMY f3c35de780 [Configuration] Fix startup error if an element in a list is not a String 2024-11-14 20:20:11 +01:00
NEZNAMY 75f5378c8c [Belowname] Do not send updates of vanished players to those who cannot see them 2024-11-14 18:07:07 +01:00
NEZNAMY 19d32127e8 [Fabric] Update to 24w46a 2024-11-13 16:49:59 +01:00
NEZNAMY 2f00aa7750 [Conditions] Fix mistake from past commits not loading true/false values if not wrapped in "" 2024-11-10 18:24:12 +01:00
NEZNAMY 07f5dc68b5 [Velocity] Use new listOrder API (and bump required velocity build number to 450) 2024-11-10 10:08:35 +01:00
NEZNAMY 96a39d7037 [Configuration] Fix unknown key detection not working 2024-11-09 17:40:54 +01:00
NEZNAMY 409f1c4230 [Fabric] Update to 24w45a 2024-11-06 19:19:51 +01:00
NEZNAMY c3d2f9aeea [Commands] Reuse the same list of valid properties 2024-11-05 17:35:17 +01:00
NEZNAMY 380b3dd1f6 [Scoreboard] Add startup warn if trying to use NumberFormat on <1.20.3 server and runtime warn when trying to send it to <1.20.3 player 2024-11-03 16:52:10 +01:00
NEZNAMY c15a6009a2 [MySQL] Execute MySQL queries in a separate thread 2024-11-03 10:04:45 +01:00
NEZNAMY e4e7c364fd [Code style] Make list mutable 2024-11-03 09:46:14 +01:00
NEZNAMY 438361eed2 [Code style] Make fields final 2024-11-03 09:31:50 +01:00
NEZNAMY 684b35f2df [Code style] Refactor config options loading and add a few more misconfiguration checks 2024-11-03 09:13:41 +01:00
NEZNAMY 1d603ae76f [Conditions] Fix warning from previous commit 2024-11-02 16:18:46 +01:00
NEZNAMY a342b23890 [Conditions] Fix conditions without true/false defined not refreshing correctly 2024-11-02 15:49:37 +01:00
NEZNAMY c0dde2eaa7 [Config] Change default default-skin value in layout as the old one contains extra layer (which will be visible on 1.21.4) 2024-11-01 11:25:35 +01:00
NEZNAMY 6245fe9553 [Wiki] Monthly wiki backup for November 2024 2024-11-01 10:19:39 +01:00
NEZNAMY fb5be9e2aa [Paper] Update direct NMS to 1.21.3 2024-10-31 21:11:40 +01:00
NEZNAMY 918620a7dc [Bukkit & Fabric] Update to 24w44a (predicted on Bukkit) 2024-10-31 20:46:51 +01:00
NEZNAMY d2855449f0 [Layout] Add misconfiguration checks if slot is out of range 2024-10-30 20:56:59 +01:00
NEZNAMY f59326c0ae [Configuration] Add console warning if using invalid property for groups / users 2024-10-30 19:58:12 +01:00
NEZNAMY 033b9ed035 [Configuration] Improve warning messages 2024-10-29 20:37:27 +01:00
NEZNAMY 5c299767ec [Vanish detection] Disable Bukkit canSee support due to issues with vanish plugins 2024-10-29 20:16:30 +01:00
NEZNAMY 818701cd07 [RedisSupport] Register messages for features in constructors instead of load to make sure they run immediately and sync in the main thread 2024-10-29 20:08:58 +01:00
NEZNAMY 9b5e63aa25 [RedisSupport] Slightly improve debug message 2024-10-29 20:04:56 +01:00
NEZNAMY 1ad285bd79 [RedisSupport] Queue incoming messages to avoid tasks running before plugin is fully enabled, causing errors 2024-10-28 18:06:57 +01:00
NEZNAMY 4507bb179a Merge pull request #1374 from Tanguygab/master
Added Text PlaceholderAPI support on Fabric
2024-10-27 18:00:39 +01:00
Tanguygab 2dd79295a9 Fixed spacing 2024-10-27 17:56:25 +01:00
Tanguygab b21d840e60 [Fabric] Added FabricTabExpansion 2024-10-27 17:44:24 +01:00
NEZNAMY f3fa40bb0a [GitHub] Store plugin description in source code as backup (phase 4) 2024-10-27 17:11:38 +01:00
Tanguygab d8e14de0cf Fixed error with Text PAPI placeholder 2024-10-26 21:34:25 +02:00
Tanguygab f7b96eceeb Added support for Text PlaceholderAPI on Fabric 2024-10-26 21:26:22 +02:00
NEZNAMY 6f760939f3 [Placeholders] Add validation check to PlaceholderManagerImpl#getPlaceholder 2024-10-26 21:23:17 +02:00
NEZNAMY 5273ad0c5f [Gradle] Update to 8.10.2 2024-10-26 20:22:51 +02:00
NEZNAMY ac12abfb8b [GitHub] Store plugin description in source code as backup (phase 3) 2024-10-26 19:19:04 +02:00
NEZNAMY e5146e7f53 [Misconfiguration] Fix color codes being parsed in warning messages when they should not be. 2024-10-26 19:09:53 +02:00
NEZNAMY 62d1210872 [RGB] Fix using "<" symbol inside gradient breaking it 2024-10-26 18:59:49 +02:00
NEZNAMY 0d0177cd40 Bump bStats to 3.1.0 (this also somehow magically fixes false positives of anti-viruses) 2024-10-26 14:07:38 +02:00
NEZNAMY 227ecf9c9d [Placeholders] Fix error when registering custom server placeholder on proxy 2024-10-26 11:03:09 +02:00
NEZNAMY 562cd50762 [Belowname] Rename config options to hopefully be more clear 2024-10-26 10:29:21 +02:00
NEZNAMY ec56d530a6 [Scoreboard] Fix startup error when a line is configured to be null 2024-10-25 20:57:39 +02:00
NEZNAMY 6350714508 [GitHub] Store plugin description in source code as backup (phase 2) 2024-10-25 20:42:13 +02:00
NEZNAMY 1b251dad6a [GitHub] Store plugin description in source code as backup (phase 1) 2024-10-24 17:12:04 +02:00
NEZNAMY 7765b51a55 5.0.2-SNAPSHOT 2024-10-23 21:16:18 +02:00
NEZNAMY ff6ba6a8e9 5.0.1 2024-10-23 21:04:39 +02:00
NEZNAMY ce3485c823 [Layout] Fix player group condition not getting refreshed properly 2024-10-23 21:04:09 +02:00
NEZNAMY 90a99d3a79 [Fabric] Update to 1.21.3 2024-10-23 20:08:52 +02:00
NEZNAMY 7248dc4fef [Layout] Make check for 1.21.2+ listOrder support better (and work correctly) 2024-10-23 18:36:15 +02:00
NEZNAMY d704db55fa [Code style] Simplify 2024-10-23 16:26:43 +02:00
NEZNAMY 0f581e47b1 5.0.1-SNAPSHOT 2024-10-23 16:25:29 +02:00
NEZNAMY 48013f9b3c 5.0.0 2024-10-22 19:29:31 +02:00
NEZNAMY 258c8ce989 [Velocity] Use proper API method for creating TabListEntry 2024-10-22 19:29:13 +02:00
NEZNAMY c56a703028 Confirm 1.21.2 protocol version 2024-10-22 18:51:17 +02:00
NEZNAMY 4b9ce1b067 [Fabric] Update to 1.21.2 2024-10-22 18:50:56 +02:00
NEZNAMY 39b56725dd [Fabric] Update to 1.21.2-rc1 2024-10-18 21:44:54 +02:00
NEZNAMY b281b18a4e [Code style] Properly implement Platform#getServerVersion on proxies to avoid incorrect results 2024-10-17 17:30:04 +02:00
NEZNAMY b85487375d [Configuration] Rename version in config conversion 2024-10-16 18:27:03 +02:00
NEZNAMY 9ccdc0cf7e [Fabric] Update to 1.20.2-pre5 2024-10-16 17:58:40 +02:00
NEZNAMY 51b1bcf507 [Fabric] Update to 1.20.2-pre4 2024-10-15 18:59:00 +02:00
NEZNAMY 0afe598f3c [Bukkit] Small NMS cleanup 2024-10-13 11:10:38 +02:00
NEZNAMY 4227b0c702 [Nametags] Fix double team register due to concurrency 2024-10-12 19:19:30 +02:00
NEZNAMY 2a4e4a4e2e [Nametags] Fix team register/unregister duplications in combination of (un)vanishing and disable condition change 2024-10-12 19:18:03 +02:00
NEZNAMY a9fa8291de [Velocity] Avoid new object instantiation 2024-10-12 12:39:58 +02:00
NEZNAMY 007c0abc95 [Plugin messages] Protocol break - Identify packets by their ID instead of name 2024-10-12 11:20:18 +02:00
NEZNAMY f9752b7090 [Performance] Disable CPU tracking in netty because getting system time on every packet send was causing high CPU usage by itself 2024-10-12 10:11:32 +02:00
NEZNAMY b44b91f8cc [GitHub] Update branch name in actions 2024-10-11 21:04:51 +02:00
NEZNAMY 352721540a [Fabric] Update to 1.21.2-pre3 2024-10-11 20:25:31 +02:00
NEZNAMY 1ab5f49c3b [Velocity] Remove TAB-Bridge support for scoreboard packet encoding 2024-10-11 19:27:35 +02:00
NEZNAMY 976e6e397a [Bedrock] Update warning message 2024-10-11 17:15:04 +02:00
NEZNAMY e95139b79e [Placeholders] Fix nested placeholders in relational placeholders updating not triggering the main placeholder refresh 2024-10-09 17:16:25 +02:00
NEZNAMY a75f50dcf6 [Bedrock] No longer disable a feature and implement sidebar workaround as new Geyser build resolves these issues on their side. 2024-10-09 14:13:42 +02:00
NEZNAMY 87334c586c I just love Velocity 2024-10-06 18:20:19 +02:00
NEZNAMY eddec70a99 [Nametags] Fix attempts to send team packets when disabled for player 2024-10-06 16:44:47 +02:00
NEZNAMY d1d3279c8e [Fabric] Update to 24w40a 2024-10-03 18:50:58 +02:00
NEZNAMY 13ff1dfb18 [Velocity] Fix exploit (not specifying more to keep users safe) 2024-10-02 20:36:38 +02:00
NEZNAMY e0d09132dd [Wiki] Monthly wiki backup for October 2024 2024-10-01 20:03:45 +02:00
NEZNAMY 99478e7f33 [Build] Remove publishing script 2024-09-28 13:35:01 +02:00
NEZNAMY 02087c735a [Conditions] Automatically trim all spaces 2024-09-27 17:50:12 +02:00
NEZNAMY cd13d588d6 [Layout] Fix ROWS direction not working on 1.21.2 from previous commit 2024-09-26 21:21:04 +02:00
NEZNAMY 6efc23cc15 [Fabric] ACTUALLY update to 24w39a (while keeping older version support) 2024-09-26 21:10:58 +02:00
NEZNAMY 5c28711b26 [Layout] Take advantage of the new 1.21.2 listOrder tablist sorting field to remove entries from chat complete 2024-09-26 20:05:58 +02:00
NEZNAMY e9ae57aff5 [Fabric] Update to 24w39a 2024-09-25 18:16:12 +02:00
NEZNAMY 92e4b1749e [Velocity] Improve error messages 2024-09-24 18:29:46 +02:00
NEZNAMY 854eed74d7 [Code style] Clean up method 2024-09-22 13:45:17 +02:00
NEZNAMY 7c5ef3aa8a [Bukkit] Bump dependency 2024-09-21 15:11:12 +02:00
NEZNAMY 78670b5d90 [Code style] Simplify code 2024-09-20 21:12:03 +02:00
NEZNAMY f43d54b6f6 [Bukkit] Fix team packet creation issue in a past commit 2024-09-20 20:51:24 +02:00
NEZNAMY db0dd75e27 [Fabric] Update to 24w38a 2024-09-19 18:49:06 +02:00
NEZNAMY d6d1649975 [Performance] Speed up TAB#getPlayer function 2024-09-15 14:04:07 +02:00
NEZNAMY f0f0e40897 [Velocity] Cache team display name objects 2024-09-14 18:11:53 +02:00
NEZNAMY 43c1116ac7 [Code style] Call shorter method 2024-09-14 12:02:08 +02:00
NEZNAMY 225f30a257 [Performance] Add check to speed up function 2024-09-13 22:38:43 +02:00
NEZNAMY ae03436592 [Performance] Fix Bridge response placeholder processing calling RefreshableFeature#refresh for every placeholder that changed instead of just once. 2024-09-13 13:09:00 +02:00
NEZNAMY 8a6014fd02 [Configuration] Add global-playerlist.update-latency adding to config converter 2024-09-13 12:33:09 +02:00
NEZNAMY dcc0fafe66 [Layout] Store properties separately and per layout and drop global properties map. This also fixes a theoretical memory leak if mass-switching to new layouts using the API. 2024-09-13 09:49:14 +02:00
NEZNAMY bc33679a1f [Velocity] Cache TextHolder instances for better performance 2024-09-11 19:22:07 +02:00
NEZNAMY d69efe76e5 [Fabric] Update to 24w37a 2024-09-11 18:26:37 +02:00
NEZNAMY 11babe4293 [BelowName] Fix text refresher not running in belowname thread 2024-09-10 20:55:34 +02:00
NEZNAMY 2ba7492f24 [Performance] Skip redundant check if team exists 2024-09-10 17:40:24 +02:00
NEZNAMY 6c65ffb701 [Error logging] Increase log size limit to 16MB from 1MB 2024-09-09 19:49:30 +02:00
NEZNAMY 864e30b3db [Bukkit] Fix direct NMS code check and split it per function (supported versions will start varying since 1.21.2) 2024-09-09 18:42:48 +02:00
NEZNAMY b9124ef873 [Performance] Micro optimization on Bukkit in Team packet 2024-09-09 18:03:33 +02:00
NEZNAMY 77165a5ffb [Performance] Micro optimization on Bukkit 2024-09-08 20:26:28 +02:00
NEZNAMY 54a519de0b [Layout] Use randomly generated property names for fixes slots instead of building them deterministically 2024-09-07 21:23:08 +02:00
NEZNAMY 462d38373f [Layout] Add API method for getting layout from config by name 2024-09-07 18:19:41 +02:00
NEZNAMY 44234422d0 [Performance] Use saved team/objective pointers instead of getting them from maps 2024-09-06 17:23:48 +02:00
NEZNAMY c71fcf51c1 [Velocity] Change checks for Scoreboard API access for better performance 2024-09-06 16:57:08 +02:00
NEZNAMY 33cc4b2aad [Fabric] Update to 24w36a 2024-09-04 17:59:30 +02:00
NEZNAMY 6485356c74 [Scoreboard] Use separate maps for scoreboard-related properties for faster access (also fix a theoretical memory leak if sending too many custom scoreboards with API) 2024-09-01 16:11:36 +02:00
NEZNAMY d0476a93ba [Wiki] Monthly wiki backup for September 2024 2024-09-01 10:07:12 +02:00
NEZNAMY 2eb3548008 [Performance] [BossBar] Store properties in a separate structure instead of using shared properties map 2024-08-31 15:51:13 +02:00
NEZNAMY 028a58c2ae [BungeeCord] Update to builds 1860+ (no longer works on older versions) 2024-08-29 19:10:25 +02:00
NEZNAMY 7675fc11b6 [Fabric] Fix wrong server version check 2024-08-29 19:08:03 +02:00
NEZNAMY 7a10a9e8f2 [Fabric] Remove redundant SneakyThrows annotations 2024-08-29 17:16:49 +02:00
NEZNAMY 8ee869830b [Fabric] Mark all recent supported snapshots as compatible 2024-08-29 16:53:58 +02:00
NEZNAMY 63ecd7b2a2 [Fabric] Move method to different module to reduce reflection usage 2024-08-29 16:44:24 +02:00
NEZNAMY a125076033 [Fabric] Update to 24w35a 2024-08-29 15:15:43 +02:00
NEZNAMY 12153f5783 [Wiki] Create wiki folder containing a backup of all wiki pages 2024-08-28 19:02:32 +02:00
NEZNAMY fdcec16181 [BungeeCord] Avoid sending UPDATE_LIST_ORDER tablist action to <1.21.2 players (#1359) 2024-08-25 20:09:25 +02:00
NEZNAMY 0c4d653921 [Fabric + Bukkit] Update to 24w34a (not tested) 2024-08-25 10:31:25 +02:00
NEZNAMY f488f18a55 Merge #1357 into v5 (Get skin from profile properties by property name) 2024-08-20 16:07:33 +02:00
NEZNAMY 6164dd8f5f [Performance] Change recursion to while loop 2024-08-18 15:14:33 +02:00
NEZNAMY 270062cfe4 [Code style] Remove redundant tablist tracking code 2024-08-16 16:34:33 +02:00
NEZNAMY 014e197a41 [Code style] Remove some redundancies 2024-08-14 19:25:03 +02:00
NEZNAMY bed587d6ba [Fabric] Split PermissionsAPI hook into a separate class 2024-08-14 19:10:02 +02:00
NEZNAMY c15934fdea [Configuration] Rework config loading (part 2) 2024-08-13 19:45:28 +02:00
NEZNAMY 385516f373 [Configuration] Fix bossbar order not being preserved 2024-08-12 18:29:29 +02:00
NEZNAMY c50f5e7728 [Configuration] Fix bossbar progress config loading 2024-08-12 18:25:50 +02:00
NEZNAMY 494bd7368d [Configuration] Fix config loading running before conversion 2024-08-11 08:38:36 +02:00
NEZNAMY b6e8f4c1a7 [Code style] Store TabList, BossBar and Scoreboard instances in TabPlayer object instead of each implementation 2024-08-10 21:15:06 +02:00
NEZNAMY 392e97df2c [Configuration] Fix global playerlist config loading 2024-08-10 21:07:13 +02:00
NEZNAMY 45dbd8c4b2 [Bukkit] Add 1.21.1 to list of versions supported by paper hook 2024-08-10 19:08:38 +02:00
NEZNAMY a0ac7d3324 [Code style] Change fields in TabFeature into getters and fix some annotations 2024-08-10 18:03:27 +02:00
NEZNAMY 4dd7ce5b98 [Configuration] Rework config loading and add a lot more checks (part 1) 2024-08-10 17:11:01 +02:00
NEZNAMY 1a96101672 Update to 1.21.1 2024-08-10 09:57:50 +02:00
NEZNAMY 88f40ab457 [BossBar] Fix ConcurrentModificationException (#1350) 2024-08-08 16:21:53 +02:00
NEZNAMY 70403042d2 [Layout] Fix API usage causing placeholders to not refresh 2024-08-06 17:56:58 +02:00
NEZNAMY ff5a9fa3a9 [Bukkit] Add null check for non-null field because other plugins can make it null 2024-07-28 11:14:36 +02:00
NEZNAMY 5ead664cf9 Update README.md 2024-07-27 23:02:01 +02:00
NEZNAMY bba2b6f2e8 [Code style] Remove redundant check that will never pass 2024-07-27 17:40:31 +02:00
NEZNAMY 4b25571e1a [Code style] Clean up property loading 2024-07-27 16:47:18 +02:00
NEZNAMY 407d0eaeac [Velocity] Ask people to install VelocityScoreboardAPI 2024-07-27 14:53:50 +02:00
NEZNAMY 05bb3a5fa9 [Code style] Make skin downloading return Skin instead of List<String> 2024-07-27 12:16:36 +02:00
NEZNAMY 714a72c849 Merge pull request #1335 from qdpy/v5
Add a signed_texture skin parameter to avoid an API call
2024-07-27 12:00:34 +02:00
NEZNAMY 9ba8f149f7 [Code style] Remove unused method 2024-07-27 10:23:29 +02:00
NEZNAMY c9bb38c036 [Scoreboard] Add misconfiguration check if scoreboard has more than 15 lines defined 2024-07-26 19:01:19 +02:00
NEZNAMY 3241f1cf41 [Sorting] Fix sorting placeholder using old instance when a placeholder is overridden 2024-07-25 21:01:47 +02:00
NEZNAMY 8a7099a6ab [BungeeCord] Add try/catch for yet another NPE in BungeeCord code 2024-07-25 15:57:25 +02:00
NEZNAMY 65c6adcdb2 [Bukkit] Fix compatibility with random modded server 2024-07-22 19:27:42 +02:00
evlad f3f2e69bcf prevent AIOOB exeception 2024-07-16 06:19:29 +03:00
qdpy 335f0a3d6f Add skin signed texture 2024-07-16 03:52:26 +02:00
NEZNAMY a7e711850c [Performance] Use Map#put return type to get rid of Map#get 2024-07-14 12:28:20 +02:00
NEZNAMY 6fb2a7ce76 [Performance] Switch to ConcurrentHashMap instead of synchronizing a map 2024-07-14 11:59:17 +02:00
NEZNAMY 56a6e34847 [Fix] Wrong boolean value from previous commit 2024-07-11 17:23:48 +02:00
NEZNAMY cfa4c869ca [Code style] Clean up task executing and fix mistakes from previous recodes 2024-07-10 21:38:51 +02:00
NEZNAMY d548fbcb74 [Technical] Remove a layer of task wrapping 2024-07-08 19:20:56 +02:00
NEZNAMY d40fbced1a [Performance] [NameTags] Skip containsTeam call 2024-07-08 19:05:39 +02:00
NEZNAMY 21c251b40b [Performance] Avoid redundant StructuredComponent creation when converting RGB colors to legacy in scoreboard 2024-07-07 21:30:51 +02:00
NEZNAMY ec3371085b [Performance] Avoid double map access on team/objective register 2024-07-07 15:56:49 +02:00
NEZNAMY 3779f38c5d [Playerlist Objective] [Performance] Avoid performing unnecessary operations on disable condition change 2024-07-07 15:46:58 +02:00
NEZNAMY 5b62fe2be7 [BelowName] [Performance] Avoid performing unnecessary operations on disable condition change 2024-07-07 15:40:42 +02:00
NEZNAMY ea223bbcb4 [Performance] Avoid double access to placeholder map 2024-07-07 11:17:09 +02:00
NEZNAMY 7f4a56149a [Placeholders] Make one more method synchronized to prevent incorrect re-register that breaks the placeholder on concurrent access 2024-07-06 21:51:02 +02:00
NEZNAMY 52a620c479 [Placeholders] Make methods synchronized to fix concurrent access potentially breaking some placeholders 2024-07-06 21:38:05 +02:00
NEZNAMY 593aaf8039 [Conditions] Avoid re-registering the same placeholder multiple times 2024-07-06 21:11:04 +02:00
NEZNAMY cebb6ea626 [NameTags] Register visibility refresher subfeature in constructor to prevent issues in placeholder updating when registering the placeholder 2024-07-06 21:02:30 +02:00
NEZNAMY 337dc1c4d5 [Technical] Decode plugin messages in a new separate thread 2024-07-06 19:30:20 +02:00
NEZNAMY e110f942e8 [Scoreboard] Add misconfiguration check for invalid scoreboard properties and improve code for existing features 2024-07-06 16:49:24 +02:00
NEZNAMY 6bd5f9dab1 [Error logging] Print warns when receiving redis message of unknown player and add some debug messages 2024-07-03 21:22:45 +02:00
NEZNAMY 09149c559d [Code style] Add missing NotNull annotation 2024-06-30 21:28:42 +02:00
NEZNAMY f9a1130e4b [Performance] Make some placeholders on proxy trigger-based instead of periodic refreshing 2024-06-29 15:43:44 +02:00
NEZNAMY 91304233aa [Code style] Finish getting rid of global component cache 2024-06-29 14:07:52 +02:00
NEZNAMY 99286578c1 [Code style] Create separate component cache for layout fixed slots 2024-06-29 13:43:07 +02:00
NEZNAMY 81888d9aa5 [Code style] Avoid creating simple components in some cases 2024-06-28 21:50:23 +02:00
NEZNAMY f2e4b1b94a [Debug] Add message showing how long it took to unregister teams, objectives and bossbars on unload 2024-06-28 11:26:36 +02:00
NEZNAMY ab269ad740 [Code style] Dynamically remove player boss bars on unload instead of calculating them inside the feature 2024-06-28 10:44:36 +02:00
NEZNAMY 5a1df75526 [Fix] Forcefully shutdown thread executor after it failed to shut down softly 2024-06-27 21:28:12 +02:00
NEZNAMY b74ed1d14e [Code style] Unregister all teams and objectives dynamically instead of in every feature separately and calculating all logic. Can't believe it took me years to come up with this. 2024-06-27 21:24:33 +02:00
NEZNAMY d087db38e3 [Bug fix] Update disable condition status in feature's thread 2024-06-27 21:08:21 +02:00
NEZNAMY ff8c406823 [Velocity] Avoid errors in VelocityScoreboardAPI hook by handling unexpected situations 2024-06-27 15:33:13 +02:00
NEZNAMY 7e12a75bea [Technical] Include thread name in error message if it failed to shut down 2024-06-27 15:27:40 +02:00
NEZNAMY aa7fb09399 [Teams] Fix vanish status change processing not respecting disable condition 2024-06-27 14:40:07 +02:00
NEZNAMY a805b3fe90 [Bukkit] Mark Paper 1.21 as compatible with direct NMS code 2024-06-27 11:06:13 +02:00
NEZNAMY 63d9122440 [Bossbar] Fix bars being resent on server switch even for <1.20.2 players 2024-06-27 10:54:38 +02:00
NEZNAMY f9293c9c92 [Bug fix] Unregister teams of redis players on unload 2024-06-26 16:11:44 +02:00
NEZNAMY 4892fd5817 [Error fix] Fix async features sometimes still using global onlinePlayers array 2024-06-26 14:37:25 +02:00
NEZNAMY cf4a8ae98d [Error fix] Add check for null team name for redis players (temporarily?) 2024-06-26 14:32:24 +02:00
NEZNAMY f0edfeeebf [Error fix] Add try/catch for RedisBungee message sending 2024-06-26 14:28:50 +02:00
NEZNAMY 4d54ad35ff [Bug fix] Colors not displaying correctly in Fabric and Velocity console 2024-06-26 14:13:46 +02:00
NEZNAMY b6b512f788 Merge pull request #1324 from Miyli/v5
[GitHub] Add build number to artifact
2024-06-25 18:02:00 +02:00
Miyli a6076eb6ac [GitHub] Add build number to artifact 2024-06-25 17:40:20 +02:00
NEZNAMY 01b74f0a6e [Bug fix] Unload features in main thread and wait for tasks to shut down to prevent unloading and loading at the same time on reloads 2024-06-25 15:37:40 +02:00
NEZNAMY 6c6752c348 [Velocity] Introducing: Velocity Scoreboard API 2024-06-25 15:10:40 +02:00
NEZNAMY 73873ac193 [Performance] Remove synchronized modifier from method 2024-06-25 14:20:23 +02:00
NEZNAMY 6542dad7bd [Bug fix] Fix synchronized deadlock in placeholder refreshing 2024-06-24 14:16:16 +02:00
NEZNAMY 9bf4a2b555 [Code style] Add and use Scoreboard#renameTeam method 2024-06-21 13:53:06 +02:00
NEZNAMY 66d18a4469 [Code style] Expose Scoreboard#containsTeam method in interface 2024-06-21 13:40:23 +02:00
NEZNAMY 7a489067e6 [NameTags] Fix unregistering and registering team on rename even for players who should not see the team because player is vanished 2024-06-20 22:27:37 +02:00
NEZNAMY 600d6de132 [Code style] Reduce access level of methods 2024-06-20 19:11:02 +02:00
NEZNAMY 48e35e7abd [Performance] Split Header and Footer caches as the values are practically never the same 2024-06-20 14:30:09 +02:00
NEZNAMY 8613e9d734 [Error fix] Add try/catch for PremiumVanish hook 2024-06-20 14:25:50 +02:00
NEZNAMY 9ed277d80d [Error fix] Make methods synchronized to avoid ConcurrentModificationException 2024-06-20 13:44:16 +02:00
NEZNAMY 26345d00a3 [Code style] Clean up method calls 2024-06-20 13:00:57 +02:00
NEZNAMY be70eea0f8 [Performance] [Global playerlist] Add option to disable latency refreshing and disable it by default 2024-06-20 12:14:06 +02:00
NEZNAMY 01357d9ce4 [Sorting] Fix NPE 2024-06-19 17:40:06 +02:00
NEZNAMY 05afafbc45 [Performance] [Global playerlist] Create object for server groups to compare identity instead of having to do heavy checks (also save spy-server status for fast access) 2024-06-18 19:25:55 +02:00
NEZNAMY 68f7272128 [Bug fix] Make plugin message encoding thread shared and don't shut it down, which caused scoreboard packets to not get sent fully on reload, resulting in "Network Protocol Error" on 1.20.5+ 2024-06-18 17:28:40 +02:00
NEZNAMY caa0ab723a [Code style] Reduce calls to "refresh" method in features 2024-06-18 16:40:47 +02:00
NEZNAMY ee0d6002c8 [Code style] Forward group change into features with a new method specifically for that purpose instead of refresh method 2024-06-18 16:30:03 +02:00
NEZNAMY 48f0d04305 [Performance] Only send tablist update packet once for connected player 2024-06-18 15:24:12 +02:00
NEZNAMY 6516b80bf0 Fix error from previous commit 2024-06-18 14:30:45 +02:00
NEZNAMY b0403ff94b [GitHub] Update build.yml dependencies 2024-06-18 14:23:07 +02:00
NEZNAMY 77f88edf08 [Performance] Submit feature refresh task only once instead of once for every player 2024-06-18 14:10:07 +02:00
NEZNAMY be70664c84 [Performance] Use method reference to avoid new lambda instantiation each time 2024-06-18 12:55:27 +02:00
NEZNAMY 37ca992233 [Performance] Store component flat text instead of calculating it each time 2024-06-18 12:42:57 +02:00
NEZNAMY 620904de1c [Performance] Store last placeholder values inside player with placeholder key instead of placeholder with player key 2024-06-18 12:19:33 +02:00
NEZNAMY 9c0fac5c44 [Performance] [BossBar] Store visible boss bars of a player for fast access 2024-06-18 11:48:36 +02:00
NEZNAMY 8846c5a9fe [Performance] [NameTags] Only initialize set if used 2024-06-18 11:14:18 +02:00
NEZNAMY 880d2a8570 [PremiumVanish hook] Make methods synchronized to fix ConcurrentModificationException 2024-06-18 10:18:02 +02:00
NEZNAMY 45b6591518 [API Break] Require placeholders to return String instead of Object 2024-06-17 20:48:09 +02:00
NEZNAMY c6ce99d527 [Code style] Remove redundant SneakyThrows 2024-06-17 20:45:13 +02:00
NEZNAMY 7a7861fd9c [Performance] [Bukkit] Only set field if value actually changed 2024-06-17 20:10:00 +02:00
NEZNAMY 3c11630a6c [Code style] Simplify 2024-06-17 15:50:51 +02:00
NEZNAMY 3b9f4c9884 [RedisBungee] Recode handling of sub-features and small fixes (#1317?) 2024-06-17 15:00:08 +02:00
NEZNAMY 0757fc4352 [RedisBungee] Fix error (#1317) 2024-06-17 10:54:27 +02:00
NEZNAMY c1af721ad2 [NameTags] Properly update online players 2024-06-16 21:17:29 +02:00
NEZNAMY 0ea04394f9 [RedisBungee] Override %serveronline% to count redis players as well (#1316) 2024-06-16 20:09:34 +02:00
NEZNAMY 576c178b4f [NameTags] Move the feature handling to a new custom thread 2024-06-16 19:05:58 +02:00
NEZNAMY 674708cea6 [Playerlist objective] Fix error 2024-06-16 16:54:45 +02:00
NEZNAMY 179b05f992 [Belowname] Fix error 2024-06-16 16:53:26 +02:00
NEZNAMY e2c768bb56 [Performance] Speed up all placeholders returning int by caching string values 2024-06-16 15:10:57 +02:00
NEZNAMY 79e492b230 [Performance] Speed up %gamemode% placeholder 2024-06-16 14:40:07 +02:00
NEZNAMY 708bc5c302 [Global playerlist] Fix error in placeholder before it's loaded 2024-06-16 13:44:19 +02:00
NEZNAMY 9543cd5b5f [Performance] Access player's world and server variables directly instead of via getter 2024-06-16 12:49:56 +02:00
NEZNAMY 0bc8313897 [Technical] Move global playerlist feature handling to a new custom thread 2024-06-16 12:22:30 +02:00
NEZNAMY 91986926cb [Performance] Avoid redundant text color attempt 2024-06-15 17:34:07 +02:00
NEZNAMY ac638add7a [Performance] [Paper] Only rewrite packet if something changed 2024-06-15 15:58:30 +02:00
NEZNAMY 9551783d49 [Performance] [NameTags] Avoid calculating other team properties when changing visibility rule 2024-06-15 15:00:22 +02:00
NEZNAMY 85cf2b3e91 [Performance] [NameTags] Avoid calculating other team properties when changing collision rule 2024-06-15 14:25:05 +02:00
NEZNAMY f98ccb9e44 [Performance] [NameTags] Avoid calculating nametag visibility when only updating prefix/suffix 2024-06-15 13:54:56 +02:00
NEZNAMY 066d50e492 [Performance] Save LuckPerms User object instead of getting it on every request 2024-06-15 12:58:21 +02:00
NEZNAMY 6c823e86fe [Performance] [Scoreboard] Check player's active scoreboard instead of set of players who can see the scoreboard 2024-06-15 12:16:13 +02:00
NEZNAMY 6d3ea577d5 [Error fix] Make method synchronized to avoid ConcurrentModificationException 2024-06-15 11:29:21 +02:00
NEZNAMY 2826e39ea2 [Code style] Make paper a submodule of bukkit 2024-06-15 09:58:48 +02:00
NEZNAMY d855f04612 [Performance] [Fabric] Save enum values in an array for fast access 2024-06-14 23:27:25 +02:00
NEZNAMY 99eb93e66f [Performance] [Bukkit] Add native NMS implementations on 1.20.5+ Paper (where mojang mappings are used) 2024-06-14 23:24:52 +02:00
NEZNAMY e91110d049 [Performance] Avoid double map access 2024-06-14 19:45:44 +02:00
NEZNAMY 8e1bb8519c [Performance] [Bukkit] Save PlayerConnection for sending packets instead of getting it using reflection each time 2024-06-14 19:12:09 +02:00
NEZNAMY 61df01ed1c [Performance] Massively boost last color of prefix getting, also remove cache and save it inside the component 2024-06-14 18:38:15 +02:00
NEZNAMY c219aa83b1 [Code style] Remove unused imports 2024-06-14 15:26:03 +02:00
NEZNAMY 71109b397d [Code style] Remove redundant initialization 2024-06-14 15:23:42 +02:00
NEZNAMY 4fe4c2981d [Code style] Add NotNull annotation 2024-06-14 15:15:16 +02:00
NEZNAMY 2f9f2f722c [Code style] Add NotNull annotation 2024-06-14 15:06:33 +02:00
NEZNAMY 40009d018c [Code style] Clean up code by passing Objective parameter instead of its values 2024-06-14 15:03:29 +02:00
NEZNAMY 251480fd4a [MiniMessage] Add dummy character to parsed text to disable color compacting (#1312) 2024-06-14 14:23:42 +02:00
NEZNAMY 6295145d5b [Scoreboard] Disable check to fix error on reload 2024-06-14 12:09:26 +02:00
NEZNAMY 17f2e67334 [Code style] Construct platform's team immediately to avoid nullability warn 2024-06-14 11:50:39 +02:00
NEZNAMY fe4130a846 [Bukkit] Fix text cutting not considering client version (#1313) 2024-06-14 10:52:57 +02:00
NEZNAMY 141201b7aa [Sponge 7] Add missing text cutting to 32 characters 2024-06-13 21:39:50 +02:00
NEZNAMY 3b3c511572 Change build.yml to use v5 branch 2024-06-13 21:15:10 +02:00
NEZNAMY 8c6cf8ee22 Add 1.21 support 2024-06-13 21:14:05 +02:00
NEZNAMY 231fd36ce9 [Performance] Save FixedFormat across all platforms and store it inside TabComponent class to reduce need for a cache 2024-06-12 20:50:13 +02:00
NEZNAMY fb9dd0a899 [Code style] Extend class to avoid code duplication 2024-06-12 20:02:50 +02:00
NEZNAMY d98bcdb4e8 [Fix] Concurrent access to online players causing features to try to work with players before loading them 2024-06-12 19:15:33 +02:00
NEZNAMY f94a87e6d5 [Performance] Use more efficient caching of last team prefix color 2024-06-12 17:39:30 +02:00
NEZNAMY 1501d5e71e [Performance] Store serialized json of a component inside of it instead of using a cache 2024-06-12 17:24:16 +02:00
NEZNAMY a7d955266d [Code style] Make cache efficiency calculation more accurate 2024-06-12 17:08:01 +02:00
NEZNAMY 9633a6c176 [Code style] Make cache efficiency calculation more accurate 2024-06-12 17:04:33 +02:00
NEZNAMY 9d50090dbd [Performance] Create separate component caches for scoreboard and nametags 2024-06-12 17:02:50 +02:00
NEZNAMY 8b96a8e654 [Performance] Add missing caching for Redis sub-features 2024-06-12 14:15:54 +02:00
NEZNAMY 453288bf77 [Performance] Convert text to TabComponent in BossBar immediately and cache it 2024-06-12 14:08:59 +02:00
NEZNAMY 26fb0ec2e9 [Performance] Split component caches for some features as they usually never share the same inputs across features, reducing efficiency 2024-06-12 13:53:03 +02:00
NEZNAMY 2671052cad [Code style] Remove redundant SneakyThrows 2024-06-11 22:08:20 +02:00
NEZNAMY 7f1bcd69c8 [Code style] Save platform's scoreboard teams and objectives inside TAB's classes for fast access 2024-06-11 21:23:18 +02:00
NEZNAMY 5fe83af514 [Fix] Make scoreboard methods synchronized to prevent values not getting saved when accessing from multiple threads (despite being concurrent) 2024-06-11 21:12:54 +02:00
NEZNAMY 89dc2f09fc [Fix] Make player properties ConcurrentHashMap to fix issues with parallel access 2024-06-11 20:53:15 +02:00
NEZNAMY 7ce564409a [Fix] Disable instance activity check as internals were accidentally triggering it, especially with parallel processing 2024-06-11 18:32:05 +02:00
NEZNAMY 2e34069d14 [Fix] Add "synchronized" to method to fix concurrent access error 2024-06-11 18:27:21 +02:00
NEZNAMY e435501501 [Sorting] Fix sorting by pre-defined values space trimming inconsistency 2024-06-11 15:38:38 +02:00
NEZNAMY 51402637c6 [Fabric] Update to 1.21-rc1 2024-06-10 16:29:25 +02:00
NEZNAMY 44593f7a75 [Code style] Create custom thread executor class for easier usage 2024-06-10 14:10:27 +02:00
NEZNAMY 48596efa77 [Technical] Process Belowname and Playerlist Objective feature in a separate custom thread 2024-06-10 13:10:11 +02:00
NEZNAMY 3e323df70d [Fix] Process Header/Footer disable condition change in its own thread 2024-06-10 11:01:50 +02:00
NEZNAMY 0d89cef519 [Technical] Process SpectatorFix feature in a separate custom thread 2024-06-09 13:00:03 +02:00
NEZNAMY f758fdf8f3 [Code style] Create runnables instead of using lambdas for cleaner stack traces 2024-06-09 12:43:15 +02:00
NEZNAMY 44b9f06087 [Technical] Process Scoreboard feature in a separate custom thread 2024-06-09 12:12:22 +02:00
NEZNAMY d1155d5e7a [Fabric] Update to 1.21-pre4 2024-06-07 18:39:15 +02:00
NEZNAMY 51ccd029ca [Layout] Fix feature attempting to format tablist names of players who have them disabled with condition 2024-06-07 15:26:48 +02:00
NEZNAMY 1df83dac91 [Fix] RejectedExecutionException in feature threads on reload 2024-06-07 14:49:21 +02:00
NEZNAMY a3c1d643fd [Fix] Refresh call not being called in feature's thread 2024-06-07 14:05:21 +02:00
NEZNAMY 4350e311e7 [Fix] Throwables not being logged when thrown thrown in new threads 2024-06-07 13:48:39 +02:00
NEZNAMY 89ec9355bd [Technical] Process PingSpoof feature in a separate custom thread 2024-06-07 12:15:58 +02:00
NEZNAMY f2be7220f3 [Layout] Send fake ping if ping spoof is enabled immediately instead of having to rewrite it 2024-06-07 12:05:08 +02:00
NEZNAMY 0cbac252a5 [Technical] Process BossBar feature in a separate custom thread 2024-06-07 11:43:16 +02:00
NEZNAMY 3ea60971be [Fix] ConcurrentModificationException on high player counts 2024-06-06 22:09:48 +02:00
NEZNAMY 8ea76e2016 [Technical] Create Runnables instead of using lambdas for cleaner stack traces 2024-06-06 22:01:02 +02:00
NEZNAMY 86d2d732eb [BungeeCord] Fix NPE in packet reader 2024-06-06 18:15:08 +02:00
NEZNAMY 55ce84a5ae [Technical] Process Header/Footer in a separate custom thread (and prepare other features) 2024-06-06 18:07:40 +02:00
NEZNAMY 0f0e4de682 [Code style] Replace forEach with for loop for shorter stack traces 2024-06-06 14:54:20 +02:00
NEZNAMY ad60372cf2 [Error handling] Properly print errors if thrown in recently added threads 2024-06-06 14:29:42 +02:00
NEZNAMY 3cf5fd7cbd [Technical] Run plugin message encode and send in a new custom thread 2024-06-06 13:48:18 +02:00
NEZNAMY ce090566cc [Technical] Run tablist entry anti-override task in a new custom thread 2024-06-05 22:19:58 +02:00
NEZNAMY 866f645eed [Code style] Replace forEach with for loop for shorter stack traces 2024-06-05 22:17:35 +02:00
NEZNAMY 64f3bebd3d [Fabric] Update to 1.21-pre3 2024-06-05 15:50:49 +02:00
NEZNAMY 239cc461d5 [Code style] Use shorter alternative 2024-06-05 11:28:57 +02:00
NEZNAMY b832784977 [Code style] Refactor pipeline injection on BungeeCord 2024-06-05 11:09:59 +02:00
NEZNAMY 0f37b0be54 [RedisBungee] Add config option for disabling RedisBungee hook 2024-06-03 17:10:11 +02:00
NEZNAMY bb428af3af [Configuration] Add another misconfiguration check 2024-06-03 11:37:03 +02:00
NEZNAMY 1c4d9704f2 [Commands] Fix error when setting property value to ' or " 2024-06-01 16:48:24 +02:00
NEZNAMY 53a5eae721 [Fabric] Update to 1.21-pre2 2024-05-31 20:45:42 +02:00
NEZNAMY 049ab257e2 [Placeholders] Fix server placeholders not returning proper value on request sometimes 2024-05-30 16:28:38 +02:00
NEZNAMY 0ece5319ea [Fabric] Update to 1.21-pre1 2024-05-29 20:32:10 +02:00
NEZNAMY b847cc1178 [Teams] [Bukkit] Fix team action not being taken correctly on 1.8 - 1.12, causing issues with previous commit 2024-05-29 13:36:22 +02:00
NEZNAMY 422da3e1ba [Commands] Fix error in debug command 2024-05-28 14:52:40 +02:00
NEZNAMY 868c6baebf [Teams] Fix another instance of "Network Protocol Error" disconnect on 1.20.5+ 2024-05-28 14:27:47 +02:00
NEZNAMY 9a3cf167ed [Performance] Change way placeholder values are stored in plugin's PlaceholderAPI expansion for better performance 2024-05-27 22:46:10 +02:00
NEZNAMY cdf47ebab0 [Visuals] Refresh permission groups in another thread (also split its CPU usage tracking to 2 parts) 2024-05-27 22:06:00 +02:00
NEZNAMY 929d1f945b [Code style] Replace try/catch with SneakyThrows 2024-05-27 15:46:01 +02:00
NEZNAMY 2671242d2c [Code style] Remove duplicate class mapping 2024-05-27 14:25:34 +02:00
NEZNAMY 78cff8dea8 [Bukkit] Add support for latest 1.21 snapshot 2024-05-27 13:43:32 +02:00
NEZNAMY 7ac3840831 [Javadoc] Fix typo 2024-05-27 13:42:51 +02:00
NEZNAMY 556ee6df01 [Sorting] Make number padding correct (may rarely only insert 1 character) 2024-05-26 12:48:28 +02:00
NEZNAMY 2eca28e497 [Sorting] Fix number compression bug (#1297) 2024-05-26 12:37:35 +02:00
NEZNAMY 76ff5d49e2 [Code style] Simplify array creation 2024-05-25 21:50:43 +02:00
NEZNAMY 2a919aa6b7 [Performance] Fix Header/Footer components not being cached on some platforms from recent commits 2024-05-25 14:21:12 +02:00
NEZNAMY c83f806e4c [Code style] Store expected TabList display names by UUID instead of TabPlayer 2024-05-25 14:07:50 +02:00
NEZNAMY afbfd38dfd [Bukkit] Add back fallback solutions for Scoreboards using Paper API and Bukkit API 2024-05-23 13:51:11 +02:00
NEZNAMY 6874916ce0 [Javadoc] Update class description 2024-05-23 13:10:39 +02:00
NEZNAMY 143c4fc7e4 [Code style] Remove expected header/footer tracking, since it's not used anyway (for now) 2024-05-23 13:04:49 +02:00
NEZNAMY f85e945a32 [Bukkit] Fix compatibility wih Mojang mapped servers jars 2024-05-22 21:21:24 +02:00
NEZNAMY d0110c9a4f [Fabric] Update to 24w21b 2024-05-22 20:54:15 +02:00
NEZNAMY 4307768bee [Code style] Split TabList class into an interface and abstract class 2024-05-22 20:24:59 +02:00
NEZNAMY 88773e3232 [BossBar] Fix error when trying to display bossbar that was not visible on join 2024-05-22 10:14:06 +02:00
NEZNAMY 8f32772f83 Add TODO 2024-05-21 18:09:02 +02:00
NEZNAMY 17b3c28d91 [Performance] Only track placeholder usage if cpu tracking is enabled 2024-05-21 17:58:34 +02:00
NEZNAMY 45945d9a13 [Sponge 8] Add 1.20.6 support 2024-05-21 16:57:45 +02:00
NEZNAMY ba9741a084 [Code style] Split Scoreboard class into and interface and abstract class with tracking, also resend content on server switch using tracked values instead of from each feature individually 2024-05-21 16:04:35 +02:00
NEZNAMY 3bcad371d2 [Code style] Create a class for BossBar managing all safety checks and extend it in all implementations and resend them on server switch internally instead of using bossbar feature itself 2024-05-20 22:06:49 +02:00
NEZNAMY 1e9bf68671 [BossBar] Fix Network Protocol Error disconnect on server switch 2024-05-20 12:37:31 +02:00
NEZNAMY f57df6a536 [BossBar] Make %countdown% return 0 after announcement is over instead of going to negative numbers 2024-05-20 11:18:44 +02:00
NEZNAMY 793536d2d3 [Fix] Fix typo 2024-05-18 18:18:00 +02:00
NEZNAMY 9a348da34f [Code style] Simplify 2024-05-18 17:45:03 +02:00
NEZNAMY 4819bf63b6 [Bukkit] Add back fallback solutions for Header/Footer using Paper API and Bukkit API if NMS fails to load 2024-05-18 17:32:43 +02:00
NEZNAMY 5777d49861 [Performance] [Bukkit] Use Adventure API for BossBars if available for massively improved performance 2024-05-18 12:28:46 +02:00
NEZNAMY 7540c61fde [Code style] Create fields for adventure components from TAB component to allow multiple options in the future 2024-05-18 11:57:59 +02:00
NEZNAMY 308da4cbac [Code style] Create field for feature name and define it in constructor instead of creating abstract functions 2024-05-17 22:17:11 +02:00
NEZNAMY 615af41de8 [Performance] Return placeholder values as strings to prevent boxing objects being created 2024-05-17 20:24:46 +02:00
NEZNAMY 7c473a4e3f [Performance] Change functional interface usage to avoid int -> Integer boxing 2024-05-17 18:58:03 +02:00
NEZNAMY 79b4f9821b [Performance] Add cooldown to player vanish check 2024-05-17 17:23:16 +02:00
NEZNAMY 80825ee701 [Code style] Mark fields of class as NonNull 2024-05-17 16:51:09 +02:00
NEZNAMY aa0e60b091 [Fix] Fix startup error caused by one of the previous commits 2024-05-17 16:50:49 +02:00
NEZNAMY 718cfb682d [Code style] Improve readability of some sorting functions (also probably fix sorting API not working in layout) 2024-05-17 12:49:02 +02:00
NEZNAMY 66f0426e6a [Code style] Remove unused method 2024-05-17 12:25:55 +02:00
NEZNAMY 145671ecdd [Code style] Remove unused methods 2024-05-17 12:22:08 +02:00
NEZNAMY 43d71f7e49 [Code style] Remove unused import 2024-05-17 12:20:26 +02:00
NEZNAMY 150de771d5 [Code style] Initialize map values in construction instead of creating anonymous class 2024-05-17 12:20:16 +02:00
NEZNAMY 50da728abf [Code style] Remove redundant checks 2024-05-17 11:05:28 +02:00
NEZNAMY 656ce6ecad [Performance] Create player data classes for Tablist formatting and teams to avoid Map lookup each time 2024-05-17 11:01:54 +02:00
NEZNAMY 8a758dffec [Performance] Create player data class for Belowname to avoid Map lookup each time 2024-05-16 21:14:07 +02:00
NEZNAMY 52bd538ed0 [Performance] Create player data class for Playerlist objective to avoid Map lookup each time 2024-05-16 20:58:49 +02:00
NEZNAMY 115c5ac890 [Bug fix] Fix disabled tablist formatting referencing wrong variable 2024-05-16 20:43:46 +02:00
NEZNAMY 0f7d15a4d0 [Performance] Create player data class for Header/Footer to avoid Map lookup each time 2024-05-16 20:39:04 +02:00
NEZNAMY 5b6e07d5d3 [Fabric] Update to 24w20a 2024-05-15 17:22:15 +02:00
NEZNAMY 8579ed322d [Code style] Remove redundant class wrapper for a Map 2024-05-15 15:19:53 +02:00
NEZNAMY 44464b605d [Fabric] Replace Minecraft version check with fabric networking api check (which is what is preventing the mod from working there) 2024-05-14 11:30:08 +02:00
NEZNAMY e8124dd280 [Fabric] Replace Fabric API call with direct NMS call 2024-05-13 22:25:19 +02:00
NEZNAMY 0105fc2087 [Fabric] Remove unused field 2024-05-12 14:10:28 +02:00
NEZNAMY 39e7b26128 [Fabric] Remove entity packets from access widener 2024-05-12 14:05:58 +02:00
NEZNAMY ddbc13fc27 [Scoreboard] Remove respect-other-plugins config option, it is now forced 2024-05-12 13:57:15 +02:00
NEZNAMY 80660fa6cf [Performance] Small optimization of scoreboard packet processing 2024-05-12 13:16:52 +02:00
NEZNAMY b4a94e8da5 [Code style] Remove unused method 2024-05-12 12:34:32 +02:00
NEZNAMY 051c9a8c85 [BossBar] Add console warn if used color or style is invalid 2024-05-12 12:27:28 +02:00
NEZNAMY 840b1ea4a2 [Code style] Remove unused field 2024-05-12 11:32:14 +02:00
NEZNAMY ea57695a31 [Fix] Remove forgotten references to deleted unlimited nametag feature 2024-05-12 11:28:49 +02:00
NEZNAMY 90778bc84c Merge branch 'master' into v5 2024-05-11 21:31:45 +02:00
NEZNAMY 949216325f [Nametags] Only make nametags of invisible players invisible for 1.8 players to compensate for the bug instead of everyone (#1287) 2024-05-11 21:29:55 +02:00
NEZNAMY cdb521de1f [BungeeCord] Initialize different BossBar instance for 1.8 players to avoid version check every time 2024-05-11 17:01:49 +02:00
NEZNAMY e50b39c559 [Javadoc] Document more methods 2024-05-11 14:36:01 +02:00
NEZNAMY 80afb497e0 [Code style] Remove unused field 2024-05-11 13:34:53 +02:00
NEZNAMY e83a12aaee [Fabric] Mark 1.17 as supported again 2024-05-11 13:24:12 +02:00
NEZNAMY 461860bb17 Delete unlimited nametag and wither bossbar features 2024-05-11 13:22:00 +02:00
NEZNAMY cff4e3e6b0 5.0.0-SNAPSHOT 2024-05-11 12:20:46 +02:00
NEZNAMY dddf0b35a2 [Code style] Add missing access level to field 2024-05-11 11:43:51 +02:00
NEZNAMY 35ec2bf215 [Code style] Make fields final 2024-05-11 11:43:05 +02:00
NEZNAMY 607b60b323 [Code style] Remove unused method 2024-05-11 11:41:35 +02:00
NEZNAMY bb6ad72962 [Code style] Improve presence check of Player#getPing method 2024-05-10 17:05:47 +02:00
NEZNAMY 4fb8420d69 [Code style] Remove unused method 2024-05-10 16:59:11 +02:00
NEZNAMY 74af19a9d9 [Code style] Move field to a local variable 2024-05-10 16:53:51 +02:00
NEZNAMY 737ac70e6e [Fabric] Update to 24w19a 2024-05-10 16:28:39 +02:00
NEZNAMY bfc72b735c [Fabric] Add another submodule for 1.18.2 to avoid hardcoding method and class names 2024-05-10 16:05:09 +02:00
NEZNAMY 323412f935 [Code style] Decrease field visibility 2024-05-10 14:56:00 +02:00
NEZNAMY db34ba4995 [Fabric] Improve check for Fabric API features availability 2024-05-10 14:54:01 +02:00
NEZNAMY 15d7eb6d91 [Javadoc] Document more methods 2024-05-10 14:50:02 +02:00
NEZNAMY e2b95df2e4 [Bukkit] Explicitly specify constructor to fix compatibility with forge mods that widen a constructor access, causing TAB to choose the wrong one on 1.19.3+ (#1276) 2024-05-10 13:42:50 +02:00
NEZNAMY 1117094412 [Bug fix] Update library to fix error 2024-05-09 14:54:46 +02:00
NEZNAMY 4bab076642 [Bug fix] Fix group change from Bridge not being processed correctly (#1281) 2024-05-09 13:38:36 +02:00
NEZNAMY d602818f67 [Bug fix] Disable tablist containsEntry usage due to a race condition that sometimes makes it not work properly until a better solution is found (#1284) 2024-05-08 09:37:35 +02:00
NEZNAMY 1a76321062 [Fabric] Update to 24w18a 2024-05-04 20:37:08 +02:00
NEZNAMY 4b056529dd [Fix] Add cpu usage of placeholders inside plugin's main thread instead of placeholder thread to avoid incorrect numbers when it's overloaded 2024-05-04 20:29:21 +02:00
NEZNAMY bdbf3ba5b1 [Velocity] Remove message printing if attempting to send plugin message to a player who is not connected to any server (#1248) 2024-05-03 22:35:29 +02:00
NEZNAMY aab9a650b0 Fix startup error when using a plugin that shades MiniMessage without shading component serializers (#1280) 2024-05-03 14:47:25 +02:00
NEZNAMY f8487f07a6 Create array of component decorations to take values from to avoid instantiation each time 2024-05-02 21:06:15 +02:00
NEZNAMY e38721fa69 Speed up function 2024-05-02 19:30:22 +02:00
NEZNAMY 18e52f2f10 Add 1.20.6 to known versions 2024-04-29 18:06:16 +02:00
NEZNAMY f1300410b8 Update Fabric to 1.20.6 2024-04-29 18:03:52 +02:00
NEZNAMY e17059f1f7 Update library to fix error (1276) 2024-04-29 12:26:29 +02:00
NEZNAMY f3e9f7cf06 [BungeeCord] Remove console warn regarding ViaVersion on BungeeCord as it no longer breaks anything 2024-04-28 18:08:50 +02:00
NEZNAMY 63ed24d136 [Bukkit] Use Spigot internals over Paper API as a primary TPS getter (uses 10x less CPU and 100x less memory allocations) 2024-04-28 14:01:33 +02:00
NEZNAMY 66008cd767 [Velocity] Fix "Received packet for unknown scoreboard objective: TAB-BelowName" on server switch 2024-04-27 19:03:46 +02:00
NEZNAMY 1852c1b5c4 Create array to iterate over to prevent creating new instance every time 2024-04-26 20:12:45 +02:00
NEZNAMY 9a4fc2fc73 Fix field type 2024-04-26 11:52:55 +02:00
NEZNAMY 6846a78833 Require plugin list in a bug report 2024-04-26 11:52:15 +02:00
NEZNAMY d3d9e85a7f Merge branch '4.1.5'
# Conflicts:
#	build.gradle.kts
2024-04-26 11:47:03 +02:00
NEZNAMY f867b6b2ea 4.1.5-SNAPSHOT 2024-04-23 21:09:31 +02:00
NEZNAMY 6b38954694 4.1.4 2024-04-23 20:57:31 +02:00
NEZNAMY 9eb350df30 Fix startup on 1.19.3 - 1.20.4 2024-04-23 20:57:12 +02:00
NEZNAMY 639aa5e251 Fix startup on 1.19.3 - 1.20.4 2024-04-23 20:56:06 +02:00
NEZNAMY f089e26998 Added 1.20.5 support to Bukkit 2024-04-23 19:30:14 +02:00
NEZNAMY 9070a6aadc 4.1.3 2024-04-23 19:11:24 +02:00
NEZNAMY c68e135c66 Added 1.20.5 support to Bukkit 2024-04-23 19:11:17 +02:00
NEZNAMY 8693b4fe8f Mark 1.20.5 as latest supported version 2024-04-23 17:37:21 +02:00
NEZNAMY 9390a402ff Update Fabric to 1.20.5 2024-04-23 17:35:38 +02:00
NEZNAMY 7fda2c5f88 Add 1.20.5 ProtocolVersion constant 2024-04-23 17:35:15 +02:00
NEZNAMY cb394007a4 Update Fabric to 1.20.5 2024-04-23 17:25:00 +02:00
NEZNAMY cd92910281 Add 1.20.5 ProtocolVersion constant 2024-04-23 17:24:35 +02:00
NEZNAMY 7f0b2e2b13 Initialize feature data structured on declaration to make it cleaner & fix error 2024-04-23 16:16:51 +02:00
NEZNAMY bdc44d46c8 Add Exception throwing when attempting to use API class instances that got discarded on reload. 2024-04-23 16:10:24 +02:00
NEZNAMY 8d8480469a Update Fabric to 1.20.5-rc3 2024-04-23 15:28:30 +02:00
NEZNAMY a91fe6db36 Remove unused SneakyThrows 2024-04-23 12:51:41 +02:00
NEZNAMY 20d734aa80 Add missing action mapping 2024-04-23 12:43:20 +02:00
NEZNAMY f053bbd544 Access Team packet data directly on 1.17+ Fabric instead of using reflection 2024-04-23 12:40:14 +02:00
NEZNAMY 7eba26cab1 Rename class to match version number 2024-04-23 12:15:47 +02:00
NEZNAMY 5f77dd0adb Rename class to match version number 2024-04-23 12:14:42 +02:00
NEZNAMY 4ef94d3c60 Update Fabric to 1.20.5-rc3 2024-04-22 17:03:08 +02:00
NEZNAMY 36006045e2 Add "listed" TabList flag support into internal API 2024-04-22 16:54:39 +02:00
NEZNAMY d5d025c2c9 Store information about player meeting a feature's disable condition inside player class to improve performance by avoiding Map lookup each time 2024-04-22 14:41:34 +02:00
NEZNAMY 5b7053d500 Store feature data of players inside TabPlayer class to improve performance by avoiding Map lookup 2024-04-22 14:06:37 +02:00
NEZNAMY ce2e60c268 4.1.4-SNAPSHOT 2024-04-21 21:43:25 +02:00
NEZNAMY 8e9b575c37 Update Fabric to 1.20.5-rc2 2024-04-19 20:13:26 +02:00
NEZNAMY beab7d5a4f Update Fabric to 1.20.5-rc1 2024-04-18 16:26:16 +02:00
NEZNAMY d2f5fc4d69 Update Fabric to 1.20.5-pre4 2024-04-17 15:57:16 +02:00
NEZNAMY fb9a7f693b Update Fabric to 1.20.5-pre3 2024-04-16 16:48:03 +02:00
NEZNAMY 1d2d636270 Fix compilation warning on Fabric 1.14.4 submodule 2024-04-16 09:28:35 +02:00
NEZNAMY 37ec5ee442 Update Fabric to 1.20.5-pre2 2024-04-15 19:08:50 +02:00
NEZNAMY 5ed67cf5f8 Remove recently added config option that doesn't add any real value 2024-04-13 13:21:04 +02:00
NEZNAMY 76fc47d5bd Fix client lag when another plugin removes player from team (#1193) 2024-04-13 13:11:35 +02:00
NEZNAMY aa856beee1 Refactor team anti-override code to avoid duplicating code 2024-04-13 11:37:50 +02:00
NEZNAMY e6f2cc3eda Simplify 2024-04-12 15:00:32 +02:00
NEZNAMY 02fd45587b Fix replacements to %group% changing the group name inside plugin internals as well 2024-04-12 14:45:32 +02:00
NEZNAMY 7cef6eeb61 Update Fabric to 1.20.5-pre1 2024-04-10 19:11:43 +02:00
NEZNAMY da919bc43b Add BuiltByBit link to download links 2024-04-05 22:52:13 +02:00
NEZNAMY 2b661d6c5b Downgrade Java requirement on Fabric back to 17 2024-04-04 19:01:58 +02:00
NEZNAMY ee0ced63ef Update Fabric to 24w14a and Java version to 21 2024-04-03 21:22:35 +02:00
NEZNAMY 736dad5819 Merge pull request #1263 from JT122406/master
Update Loom and Gradle to Latest
2024-04-03 19:08:45 +02:00
Joseph T. McQuigg 11f36e8a22 Update Loom and Gradle to Latest 2024-04-03 13:05:57 -04:00
NEZNAMY 2cddf0b313 Add version compatibility check on Velocity 2024-03-31 12:03:58 +02:00
NEZNAMY 36a2d449b7 Add player version printing to debug command 2024-03-30 13:48:42 +01:00
NEZNAMY b6786456a2 Fix wrong method being overridden 2024-03-30 13:28:14 +01:00
NEZNAMY 76e8e1af6f Remove redundant .toLowerCase() 2024-03-30 12:33:49 +01:00
NEZNAMY ff55838a54 Remove unused imports 2024-03-29 14:21:21 +01:00
NEZNAMY b14b6828cc Move accesswidener file to resources/ folder instead of root in the compiled jar 2024-03-28 10:58:03 +01:00
NEZNAMY be7fb67988 Move configuration files to config/ folder instead of root in the compiled jar 2024-03-28 10:49:53 +01:00
NEZNAMY ae0be45d7d Drop 1.17(.0) support on Fabric (1.17.1 still supported) 2024-03-28 10:36:47 +01:00
NEZNAMY ba237d5dc4 Update Fabric to 24w13a 2024-03-28 10:27:22 +01:00
NEZNAMY bdf5cd8751 Remove unused constructor 2024-03-28 10:27:06 +01:00
NEZNAMY 9a6d309840 Update Fabric to 24w12a 2024-03-20 17:42:51 +01:00
NEZNAMY 9a6e92a58c Micro optimization of placeholder output replacements 2024-03-20 15:05:20 +01:00
NEZNAMY 1bc018918b Use common implementation of a method as default instead of making it abstract 2024-03-20 14:49:37 +01:00
NEZNAMY f32f8f5cdd Convert components in scoreboard class instead of each subclass 2024-03-20 14:39:46 +01:00
NEZNAMY 39585de3f0 Remove fallback solutions for Scoreboard on Bukkit 2024-03-20 14:16:18 +01:00
NEZNAMY 9ce81fc68e Convert component in abstract TabList class instead of manually in every class 2024-03-20 13:54:14 +01:00
NEZNAMY 0d56f0da98 Remove backup solutions for header/footer on Bukkit and only use packets 2024-03-20 13:49:31 +01:00
NEZNAMY 14a3d8da12 Save converted components as a field in TAB component instead of a separate cache that requires lookup, making it slower 2024-03-19 12:57:35 +01:00
NEZNAMY e4fa99b865 Use packets as primary solution for header/footer on Bukkit 2024-03-19 09:42:48 +01:00
NEZNAMY 96af2cb573 Drop native <gradient:#RRGGBB:#RRGGBB>Text</gradient> support to fix unexpected results when used in a way supported by MiniMessage but not this TAB function (and also because TAB called that function first) (#1252) 2024-03-18 22:40:57 +01:00
NEZNAMY f53ec58b2f Fix redundant empty component creation when using magic code after color code 2024-03-18 21:58:46 +01:00
NEZNAMY 2ff30dda83 Use HashMap instead of ConcurrentHashMap for better performance 2024-03-18 18:38:56 +01:00
NEZNAMY c0972f8faf Simplify 2024-03-18 15:52:25 +01:00
NEZNAMY 9663e1aa7e Remove redundant check 2024-03-18 15:46:28 +01:00
NEZNAMY d83a61b30a Replace &#RRGGBB with #RRGGBB sooner to save resources while refreshing 2024-03-18 15:35:59 +01:00
NEZNAMY 96d83da9b7 Remove unused method 2024-03-18 15:23:31 +01:00
NEZNAMY f1d881e0ba Add more checks to API methods 2024-03-18 13:41:23 +01:00
NEZNAMY d1285c7e04 Only send belowname update packets to players in the same world and server 2024-03-18 11:31:41 +01:00
NEZNAMY d4476e2d4c Optimize tablist packet creation on 1.19.3+ Bukkit & Fabric 2024-03-18 10:57:47 +01:00
NEZNAMY 3124d51381 Micro optimization of permission conditions 2024-03-18 10:19:12 +01:00
NEZNAMY b6ae1a79d4 Fix breaking errors on Fabric 1.16 - 1.18.2 2024-03-15 21:29:49 +01:00
NEZNAMY 42d7b598bf Improve comments in accesswidener 2024-03-15 21:29:00 +01:00
NEZNAMY 052e828d5a Split player spawn packet reading on Fabric based on version 2024-03-15 20:51:38 +01:00
NEZNAMY 55d4f8da6b Use logger directly on Fabric 1.18.2+ 2024-03-15 20:33:52 +01:00
NEZNAMY 1c5a4bbacc Simplify channel getting on BungeeCord 2024-03-15 16:54:27 +01:00
NEZNAMY 565d4bdeec Add java version requirement to fabric.mod.json 2024-03-15 15:47:56 +01:00
NEZNAMY 3d9ef7d252 Remove no longer needed SneakyThrows annotation 2024-03-15 15:39:23 +01:00
NEZNAMY 161bcbb236 Merge scoreboard packet listener methods into one 2024-03-15 15:22:23 +01:00
NEZNAMY c8af0ab5bc Use access widener on Fabric instead of reflection where easily possible 2024-03-14 20:46:04 +01:00
NEZNAMY 98e9c167c5 Update Fabric to 24w11a 2024-03-14 18:52:44 +01:00
NEZNAMY ed815c5767 Remove unintended return type 2024-03-14 16:14:14 +01:00
NEZNAMY f6e53ac91f Fix raw generic usage 2024-03-14 16:09:15 +01:00
NEZNAMY 3e5df50f9e Rename generic types 2024-03-14 16:05:44 +01:00
NEZNAMY 1e28a93c5f Replace star import with class imports 2024-03-14 16:02:13 +01:00
NEZNAMY 717bdd3550 Catch everything 2024-03-12 22:55:44 +01:00
NEZNAMY 3bee514bb9 Actually fix the previous error at its source 2024-03-12 10:32:56 +01:00
NEZNAMY 678d4b8db7 Use ConcurrentHashMap to fix ConcurrentModificationException 2024-03-12 10:02:06 +01:00
NEZNAMY d76e5e7f9b Simplify method (also probably speed it up) 2024-03-11 16:03:25 +01:00
NEZNAMY 9629192305 Simplify 2024-03-11 16:00:52 +01:00
NEZNAMY eef7debdba Remove unnecessary full class name 2024-03-09 11:35:23 +01:00
NEZNAMY 138d9f7f46 Initialize more variables in constructor 2024-03-08 12:52:47 +01:00
NEZNAMY e4abd9ec2f Refactor multi-version support on Fabric 2024-03-08 11:21:42 +01:00
NEZNAMY 8efce72e26 Remove component serialization code as it is no longer used 2024-03-07 19:04:27 +01:00
NEZNAMY 15ebca2d11 Use direct component conversion on Fabric instead of serializing and deserializing json 2024-03-07 18:47:55 +01:00
NEZNAMY 6583489523 Remove json deserializer support on Bukkit and only use direct component conversion 2024-03-07 17:15:53 +01:00
NEZNAMY 5610d52813 Remove part of a comment 2024-03-07 17:03:42 +01:00
NEZNAMY 618b61dd60 Use Adventure API to serialize components on Velocity 2024-03-07 17:03:24 +01:00
NEZNAMY 2de58f7a3b Remove RGB support in ViaBossbar (1.16+ clients on 1.8 server) 2024-03-07 16:44:50 +01:00
NEZNAMY 2bd94158f9 Remove redundant code 2024-03-07 14:47:56 +01:00
NEZNAMY 4e7ab43ac9 Update Fabric to 24w10a 2024-03-06 22:38:08 +01:00
NEZNAMY 33529d0cfd Slightly optimize vanish check and usage 2024-03-06 21:00:40 +01:00
NEZNAMY a5b929bdf5 Optimize tablist packet check on 1.19.3+ 2024-03-06 20:37:01 +01:00
NEZNAMY d10bd3358d Simplify 2024-03-05 15:58:14 +01:00
NEZNAMY ffe928fbe8 Save EnumSet for each tablist action into map and retrieve it instead of creating new Set on every packet send. 2024-03-05 15:51:16 +01:00
NEZNAMY fd61739d96 Remove deleted option from default config 2024-03-04 17:03:30 +01:00
NEZNAMY e1602a5655 Refactor TabList class to extract common content from each subclass into the main one 2024-03-04 13:48:53 +01:00
NEZNAMY a936302fd2 Rework tablist anti-override to save expected names and then restore the save values instead of creating them again 2024-03-04 11:45:18 +01:00
NEZNAMY 7f0d4df6ca Save teams for later use instead of recreating them every time 2024-03-03 21:23:47 +01:00
NEZNAMY 35ab0c67c5 Remove unused dependencies 2024-03-02 18:41:05 +01:00
NEZNAMY 090633140f Remove bug compensation 2024-03-02 17:21:16 +01:00
NEZNAMY 8efe02c2ef Further improve performance of command listener method (#1237) 2024-03-01 21:01:02 +01:00
NEZNAMY 2dbcaf029c Improve performance of a method if both scoreboard and bossbar features are disabled (#1237) 2024-03-01 20:28:50 +01:00
NEZNAMY ce758534bd Remove ClickEvent support as it's never used by the plugin 2024-03-01 11:58:19 +01:00
NEZNAMY 15caf70170 Update Bukkit 1.20.5 draft to 24w09a 2024-03-01 10:54:04 +01:00
NEZNAMY c07c3a3a4f Update Fabric to 24w09a 2024-03-01 10:37:43 +01:00
NEZNAMY b9000af55e Implement direct component conversion on Bukkit and use it as primary option. 2024-02-29 21:32:29 +01:00
NEZNAMY ed15ba29bb Create separate class for component conversion on Bukkit (will be extended soon) 2024-02-29 12:42:00 +01:00
NEZNAMY 2fb8da022a Fix startup on <1.8 2024-02-29 12:28:49 +01:00
NEZNAMY ed65088cb8 Add API method for setting player's expected game profile name (#1240) 2024-02-29 09:24:45 +01:00
NEZNAMY f367542096 Replace unoptimized online player check with an instant one 2024-02-28 14:19:38 +01:00
NEZNAMY 00e457542d Remove TabPlayer#isOnline as it was never really used 2024-02-28 14:12:22 +01:00
NEZNAMY b0c423b2dd Change sortingPlaceholder data type to avoid retrieving it each time 2024-02-27 20:57:32 +01:00
NEZNAMY eb5e3d637c Split method into multiple for better readability 2024-02-27 14:45:03 +01:00
NEZNAMY 4acd24b829 Make permission-related options refresh interval configurable 2024-02-27 13:59:58 +01:00
NEZNAMY f8576834fe Optimize method 2024-02-27 13:12:40 +01:00
NEZNAMY 1e9888a8b5 Optimize method 2024-02-27 13:08:17 +01:00
NEZNAMY d99b6643e9 Fix team color defaulting to black on Sponge 2024-02-27 11:17:16 +01:00
NEZNAMY 0b98d98867 Add Sponge 1.20.4 support 2024-02-27 11:04:55 +01:00
NEZNAMY d040b0faf5 Save global playerlist server group mapping instead of computing it on each request 2024-02-27 10:16:55 +01:00
NEZNAMY d53c20da56 Do not respawn armor stands on sneak for flying players (#1236). 2024-02-26 20:28:06 +01:00
NEZNAMY fad035699f Revert broken commit 2024-02-26 19:00:28 +01:00
NEZNAMY c7ab2b93a3 Move armor stands instead of teleporting them when players move. This should result in a significantly better sync. May also help with #1236 a bit. 2024-02-26 18:46:51 +01:00
NEZNAMY 4b75144276 Create cache for last color of prefix 2024-02-26 11:16:30 +01:00
NEZNAMY 99edcf68c1 Use new TabList method for better performance 2024-02-25 15:05:56 +01:00
NEZNAMY 9b6f1ce955 Add TabList#containsEntry method 2024-02-25 15:00:34 +01:00
NEZNAMY b570020944 Only send global playerlist latency update packets of players a player can actually see 2024-02-23 20:44:57 +01:00
NEZNAMY e91ba4ef79 Slightly speed up method 2024-02-23 19:14:45 +01:00
NEZNAMY a1fbefb13f Set use-numbers to true by default because buggy clients don't work without it and will not fix it anytime soon 2024-02-22 23:42:57 +01:00
NEZNAMY 340592d951 Document class 2024-02-22 11:59:45 +01:00
NEZNAMY a1c0bf7dec Use new Paper API for Scoreboard NumberFormat in fallback PaperScoreboard implementation 2024-02-20 18:49:52 +01:00
NEZNAMY a62f20469e Replace NotNull with NonNull in Scoreboard class for better troubleshooting 2024-02-19 20:22:17 +01:00
NEZNAMY 3f1f78315b Add Getter 2024-02-19 20:12:18 +01:00
NEZNAMY 12d8bc75cd Replace NotNull with NonNull in TabList class for better troubleshooting 2024-02-19 20:10:26 +01:00
NEZNAMY c1cf02ee9f Fix ConcurrentModificationException 2024-02-19 16:17:13 +01:00
NEZNAMY 7ac240af76 Update Fabric to 24w07a 2024-02-15 09:29:33 +01:00
NEZNAMY 82c34a33b5 Do not update tablist format for players on different servers who the player cannot see (#1232) 2024-02-14 18:08:04 +01:00
NEZNAMY d2330337ef Simplify method 2024-02-14 16:56:45 +01:00
NEZNAMY af38bd0383 Fix double score send on join 2024-02-14 11:52:07 +01:00
NEZNAMY c0a9ede377 Add runtime warn when belowname / playerlist objective placeholder returned decimal number 2024-02-14 11:45:40 +01:00
NEZNAMY 555ba3eb73 Split method into multiple for better readability and clean up 2024-02-11 20:24:54 +01:00
NEZNAMY 6f0e757ad6 Add console warn if condition type is needed but not configured 2024-02-11 20:07:59 +01:00
NEZNAMY 077b8fee96 Add more info into error message 2024-02-11 16:11:55 +01:00
NEZNAMY 643df47437 (Attempt to) fix deadlock (#1230) 2024-02-11 10:28:43 +01:00
NEZNAMY 9e1ea4678d Create shared empty instance for PlaceholderReplacementPattern to avoid new instances even if nothing is configured 2024-02-10 17:37:20 +01:00
NEZNAMY f3576b678b Make MySQL errors more user-friendly 2024-02-10 11:22:52 +01:00
NEZNAMY 2b49bce998 Add config option to disable SSL certification in MySQL if the database is not using it 2024-02-10 10:22:42 +01:00
NEZNAMY 896048ffff Replace "featureName" field with getter 2024-02-09 16:56:12 +01:00
NEZNAMY 04cb14f790 Add some more Javadoc 2024-02-09 16:12:16 +01:00
NEZNAMY bd22b0c08b Properly remove disconnected player from bossbars 2024-02-09 15:47:47 +01:00
NEZNAMY 95ce52bbeb Add TODO note 2024-02-09 11:04:40 +01:00
NEZNAMY 6b411f5cce Slightly optimize method 2024-02-09 10:56:59 +01:00
NEZNAMY c15b0d178b Create array field to iterate over for slightly better performance 2024-02-09 10:40:48 +01:00
NEZNAMY f5ffb69675 Disable tab expansion by default 2024-02-08 20:59:18 +01:00
NEZNAMY 24e59d970f Initialize NotNull field just in case 2024-02-08 17:10:55 +01:00
NEZNAMY 4ece4b66cd Only initialize map if used 2024-02-07 22:02:50 +01:00
NEZNAMY 7852d244bb Fix #1227 2024-02-07 21:37:27 +01:00
NEZNAMY d1c2487ba4 Enable CPU usage tracking only after running /tab cpu for the first time 2024-02-07 20:43:32 +01:00
NEZNAMY 4ea789efcf Add config option to disable refreshing placeholders in another thread (boosts performance) 2024-02-07 20:17:02 +01:00
NEZNAMY 43fc97c671 Update Fabric to 24w06a 2024-02-07 18:28:09 +01:00
NEZNAMY a20378d5cb Change abstract class to interface 2024-02-06 21:58:35 +01:00
NEZNAMY 7c0384c96b Create local variable to prevent spamming Map#get 2024-02-06 18:51:22 +01:00
NEZNAMY 7d4419b300 Override some methods in EmptyTabExpansion to avoid unnecessary operations 2024-02-06 17:56:28 +01:00
NEZNAMY 393af9b5c5 Add check to avoid unnecessary operations 2024-02-06 17:55:12 +01:00
NEZNAMY 559e423a1d Vastly improve performance of CPU usage tracking 2024-02-06 17:43:45 +01:00
NEZNAMY a2d68e14e6 Simplify 2024-02-06 17:10:21 +01:00
NEZNAMY 580b67d9ed Fix thread submitting tasks to itself 2024-02-06 16:45:57 +01:00
NEZNAMY 080654f703 Increase scoreboard line length limit by 2 characters on <1.13 2024-02-05 20:14:36 +01:00
NEZNAMY 002a002749 Remove redundant check 2024-02-05 19:19:51 +01:00
NEZNAMY 7e888e46f9 Implement best parts of #1219 2024-02-05 10:57:40 +01:00
NEZNAMY 199ca94acd Add compensation for 1.8.0 client-sided bug directly into TabList class (#1225) 2024-02-04 22:03:29 +01:00
NEZNAMY 67455bd47d Fix replacements not applying in nested placeholders 2024-02-04 18:32:21 +01:00
NEZNAMY fbb0e252c5 Allow -1 in refresh intervals 2024-02-04 17:57:44 +01:00
NEZNAMY 2566972ac7 Recode internal component class 2024-02-03 18:12:37 +01:00
NEZNAMY 9455294060 Simplify 2024-02-03 13:24:57 +01:00
NEZNAMY 6870b42a1a Merge branch 'master' of https://github.com/NEZNAMY/TAB 2024-02-03 11:22:45 +01:00
NEZNAMY 32cd414a51 Merge pull request #1224 from XXMA16/fix-spongeforge
Fix SpongeForge startup crash
2024-02-03 11:22:35 +01:00
XXMA16 35f63b7075 Fix code style
Signed-off-by: XXMA16 <xxma1610@gmail.com>
2024-02-03 02:43:51 +02:00
XXMA16 f9097b11b6 Fix SpongeForge startup crash
Signed-off-by: XXMA16 <xxma1610@gmail.com>
2024-02-02 19:37:19 +02:00
NEZNAMY dc5acdf729 Add default implementation to method instead of making it abstract 2024-02-01 22:09:49 +01:00
NEZNAMY e5fb25daee Update Bukkit 1.20.5 draft to match 24w05b changes 2024-02-01 20:47:03 +01:00
NEZNAMY 33b6867c02 Update Fabric to 24w05b 2024-02-01 20:39:16 +01:00
NEZNAMY d572973b60 Add console hint when configuring redundant placeholder replacement pattern using "else" 2024-01-31 19:28:30 +01:00
NEZNAMY 3d240f0f7d Fix NPE 2024-01-31 19:12:13 +01:00
NEZNAMY 3a4973d760 Change sidebar team naming 2024-01-29 11:43:25 +01:00
NEZNAMY 811c9716a2 Fix message 2024-01-28 13:26:04 +01:00
NEZNAMY 771f6db89a Remove unused imports 2024-01-28 10:55:44 +01:00
NEZNAMY 97f515f573 Remove placeholder output replacement from default config 2024-01-27 20:19:17 +01:00
NEZNAMY c589dfb281 Fix per-world settings of unlimited nametags not updating on world switch 2024-01-27 20:01:41 +01:00
NEZNAMY 82efe5b4d1 Simplify 2024-01-27 13:57:25 +01:00
NEZNAMY 765f593b88 Specify locale in .toLowerCase to avoid error when java is using a weird language 2024-01-27 13:52:01 +01:00
NEZNAMY 21e5de25cb Remove console warn when ViaVersion returns unknown version, because kids kept asking about it on ViaVersion discord despite the message clearly saying to update TAB. 2024-01-27 13:42:06 +01:00
NEZNAMY 4333e44f6d Fix false warn on proxy when bukkit permissions are enabled 2024-01-26 21:21:40 +01:00
NEZNAMY 7070fd886b Add support for Bukkit jars without CraftBukkit relocation (#1220) 2024-01-25 15:15:12 +01:00
NEZNAMY 89480a9018 Remove cpu report printing into console if overloaded and debug is enabled 2024-01-24 18:16:15 +01:00
NEZNAMY 19ce873037 Update Fabric to 24w04a 2024-01-24 17:30:58 +01:00
NEZNAMY 977007b427 Do not cache subcomponents on bungeecord (#1211) 2024-01-24 17:07:37 +01:00
NEZNAMY 218c1c9d9a Remove global serverVersion field and only use it in each platform if needed 2024-01-24 15:32:38 +01:00
NEZNAMY f358ac9d9c Create and use Platform#isProxy method 2024-01-24 14:02:38 +01:00
NEZNAMY 58fe2b4582 Make "enable-collision" support conditions 2024-01-24 13:08:03 +01:00
NEZNAMY 211624b1a9 Improve style of iteration 2024-01-24 11:56:01 +01:00
NEZNAMY 6874369a48 Simplify animation placeholder creation 2024-01-24 11:53:52 +01:00
NEZNAMY f8c52d3577 Initialize player group in declaration to prevent "Vault through Bridge" returning null on the first time (#1211) 2024-01-24 09:58:16 +01:00
NEZNAMY 376bf5314d Actually fix trying to process offline players (players are still marked as online by Bukkit API even after they disconnect) 2024-01-23 18:18:12 +01:00
NEZNAMY 2346df67a0 Fix trying to process offline players in placeholder update task 2024-01-23 18:05:01 +01:00
NEZNAMY e63c7b43e1 Show a runtime warn if belowname / playerlist objective cannot be evaluated to a number 2024-01-23 18:04:18 +01:00
NEZNAMY 578691fe8c Document and clean up class 2024-01-22 19:15:07 +01:00
NEZNAMY 189d8a82d5 Update default config to account for missing NumberFormat in scoreboards in bedrock edition 2024-01-22 17:43:45 +01:00
NEZNAMY 9412d9cc28 Add %bedrock% placeholder returning true for bedrock clients 2024-01-22 17:40:02 +01:00
NEZNAMY 75b2bfa14c Implement TabPlayer#isOnline on Fabric 2024-01-22 15:40:09 +01:00
NEZNAMY 7fcb5c8aa3 Create functions for different error types instead of printing them directly 2024-01-22 15:15:59 +01:00
NEZNAMY 9827711111 Simplify 2024-01-22 13:41:06 +01:00
NEZNAMY 16aecd65e6 Add error message when permission plugin returned null group 2024-01-22 13:32:29 +01:00
NEZNAMY c143ecc6ae Remove redundant Getter annotation 2024-01-20 17:56:29 +01:00
NEZNAMY 2935f9e205 Clean up 2024-01-20 17:54:51 +01:00
NEZNAMY a6a33a79d4 Attempt to fix error (#1218) 2024-01-20 17:22:23 +01:00
NEZNAMY 82f9d8ddee Attempt to fix error (#1218) 2024-01-20 11:08:59 +01:00
NEZNAMY daa6012b36 Simplify 2024-01-20 11:05:45 +01:00
NEZNAMY 5001c82f23 Update Fabric to 24w03b 2024-01-18 21:17:10 +01:00
NEZNAMY 59dbad9abf Fix snapshot name in fabric.mod.json 2024-01-18 12:12:13 +01:00
NEZNAMY 945c7cc7b8 Draft 1.20.5 support on Bukkit 2024-01-18 11:19:30 +01:00
NEZNAMY 3391f7646c Simplify 2024-01-17 23:27:27 +01:00
NEZNAMY 88070aa2b2 Use different constructor to simplify usage 2024-01-17 23:06:15 +01:00
NEZNAMY c792296746 Use reflection to get rid of 2 Fabric submodules 2024-01-17 23:06:06 +01:00
NEZNAMY 18a13f777d Allow empty output in sorting by pre-defined values 2024-01-17 22:27:34 +01:00
NEZNAMY 469529b324 Update Fabric to 24w03a 2024-01-17 21:15:26 +01:00
NEZNAMY 4715a5ab9d Use static number as a start of numbering if use-numbers is enabled 2024-01-17 19:08:46 +01:00
NEZNAMY be305162a6 Fix scoreboard use-numbers duplicating numbers 2024-01-17 19:08:17 +01:00
NEZNAMY 7a1bc16e9d Fix NPE 2024-01-17 11:27:36 +01:00
NEZNAMY eb2c80deb8 Add try/catch for ViaVersion hook (#1201) 2024-01-16 21:56:51 +01:00
NEZNAMY 0863b77bf0 Remove unused import 2024-01-16 20:51:16 +01:00
NEZNAMY 769a698509 Fix NPE and replace it with a warning (#1198) 2024-01-16 20:50:51 +01:00
NEZNAMY 53870b2c81 Fix anti-override on Velocity and Sponge no longer working after players are removed & added back to the tablist by another plugin 2024-01-16 12:54:52 +01:00
NEZNAMY e198a5df88 Another attempt to fix #1212 2024-01-16 11:17:28 +01:00
NEZNAMY ac947f698b Fix ClassCastException (#1213) 2024-01-16 11:05:06 +01:00
NEZNAMY ecabd62b6b Catch another exception (#1212) 2024-01-15 10:35:09 +01:00
NEZNAMY f350d200ac Fix memory leak 2024-01-14 20:28:35 +01:00
NEZNAMY 97d5a3f7ef Print all exceptions in a chain, not just the last one 2024-01-14 13:25:59 +01:00
NEZNAMY f15f3efc3f Add plugin's name prefix into console messages on Fabric 2024-01-14 11:48:24 +01:00
NEZNAMY 7563b6b946 Convert \n to newline if snakeyaml does not do it because text is in '' and not "" 2024-01-13 18:52:21 +01:00
NEZNAMY 421c53e6be Replace forEach with for 2024-01-13 18:06:16 +01:00
NEZNAMY 3db8666951 Hide spectator effect for players who unvanished (#1209) 2024-01-13 12:58:00 +01:00
NEZNAMY ecc6e01cf9 Add links to Tanguygab's new discord server where he will provide support to kids 2024-01-12 10:39:12 +01:00
NEZNAMY becf81db00 Merge 2 fields into 1 2024-01-10 15:04:50 +01:00
NEZNAMY 92f5b337c9 Use dummy implementation if original one is not available & remove redundant class 2024-01-10 14:26:56 +01:00
NEZNAMY 3ddf884c23 Get CraftBukkit classes using package name instead of version as recommended by Paper 2024-01-10 14:18:54 +01:00
NEZNAMY 116117af55 Create method to simplify usage 2024-01-09 18:20:51 +01:00
NEZNAMY 0980bcfca5 Merge pull request #1202 from Jake-Moore/master
Support to use Global Playerlist `server-groups` as Header/Footer `per-server` keys.
2024-01-09 18:12:05 +01:00
NEZNAMY 20e2e8ded6 Fix relational placeholders not refreshing properly & nested relational placeholders not being marked as used by the feature (and therefore not refreshing properly) (#1203) 2024-01-09 18:06:35 +01:00
Jake Moore 13af0b7b2b Improve handling of failed cases 2024-01-08 20:58:19 -08:00
Jake Moore cbcbfdcbf1 Adds support to use Global Playerlist server-groups as Header/Footer keys in per-server. Allowing for configuration of a server group with several velocity servers which can have a unique header-footer section. 2024-01-08 17:07:44 -08:00
NEZNAMY 38eb294667 Move version check 2024-01-08 12:24:50 +01:00
NEZNAMY 71d16ff906 Only send messages that actually affect the user 2024-01-08 12:22:19 +01:00
NEZNAMY 395058d771 Send console message saying how long NMS hook took to load 2024-01-08 12:13:58 +01:00
NEZNAMY d8af7c454c Simplify 2024-01-07 23:07:38 +01:00
NEZNAMY 2f10084e7a Replace more fields with functional interfaces 2024-01-07 22:56:08 +01:00
NEZNAMY 5af387b32d Move ScorePacketData to a separate class 2024-01-07 21:41:10 +01:00
NEZNAMY 4b7144f705 Replace fields with functional interfaces for NMS access 2024-01-07 21:20:24 +01:00
NEZNAMY 0fbbbb6485 Simplify 2024-01-07 20:23:09 +01:00
NEZNAMY f033d78785 Optionally print compatibility exception into console 2024-01-07 20:08:44 +01:00
NEZNAMY 2ea79eb57d Remove unused import 2024-01-07 14:44:49 +01:00
NEZNAMY 226ee0db9d Include supported version range in compatibility error message 2024-01-06 22:45:21 +01:00
NEZNAMY 8a62f21e84 Improve compatibility error messages 2024-01-06 22:25:33 +01:00
NEZNAMY c353c39086 Make entity view compatibility on Bukkit optional 2024-01-06 21:33:19 +01:00
NEZNAMY 2b2c877ab2 Add more Javadoc 2024-01-06 21:15:20 +01:00
NEZNAMY 016af4bfee Make DummyEntityView methods do nothing instead of throwing 2024-01-06 20:54:23 +01:00
NEZNAMY b3d26d91de Add console warn when waterfall option causing issues is enabled 2024-01-06 17:23:41 +01:00
NEZNAMY a7a7f409eb Fix sorting note getting overridden on refresh when set using API 2024-01-06 15:52:24 +01:00
NEZNAMY f44ccec712 Remove StaticLine class and use LongLine instead for the same effect 2024-01-06 14:29:08 +01:00
NEZNAMY 1c0887733c Initialize property names to random values instead of specific names 2024-01-06 14:14:33 +01:00
NEZNAMY 7ee3c35785 Rename some cpu usage categories to make them easier to understand 2024-01-06 14:01:13 +01:00
NEZNAMY 01b725cf38 Define team color immediately and forward it instead of figuring it out later 2024-01-05 21:30:43 +01:00
NEZNAMY 1ac6d90633 Add a runtime warn when sorting element is not in list (group, placeholder, or no permission) 2024-01-02 16:27:08 +01:00
NEZNAMY 9704193237 Add a runtime warn when numeric placeholder in condition did not return a number 2024-01-02 15:43:22 +01:00
NEZNAMY 69b150b391 Make team color always white for scoreboard lines (#1189) 2024-01-02 14:13:17 +01:00
NEZNAMY 7ffb3edda5 Spawn entity using bundle packet for 1.19.4+ to prevent frame being rendered between packets, causing visible armor stands for a single frame (#1168) 2024-01-02 13:10:20 +01:00
NEZNAMY 046eb68ce6 Split MisconfigurationHelper class into 3 for different message types 2024-01-01 22:03:08 +01:00
NEZNAMY 816d80a27c Construct ArmorStand with non-null level on Fabric (#1191) (one more) 2023-12-31 11:27:17 +01:00
NEZNAMY 957037e5f4 Construct ArmorStand with non-null level on Fabric (#1191) 2023-12-30 15:38:54 +01:00
NEZNAMY 9ca179a71f Make BungeeCord compatibility error message more clear about what exactly went wrong (#1190). 2023-12-30 15:34:12 +01:00
NEZNAMY cb4d96f1ba Remove unused imports 2023-12-29 22:15:05 +01:00
NEZNAMY 7d57522775 Add more Javadoc 2023-12-29 22:04:54 +01:00
NEZNAMY 902565f1b1 Remove TabList.Entry.Builder class and build entries directly 2023-12-29 19:07:13 +01:00
NEZNAMY 2f8de436af Improve Javadoc 2023-12-29 18:56:17 +01:00
NEZNAMY 38bd035d99 Create separate class for each incoming plugin message and move handling method to ProxyPlatform class 2023-12-29 18:29:51 +01:00
NEZNAMY 88b6d1a3ca Remove redundant SneakyThrows and replace some instances with throws 2023-12-29 14:40:17 +01:00
NEZNAMY a74878e7bb Make PacketSender an instance class 2023-12-29 14:17:29 +01:00
NEZNAMY 2b6eba7c4d Replace star import with direct imports 2023-12-26 12:21:06 +01:00
NEZNAMY 0de1b86ef5 Remove unnecessary static modifier 2023-12-26 12:19:46 +01:00
NEZNAMY ae0ab6471b Remove unused import 2023-12-26 12:17:58 +01:00
NEZNAMY c24cdaa8a4 Fix json syntax error when sorting character ends up being \ (#1184) 2023-12-24 23:17:07 +01:00
NEZNAMY df1442dd8a Fix YellowNumber & BelowName sending score update to disabled players 2023-12-24 15:30:57 +01:00
NEZNAMY b17d5306db Create and use TabFeature#config method for shorter code in all features 2023-12-24 15:08:34 +01:00
NEZNAMY 88d5294b8e Skip placeholder refreshing code if no placeholders are being refreshed 2023-12-24 14:25:25 +01:00
NEZNAMY 8c3405d01f Replace streams with direct code for better performance 2023-12-24 14:23:13 +01:00
NEZNAMY 9f83ba700c Fix bossbar & scoreboard writing into file on every join when remembering toggle choice is enabled even if value didn't change 2023-12-23 23:06:08 +01:00
NEZNAMY ae147211ca Code style 2023-12-23 22:58:01 +01:00
NEZNAMY 01d305eba0 Move scoreboard packet listening from pipeline class to scoreboard class 2023-12-23 21:54:26 +01:00
NEZNAMY 08c30574a9 Code style 2023-12-23 21:01:25 +01:00
NEZNAMY 39c4725a2f Change access from public to private 2023-12-23 19:45:32 +01:00
NEZNAMY 7002aeefaf Create constants for "magic numbers" 2023-12-23 17:28:19 +01:00
NEZNAMY f1201cc5bc Code style 2023-12-23 16:30:14 +01:00
NEZNAMY 0b99d4ac52 Extract duplicated code into a method 2023-12-23 16:29:11 +01:00
NEZNAMY 642cb701dd Remove unused import 2023-12-23 15:42:49 +01:00
NEZNAMY 66d6aee115 Move method to avoid duplication 2023-12-23 15:39:01 +01:00
NEZNAMY 38d8eb1108 Simplify 2023-12-23 15:20:09 +01:00
NEZNAMY f9941c5dff Add some more Javadocs 2023-12-23 14:48:26 +01:00
NEZNAMY 855a6f447a Use constants for bStats plugin ids 2023-12-23 13:59:02 +01:00
NEZNAMY fa0a1f8553 Create and use ProtocolVersion#supportsRGB instead of manually comparing number each time. 2023-12-23 13:55:52 +01:00
NEZNAMY 0d1fee1636 Add missing Javadoc to Fabric 2023-12-23 13:48:35 +01:00
NEZNAMY ec8269332f Add missing Javadoc to Bukkit 2023-12-23 13:31:16 +01:00
NEZNAMY 00fe9400e6 Add missing Javadoc to BungeeCord 2023-12-23 12:40:59 +01:00
NEZNAMY 8389b1e151 Add missing Javadoc to API 2023-12-23 12:31:09 +01:00
NEZNAMY 2289d10d6c Add missing Javadoc to Sponge and Velocity 2023-12-23 12:22:01 +01:00
NEZNAMY b30af4f144 Remove redundant "new" when creating arrays 2023-12-23 12:00:05 +01:00
NEZNAMY 041322c53f Update latest known version field 2023-12-23 11:21:03 +01:00
NEZNAMY 548968bcfc Print console warn on startup if error log has reaches its size limit 2023-12-22 20:37:31 +01:00
NEZNAMY 6d405b70e7 Properly apply replacements to server placeholders initially 2023-12-22 17:27:02 +01:00
NEZNAMY 1b4d4acc63 Fix map merging mistake 2023-12-22 17:26:24 +01:00
NEZNAMY 6f498089fb Add back option to disable tab expansion 2023-12-22 15:32:53 +01:00
NEZNAMY 8e27cba0f6 Fix compilation 2023-12-22 15:30:05 +01:00
NEZNAMY ae48041a96 Simplify 2023-12-21 23:55:57 +01:00
NEZNAMY bad16a4cdc Update Fabric to 23w51b 2023-12-21 20:50:16 +01:00
NEZNAMY 07baf769dd Suppress random irrelevant warnings 2023-12-21 20:21:23 +01:00
NEZNAMY ab409643f6 Convert class to record 2023-12-21 18:00:36 +01:00
NEZNAMY c745a61601 Recode placeholder refreshing to make refreshing thread not do anything more than placeholder requests & various minor performance improvements 2023-12-21 17:57:47 +01:00
NEZNAMY c4e8e83d1e Add check to improve method's performance 2023-12-21 13:03:49 +01:00
NEZNAMY 9133f3d792 Repackage classes and remove unused interface 2023-12-21 09:54:22 +01:00
NEZNAMY b779d584a5 Speed up placeholder replacement code if nothing is configured 2023-12-20 17:31:22 +01:00
NEZNAMY 05bb61b3ab Improve performance of a method 2023-12-20 15:50:25 +01:00
NEZNAMY 5d2885e662 Wrap maps into synchronizedMap to avoid deadlock on concurrent access 2023-12-20 15:48:26 +01:00
NEZNAMY ef645c1058 Fix random NPE 2023-12-17 20:16:09 +01:00
NEZNAMY 52a476fd0a Print cpu report into console if plugin is overloaded and debug is enabled 2023-12-17 11:19:08 +01:00
NEZNAMY 7386c45f06 Implement TabList class on Bukkit for 1.7, implement a backup solution using API and make the loading optional 2023-12-16 15:53:51 +01:00
NEZNAMY 1a508ddedf Fix error on old versions 2023-12-16 14:28:31 +01:00
NEZNAMY a8675f254a Move sendPacket call 2023-12-15 18:46:03 +01:00
NEZNAMY 0f70cd789e Move team anti-override to scoreboard class 2023-12-15 14:53:47 +01:00
NEZNAMY b8f594f42c Move tablist packet processing to tablist class 2023-12-15 12:20:17 +01:00
NEZNAMY 7857527ce1 Add console warn if floodgate is not configured correctly 2023-12-11 20:40:14 +01:00
NEZNAMY 05c41a5469 Create separate class for PremiumVanish hook & add check for outdated version to avoid an error (#1171) 2023-12-11 15:11:46 +01:00
NEZNAMY f11f8afdc4 Update README.md 2023-12-11 09:05:28 +01:00
NEZNAMY fa499aa46d Merge pull request #1170 from NicoNekoDev/master
Add NexEngine gradient
2023-12-10 13:25:05 +01:00
NicoNekoDev e8a7cc026f Documentation 2023-12-10 14:09:53 +02:00
NicoNekoDev 433ef6a8a7 Placeholders and legacy text 2023-12-10 14:08:09 +02:00
NicoNekoDev fc4b5a7bcd Fix codestyle 2023-12-10 13:40:44 +02:00
NicoNekoDev bd43134d58 Fix text 2023-12-10 13:26:34 +02:00
NicoNekoDev 557970ba4e Add NexEngine gradient
https://github.com/nulli0n/NexEngine-spigot/wiki/Configuration-Tips
2023-12-10 11:18:41 +02:00
NEZNAMY d3418e0067 Remove duplicate method 2023-12-09 20:52:44 +01:00
NEZNAMY b8d1845d4e Change way of detecting 1.19.4+ flag 2023-12-09 19:03:11 +01:00
NEZNAMY 32e360409d Create and use method for retrieving a CraftBukkit class instead of doing it manually each time (the method might also change in the future according to Paper devs, this will also make modifying and maintenance easier) 2023-12-09 19:00:46 +01:00
NEZNAMY c54a778af9 Fix RGB color conversion from adventure component to internal component 2023-12-09 15:26:55 +01:00
NEZNAMY ce25698f87 Do not cache nested components 2023-12-09 14:26:49 +01:00
NEZNAMY 4d95cc838a Change way of multi-version code handling on Fabric to significantly reduce complexity, module count and compilation time 2023-12-09 14:14:31 +01:00
NEZNAMY 67fa0a231e Add more misconfiguration checks for layout feature 2023-12-09 11:23:39 +01:00
NEZNAMY 3c2e8c81c4 4.1.3-SNAPSHOT 2023-12-08 09:50:11 +01:00
NEZNAMY 484d46252b 4.1.2 2023-12-08 09:47:09 +01:00
NEZNAMY a2e3c67298 Fix MiniMessage parsing causing issues to different syntax rules (#1166) 2023-12-08 09:46:36 +01:00
NEZNAMY b2ea05d8dd Merge 1.14.4 and 1.15.2 fabric modules for faster compilation 2023-12-07 20:26:13 +01:00
NEZNAMY 1477f47962 Merge 1.17 and 1.17.1 fabric modules for faster compilation 2023-12-07 19:18:25 +01:00
NEZNAMY 273ce49c14 4.1.2-SNAPSHOT 2023-12-07 19:16:56 +01:00
NEZNAMY 96b6790646 4.1.1 2023-12-07 18:50:30 +01:00
NEZNAMY 38227ec878 Update Fabric to 1.20.4 2023-12-07 18:48:08 +01:00
NEZNAMY 11da632972 Add 1.20.4 to version list 2023-12-07 17:46:51 +01:00
NEZNAMY 3721b9fe22 Fix MiniMessage not working in scoreboard 2023-12-07 14:12:13 +01:00
NEZNAMY ecb697e5fb Update default config to match recent scoreboard change (using ||) 2023-12-07 13:31:51 +01:00
NEZNAMY c754772280 Remove warn if ViaVersion returns -1 version (finally got to reproduce it & make tests) 2023-12-06 18:21:58 +01:00
NEZNAMY 5f2b4ba0b3 Fix disconnect on BungeeCord on 1.20.3 2023-12-06 13:46:21 +01:00
NEZNAMY 0ab60becfc Change scoreboard line number format split sequence to || instead of | to prevent false trigger when trying to actually display the character. 2023-12-05 23:01:54 +01:00
NEZNAMY 1dcfb7d6dc 4.1.1-SNAPSHOT 2023-12-05 23:01:11 +01:00
NEZNAMY 3c0b44b365 4.1.0 2023-12-05 20:45:58 +01:00
NEZNAMY ec4aec50ef Mark 1.20.3 as latest known version 2023-12-05 18:55:42 +01:00
NEZNAMY 9c86cfe056 Add Vault to softdepend to avoid console warn 2023-12-05 18:53:49 +01:00
NEZNAMY 6bb2337276 Print warn if RedisSupport received unknown message instead of throwing a NPE 2023-12-05 18:49:53 +01:00
NEZNAMY 4702ca255b Update Fabric to 1.20.3 2023-12-05 18:48:54 +01:00
NEZNAMY ab47aa36cc Confirm network id of 1.20.3 2023-12-05 18:40:55 +01:00
NEZNAMY 4bb14c4a4e Rename new config options 2023-12-05 17:27:24 +01:00
NEZNAMY 1dd75162f9 Add a version to list 2023-12-04 21:09:37 +01:00
NEZNAMY a2242e3c27 Move server version check 2023-12-04 20:52:29 +01:00
NEZNAMY 26ecf494a9 Little fix 2023-12-04 20:45:51 +01:00
NEZNAMY 659af5d0b3 Completely get rid of NMSStorage class 2023-12-04 20:33:23 +01:00
NEZNAMY 382a5ee7fc Move packet sending method to a separate class and make loading optional 2023-12-04 19:17:51 +01:00
NEZNAMY 7364496844 Move ping retrieving method to a separate class 2023-12-04 17:52:32 +01:00
NEZNAMY d342316b87 Reduce dependence on the main NMS class on Bukkit 2023-12-04 17:29:25 +01:00
NEZNAMY b38141da88 Add &f at the beginning of scoreboard lines for 1.13+ players to prevent color field from coloring text if no color is used 2023-12-04 16:40:06 +01:00
NEZNAMY 98f80f7dae Remove unused methods 2023-12-03 21:36:22 +01:00
NEZNAMY c134ec6bfe Simplify class loading 2023-12-03 21:32:00 +01:00
NEZNAMY 4e3f759243 Use NMS prefix only once instead of every usage 2023-12-03 21:03:08 +01:00
NEZNAMY a5f2e0157b Make player game profile getting optional instead of throwing a compatibility error 2023-12-03 20:52:29 +01:00
NEZNAMY fd21032e51 Make pipeline injection compatibility optional and print a warning if a compatibility error is found 2023-12-03 19:22:33 +01:00
NEZNAMY c4f5f9b7d3 Update comment 2023-12-03 18:11:40 +01:00
NEZNAMY 6baf5a7068 Fix NPE 2023-12-03 17:28:41 +01:00
NEZNAMY d3460174de Enable fallback solutions for Scoreboard implementation on Bukkit 2023-12-03 16:07:05 +01:00
NEZNAMY f9f19628ca Fix BukkitScoreboard class issues 2023-12-03 15:38:22 +01:00
NEZNAMY 04e48fea5d Clean up method 2023-12-01 20:22:41 +01:00
NEZNAMY 3e99e22fce Simplify 2023-12-01 19:18:58 +01:00
NEZNAMY 0b244d5ff7 Refactor PacketScoreboard class and draft 1.20.3 support 2023-12-01 19:15:56 +01:00
NEZNAMY 703f2ff316 Fix potentially wrong uuid write 2023-12-01 09:35:47 +01:00
NEZNAMY 34a2bbdbae Simplify 2023-12-01 09:34:14 +01:00
NEZNAMY cd8697c1a4 Add exclusions for some dependencies 2023-11-30 19:15:20 +01:00
NEZNAMY ed2dc2dd51 Avoid rare error on Velocity 2023-11-30 18:50:53 +01:00
NEZNAMY 2dfdc1b993 Update Fabric to 1.20.3-rc1 2023-11-30 18:27:49 +01:00
NEZNAMY 6f2d7f1194 Fix 1.20.3 client crash on BungeeCord 2023-11-30 17:52:01 +01:00
NEZNAMY 30009ef127 Attempt to fix error (#1146) 2023-11-29 17:19:11 +01:00
NEZNAMY 051a422331 Update Fabric to 1.20.3-pre4 2023-11-29 16:56:35 +01:00
NEZNAMY fab6489e9c Fix compilation 2023-11-28 16:12:27 +01:00
NEZNAMY a448614e4a Slightly better organize method 2023-11-27 18:54:28 +01:00
NEZNAMY 84ad874b2a Split complex method into 2 2023-11-27 18:43:43 +01:00
NEZNAMY 19ba978291 Simplify 2023-11-27 18:16:48 +01:00
NEZNAMY e304469e55 Remove star import 2023-11-27 18:10:28 +01:00
NEZNAMY 7ce9e2592a Update README.md 2023-11-27 18:07:35 +01:00
NEZNAMY 681b812c7b Rename feature in /tab cpu output 2023-11-26 13:59:29 +01:00
NEZNAMY ee1ab6785d Rename objective 2023-11-26 12:36:12 +01:00
NEZNAMY 57704b6622 Move a few classes around 2023-11-26 11:15:23 +01:00
NEZNAMY 7fe804e0fc Fix disconnect on bungeecord on 1.20.3 due to null text in component 2023-11-25 22:24:02 +01:00
NEZNAMY a71529b641 Simplify 2023-11-25 18:07:47 +01:00
NEZNAMY 4055173bdb Replace .length() == 0 with .isEmpty() 2023-11-25 17:09:20 +01:00
NEZNAMY 3f8841a276 Update to latest BungeeCord (for the 3rd time in a row) 2023-11-25 17:04:34 +01:00
NEZNAMY 15c38851bc Add misconfiguration check and prevent startup error 2023-11-25 16:56:11 +01:00
NEZNAMY 05030ae817 Update outdated message 2023-11-25 13:05:57 +01:00
NEZNAMY 255fa11371 Fix NPE 2023-11-24 18:41:08 +01:00
NEZNAMY a074c0bf74 Simplify 2023-11-24 10:49:12 +01:00
NEZNAMY 6ccd649fc4 Remove unused imports 2023-11-24 10:46:59 +01:00
NEZNAMY 21dbea07dd Add support for nested relational placeholders (#1153) 2023-11-23 20:28:21 +01:00
NEZNAMY 4c21d9d127 Remove unused import 2023-11-23 20:27:16 +01:00
NEZNAMY 766002b079 Delete aligned suffix feature, since 1.20.3 now has it naturally using playerlist objective (rip 1.20.2-) 2023-11-23 19:39:47 +01:00
NEZNAMY 2ce430ec41 Add support for all Fabric versions in a single jar 2023-11-23 19:18:31 +01:00
NEZNAMY cf2b151045 Bump Velocity version and use new method 2023-11-19 16:57:22 +01:00
NEZNAMY 92216179df Replace try/catch with SneakyThrows annotation 2023-11-19 16:27:06 +01:00
NEZNAMY a2a95b8591 Replace scoreboard enums with fields 2023-11-19 10:50:54 +01:00
NEZNAMY 4a22dc111a Fix attempt to send header/footer to 1.7 players on bungeecord, causing disconnect 2023-11-19 10:16:27 +01:00
NEZNAMY 5df7ecc245 Fix compatibility error message 2023-11-18 20:49:24 +01:00
NEZNAMY d4ef520f26 Print required bridge version in debug command if not connected 2023-11-18 20:39:09 +01:00
NEZNAMY e0f8737865 Use direct code for bungeecord instead of reflection and drop support for old versions 2023-11-18 20:37:37 +01:00
NEZNAMY 37b13a54a7 Create classes for plugin message types instead of writing all content on each usage + bump bridge version to 5 due to braking changes 2023-11-18 14:44:22 +01:00
NEZNAMY d9d28d31d0 Prepare for new 1.20.3 scoreboard features 2023-11-17 20:21:17 +01:00
NEZNAMY 66bf5da3f5 Delete CustomLine class due to not being viable 2023-11-17 11:41:11 +01:00
NEZNAMY 840c721afc Simplify, the performance gain was pretty much zero 2023-11-17 10:59:28 +01:00
NEZNAMY 859e705a48 Drop multi-version code for fabric (hard to maintain + a lot of it didn't actually work) 2023-11-16 22:10:45 +01:00
NEZNAMY 7375070b21 oops (#1144) 2023-11-15 16:53:42 +01:00
NEZNAMY 3e2eda6a71 Add ClickEvent to components 2023-11-11 20:17:22 +01:00
NEZNAMY 798d327dcb Fix plugin not removing redundant character width overrides from config 2023-11-11 18:11:37 +01:00
NEZNAMY f5ca9139e6 Fix double bossbar on Velocity on server switch (#1139) 2023-11-11 14:02:07 +01:00
NEZNAMY f35e8cfa51 Improve vanish tracking for nametag feature 2023-11-10 21:31:21 +01:00
NEZNAMY 0f6953711b Update build.yml 2023-11-10 15:57:34 +01:00
NEZNAMY e2c5e6f9ac Allow empty text for fixed slots in layout 2023-11-10 15:46:26 +01:00
NEZNAMY 5ef5d6ddba Update README.md 2023-11-10 12:51:41 +01:00
NEZNAMY 5b19a5d798 Only unregister team for other players when player is vanishing 2023-11-10 11:02:35 +01:00
NEZNAMY bf804e8923 Merge pull request #1137 from NEZNAMY/vanish-patch
Alternate implementation of #1136
2023-11-09 18:53:45 +01:00
NEZNAMY f47b8b5528 Alternate implementation of #1136 2023-11-09 18:30:56 +01:00
NEZNAMY c353f6787a Fix endsWith condition (#1134) 2023-11-07 17:37:52 +01:00
NEZNAMY 1b7d5c1a5c Add support for latest BungeeCord (yes, again), as well as some previous versions and remove compatibility check. 2023-11-06 17:12:26 +01:00
NEZNAMY 956456f157 4.1.0-SNAPSHOT 2023-11-06 13:41:51 +01:00
NEZNAMY 49077bd798 Create enum constants for packet actions for better readability and understanding of code 2023-11-05 21:16:07 +01:00
NEZNAMY 019a0287b2 4.0.9 2023-11-05 14:27:55 +01:00
NEZNAMY a71dac28c0 Hotfix after adding support for latest bungeecord (#1132) 2023-11-05 14:21:25 +01:00
NEZNAMY 155886e000 Fix typo 2023-11-04 15:39:45 +01:00
NEZNAMY ed4c303d2e 4.0.8 2023-11-04 14:55:32 +01:00
NEZNAMY 9da0db5f74 Disable creation of blank issues 2023-11-04 11:55:58 +01:00
NEZNAMY 562a973c84 Create new issue template 2023-11-04 11:53:10 +01:00
NEZNAMY 967980ddf7 Update bug_report.yml 2023-11-03 21:27:24 +01:00
NEZNAMY 81d3675a87 Check for bundle packet on 1.19.4+ when reading entity packets for unlimited nametags 2023-11-03 21:25:21 +01:00
NEZNAMY 7be786bbaf Fix vanish change being called on join even if vanish status did not change 2023-11-02 17:24:02 +01:00
NEZNAMY f41c9be1f7 Fix race condition on Velocity causing errors on shutdown 2023-11-02 17:21:48 +01:00
NEZNAMY e88062d028 Fix unvanish method not being called on server switch when clearing vanish status 2023-11-02 16:08:15 +01:00
NEZNAMY edb2828643 Fix wrong java instruction 2023-11-02 15:58:34 +01:00
NEZNAMY 58c189cd0e Catch all exceptions/errors on load/unload to prevent players from getting kicked on bungeecord if something changes again in the future 2023-10-31 16:41:29 +01:00
NEZNAMY 012994847e Unregister teams of players who vanished for those who cannot see vanished players to prevent 3rd party clients from revealing vanished players by finding out who is vanished and who actually left. 2023-10-30 20:39:21 +01:00
NEZNAMY d87801be5f Add support for latest BungeeCord (#1123) 2023-10-30 16:02:31 +01:00
NEZNAMY 6d78c0c104 Create a class storing values of minecraft limitations 2023-10-29 15:25:00 +01:00
NEZNAMY 3a83519122 Fix tablist entry related issues on both BungeeCord and Velocity (each one was affected by a different issue) 2023-10-27 22:27:42 +02:00
NEZNAMY a9f940ebed Avoid attempts to send packets to offline players on bungeecord 2023-10-27 19:17:56 +02:00
NEZNAMY db73d9ef81 Simplify 2023-10-24 17:46:25 +02:00
NEZNAMY e2bd21c717 Cache adventure components & use Paper API for header/footer by default. This has reduced CPU usage of the feature by 50% in the test I have done. 2023-10-24 17:40:22 +02:00
NEZNAMY 871215b7b5 Do not attempt to send scoreboard-related packets between login packet send and process 2023-10-24 15:18:17 +02:00
NEZNAMY a6153ef46f Force-fix NPE that doesn't make sense 2023-10-24 13:42:55 +02:00
NEZNAMY 35475a6103 Fix the previous error on Fabric & Sponge as well 2023-10-24 13:42:35 +02:00
NEZNAMY bf77ffc1a5 Fix error (#1117) 2023-10-22 23:01:53 +02:00
NEZNAMY 7a64a9a534 Code style 2023-10-22 20:08:27 +02:00
NEZNAMY 68e057b99c Fabric - Implement world name recognition and everything related to it (#1115) 2023-10-22 17:44:34 +02:00
NEZNAMY 7338afff96 Fix error when using 3 years old version of PlaceholderAPI 2023-10-21 11:27:38 +02:00
NEZNAMY 97ae7813aa Add vanish status updating to RedisBungee (#1109) 2023-10-18 16:48:01 +02:00
NEZNAMY c0f3667ac1 4.0.8-SNAPSHOT 2023-10-18 16:47:37 +02:00
729 changed files with 47067 additions and 85486 deletions

No files matched your search

+1
View File
@@ -5,3 +5,4 @@ gradlew text eol=lf
*.bat text eol=crlf
*.jar binary
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -0,0 +1,3 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
-63
View File
@@ -1,63 +0,0 @@
name: Bug Report
description: Plugin does not work as described.
labels: ["Type: Bug"]
body:
- type: input
attributes:
label: Server version
description: Version and type of used server software where TAB is installed.
placeholder: Spigot 1.18.1
validations:
required: true
- type: input
attributes:
label: TAB version
description: Full version in numbers, "latest" is not a version.
placeholder: 3.0.2
validations:
required: true
- type: textarea
attributes:
label: Describe the bug
description: How the plugin behaves
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: How the plugin is supposed to behave instead with your configuration.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: What to do in order to reproduce this issue
placeholder: |
Step 1: ..
Step 2: ..
validations:
required: true
- type: textarea
attributes:
label: Additional info
description: Anything else you want to provide that will help in resolving the issue faster, such as screenshots or videos.
validations:
required: false
- type: checkboxes
attributes:
label: Checklist
description: Let's make sure this report is valid
options:
- label: I am running latest version of the plugin
required: true
- label: I have read the wiki to make sure it's not an issue with configuration
required: true
- label: I 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
-57
View File
@@ -1,57 +0,0 @@
name: Error
description: Error message with a stack trace in console or error file.
labels: ["Type: Error"]
body:
- type: input
attributes:
label: Server version
description: Version and type of used server software where TAB is installed.
placeholder: Spigot 1.18.1
validations:
required: true
- type: input
attributes:
label: TAB version
description: Full version in numbers, "latest" is not a version.
placeholder: 3.0.2
validations:
required: true
- type: textarea
attributes:
label: Stack trace
description: The error message and stack trace you received in console or errors.log file.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce (if known)
description: What to do in order to reproduce this issue
placeholder: |
Step 1: ..
Step 2: ..
validations:
required: false
- type: textarea
attributes:
label: Additional info
description: Any other info you want to provide.
validations:
required: false
- type: checkboxes
attributes:
label: Checklist
description: Let's make sure this report is valid
options:
- label: I am running latest version of the plugin
required: true
- label: I have included a paste of the error
required: true
- label: I 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
-56
View File
@@ -1,56 +0,0 @@
name: Memory leak
description: Memory leak caused by TAB.
labels: ["Type: Memory leak"]
body:
- type: input
attributes:
label: Server version
description: Version and type of used server software where TAB is installed.
placeholder: Spigot 1.18.1
validations:
required: true
- type: input
attributes:
label: TAB version
description: Full version in numbers, "latest" is not a version.
placeholder: 3.0.2
validations:
required: true
- type: textarea
attributes:
label: Heap dump
description: Link to uploaded memory dump which I can look into.
validations:
required: true
- type: textarea
attributes:
label: Uncleared object(s)
description: Objects you believe should have been removed from the memory but are not
placeholder: |
There is only 1 player online, but heap dump has 100 instances of TabPlayer
validations:
required: false
- type: textarea
attributes:
label: Additional info
description: Any other info you want to provide.
validations:
required: false
- type: checkboxes
attributes:
label: Checklist
description: Let's make sure this report is valid
options:
- label: I am running latest version of the plugin
required: true
- label: I have included download link of my heap dump
required: true
- label: I ticked all of the boxes without actually reading them
required: false
- label: I have explained what I believe is wrong with the heap dump
required: true
@@ -1,55 +0,0 @@
name: Performance issue
description: Performance issue in TAB or in server caused by TAB
labels: ["Type: Performance"]
body:
- type: input
attributes:
label: Server version
description: Version and type of used server software where TAB is installed.
placeholder: Spigot 1.18.1
validations:
required: true
- type: input
attributes:
label: TAB version
description: Full version in numbers, "latest" is not a version.
placeholder: 3.0.2
validations:
required: true
- type: textarea
attributes:
label: Evidence
description: Any evidence proving the performance issue, such as output of /tab cpu or timings.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce (if known)
description: What to do in order to reproduce this issue
placeholder: |
Step 1: ..
Step 2: ..
validations:
required: false
- type: textarea
attributes:
label: Additional info
description: Any other info you want to provide.
validations:
required: false
- type: checkboxes
attributes:
label: Checklist
description: Let's make sure this report is valid
options:
- label: I am running latest version of the plugin
required: true
- label: I have included sufficient evidence of the issue
required: true
- label: I ticked all of the boxes without actually reading them
required: false
-29
View File
@@ -1,29 +0,0 @@
name: Wiki change
description: Change to text on a wiki page or adding a new section.
labels: ["Type: Wiki change"]
body:
- type: input
attributes:
label: Page link
description: Link to the page that should receive the change.
placeholder: https://github.com/NEZNAMY/TAB/wiki/Installation
validations:
required: true
- type: textarea
attributes:
label: Current text (if applicable)
description: Current version of the text (if modifying), ignore in case of new section.
placeholder: |
Installation on Bukkit/Spigot/Paper is as simple as it can be. Just put the plugin into plugins folder and restart the server. You can also run `/plugman load tab` to load the plugin while server is running.
validations:
required: false
- type: textarea
attributes:
label: New text
description: What the text should be changed to / how should new section look
placeholder: |
Put the plugin into plugins folder and restart the server. In case you don't want to restart your server, loading with plugins like PlugMan is supported.
validations:
required: true
-45
View File
@@ -1,45 +0,0 @@
name: Build
on:
push:
branches:
- master
- v4
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- 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 17
uses: actions/setup-java@v3.9.0 #https://github.com/actions/setup-java/releases
with:
distribution: 'temurin'
java-version: 17
- name: Cache SonarCloud packages
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@v3.2.3 #https://github.com/actions/cache/releases
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: "${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}"
restore-keys: |
${{ runner.os }}-gradle-
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
run: ./gradlew clean build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v3.1.2 #https://github.com/actions/upload-artifact/releases
with:
name: Jar
path: jar/build/libs/TAB-**.jar
+4 -1
View File
@@ -6,4 +6,7 @@ is in quality of the report (typically providing correct steps to reproduce that
* 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.
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples.
* Create spark reports and then send them to me. I will analyze them and try to find optimizations to make the plugin run faster. Commands:
* `/spark profiler start --timeout 300 --thread * --interval 0.1`
* `/spark profiler start --timeout 300 --thread * --interval 0.1 --alloc`
+24 -20
View File
@@ -1,23 +1,27 @@
# About
TAB aims to be a superior all-in-one minecraft plugin for displaying information that outperforms all
similar plugins in terms of features, performance and compatibility.
More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/Why-TAB%3F) wiki page.
# TAB-OG
# Compiling
Compilation requires JDK 17 and up.
To compile the plugin, run `./gradlew build` from the terminal.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
The universal jar contains all modules for all supported platforms.
TAB-OG is a soft fork of [TAB](https://github.com/NEZNAMY/TAB) for
[TrueOG Network](https://true-og.net/), based on upstream commit `93cfd74`.
# Documentation
You can find everything about the plugin on the [Wiki](https://github.com/NEZNAMY/TAB/wiki). This includes a detailed description
of all features, as well as information regarding compatibility or limitations of each feature.
Differences from upstream TAB:
- Versioned `5.9.9` (upstream is on `6.x`), targeting Purpur 1.19.4 serving 1.8–1.21.11
clients through ViaVersion, ViaBackwards, and ViaRewind.
- Builds only Bukkit `v1_19_R3`, BungeeCord, and Velocity; drops Fabric, Forge, NeoForge,
and the unused Bukkit/Paper NMS modules.
- Uses LuckPerms `5.5` (Vault removed) with a GraalVM JDK 17 toolchain, producing one
universal shaded JAR.
- TrueOG compatibility fixes: [Vanish-OG](https://git.trueog.net/trueog/Vanish-OG), the
[AFK-OG](https://git.trueog.net/trueog/AFK-OG) indicator, legacy scoreboard team colors,
and disabled scoreboard number formats.
- Legacy (1.8–1.12.2) nametag rendering: over-head names drop the rank brackets (the
tablist keeps them), sit one space after the rank in its color, bold the AFK indicator
for every client, and fit within the 16-character team prefix/suffix limits. A bold
rank is kept only when the whole tag can render bold — legacy clients width-count
everything after `§l` as bold and never receive a `§r`, so a plain-rendered name after
a bold rank pads the nametag end with phantom pixels; when it cannot fit, bold is
stripped over-head. Keeping bold requires `add-teamcolor-to-prefix: false` in the
ViaBackwards config, otherwise the appended team color un-bolds the name again.
- Ships live TrueOG Network config values.
# Issues
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is an issue tracker for all the kind of problems covered in issue templates.
Please refrain from attempting to use it to receive help with your server, such issues will be closed immediately.
When opening an issue, provide as much information as possible. The difference between a bug getting fixed in 1 week vs 1 hour
is in quality of the report. Bug reports where steps to reproduce are "idk" have no value and will not help to get the bug fixed at all.
# Contributing
See [CONTRIBUTING.md](https://github.com/NEZNAMY/TAB/blob/master/CONTRIBUTING.md)
Build with `./gradlew build` (JAR in `jar/build/libs/`). Requires LuckPerms; bold legacy
nametags additionally require ViaBackwards with `add-teamcolor-to-prefix: false`.
+1 -1
View File
@@ -1,5 +1,5 @@
dependencies {
compileOnlyApi("org.jetbrains:annotations:24.0.1")
compileOnlyApi("org.jetbrains:annotations:26.0.2")
}
tasks.javadoc {
@@ -1,5 +1,6 @@
package me.neznamy.tab.api;
import lombok.NonNull;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -8,6 +9,7 @@ import java.util.UUID;
/**
* An interface representing a player
*/
@SuppressWarnings("unused") // API class
public interface TabPlayer {
/**
@@ -15,21 +17,24 @@ public interface TabPlayer {
*
* @return Player's name
*/
@NotNull String getName();
@NotNull
String getName();
/**
* Returns player's UUID
*
* @return Player's UUID
*/
@NotNull UUID getUniqueId();
@NotNull
UUID getUniqueId();
/**
* Returns platform-specific entity
*
* @return an instance of bukkit/bungee player depending on platform
* @return platform's player object
*/
@NotNull Object getPlayer();
@NotNull
Object getPlayer();
/**
* Returns true once the player is successfully loaded (onJoin method ran through all methods)
@@ -45,7 +50,8 @@ public interface TabPlayer {
*
* @return player's primary permission group
*/
@NotNull String getGroup();
@NotNull
String getGroup();
/**
* Temporarily overrides player's group and applies all changes coming from new group.
@@ -66,4 +72,54 @@ public interface TabPlayer {
* @see #setTemporaryGroup(String)
*/
boolean hasTemporaryGroup();
/**
* Changes expected profile name of the player. This adapts all name-bound features
* to use this new profile name if another plugin changed profile name of the player. <p>
* Automatic profile name change detection is available on Bukkit, BungeeCord and Fabric,
* therefore using this is redundant there. This function is only needed on Velocity,
* where the detection is not available. <p>
* Warning: This function does NOT change player's profile name, it only updates the tracked
* name inside the plugin.
*
* @param profileName
* New expected profile name
*/
void setExpectedProfileName(@NonNull String profileName);
/**
* Returns player's expected profile name. This defaults to player's username, but may
* get changed by other plugins and then detected by TAB.
*
* @return Player's expected profile name
*/
@NotNull
String getExpectedProfileName();
/**
* Returns name of the server the player is connected to.
* This requires TAB on proxy.
* On backend installation, this method returns {@code "N/A"}.
*
* @return Player's server on proxy or {@code "N/A"} on backend installation
*/
@NotNull
String getServer();
/**
* Returns name of the world the player is in.
* On proxy installation, TAB-Bridge must be installed to forward this info to the proxy.
* Otherwise, it will return {@code "N/A"}.
*
* @return Player's world or {@code "N/A"} on proxy installation without TAB-Bridge installed
*/
@NotNull
String getWorld();
/**
* Returns {@code true} if this player is a bedrock player, {@code false} if not.
*
* @return {@code true} if this player is a bedrock player, {@code false} if not
*/
boolean isBedrockPlayer();
}
@@ -4,13 +4,27 @@ package me.neznamy.tab.api.bossbar;
* An enum representing all available BossBar colors.
* Calling ordinal() will return color's network ID.
*/
@SuppressWarnings("unused") // API class
public enum BarColor {
/** Pink color */
PINK,
/** Blue color */
BLUE,
/** Red color */
RED,
/** Green color */
GREEN,
/** Yellow color */
YELLOW,
/** Purple color */
PURPLE,
/** White color */
WHITE
}
@@ -4,11 +4,21 @@ package me.neznamy.tab.api.bossbar;
* An enum representing all available BossBar styles.
* Calling ordinal() will return style's network ID.
*/
@SuppressWarnings("unused") // API class
public enum BarStyle {
/** Fully solid line */
PROGRESS,
/** Line segmented into 6 parts */
NOTCHED_6,
/** Line segmented into 10 parts */
NOTCHED_10,
/** Line segmented into 12 parts */
NOTCHED_12,
/** Line segmented into 20 parts */
NOTCHED_20
}
@@ -10,11 +10,7 @@ import org.jetbrains.annotations.NotNull;
/**
* An interface representing a BossBar line.
* <p>
* For clients 1.8 and lower it uses wither, which only allows 1 bar
* to be displayed at a time. Entity packets are not available on BungeeCord,
* where nothing will be displayed for these players.
* <p>
* For 1.9+ it uses the new BossBar feature, allowing configurable styles
* It uses 1.9+ BossBar feature, allowing configurable styles
* and colors, as well as display several bars at once. Limit of maximum
* displayed BossBars depends on client resolution and GUI scale, up to
* 9 concurrent BossBars displayed at once.
@@ -205,7 +201,7 @@ public interface BossBar {
* Player to check
* @return {@code true} if player is allowed to see this Boss Bar, {@code false} if not.
*/
boolean containsPlayer(TabPlayer player);
boolean containsPlayer(@NonNull TabPlayer player);
/**
* Returns {@code true} if this boss bar is configured as announcement-only bar,
@@ -1,14 +1,14 @@
package me.neznamy.tab.api.bossbar;
import java.util.List;
import java.util.Map;
import lombok.NonNull;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
/**
* An interface allowing work with BossBars such as creating, sending
* and toggling.
@@ -17,6 +17,7 @@ import org.jetbrains.annotations.Nullable;
* This requires the BossBar feature to be enabled in config, otherwise the method will
* return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface BossBarManager {
/**
@@ -77,6 +78,28 @@ public interface BossBarManager {
*/
@NotNull Map<String, BossBar> getRegisteredBossBars();
/**
* Removes BossBar with specified name from the manager, making it no longer available.
* If BossBar was displayed to any players, it is removed.
*
* @param name
* Name of registered BossBar to remove
* @throws IllegalArgumentException
* if no BossBar with specified name exists
*/
void removeBossBar(@NonNull String name);
/**
* Removes BossBar from the manager, making it no longer available.
* If BossBar was displayed to any players, it is removed.
*
* @param bossBar
* BossBar to remove
* @throws IllegalArgumentException
* if specified BossBar is not registered
*/
void removeBossBar(@NonNull BossBar bossBar);
/**
* Toggles BossBar for this player and sends toggle message if {@code sendToggleMessage} is true.
*
@@ -116,14 +139,14 @@ public interface BossBarManager {
void setBossBarVisible(@NonNull TabPlayer player, boolean visible, boolean sendToggleMessage);
/**
* Temporarily displays registered BossBar to player for specified amount of time in milliseconds.
* Temporarily displays registered BossBar to player for specified amount of time in seconds.
*
* @param player
* Player to show BossBar to
* @param bossBar
* Name of registered BossBar to show
* @param duration
* In milliseconds for how long should BossBar be displayed
* In seconds for how long should BossBar be displayed
* @throws IllegalArgumentException
* if no BossBar with specified name exists
* @see #announceBossBar(String, int)
@@ -132,12 +155,12 @@ public interface BossBarManager {
void sendBossBarTemporarily(@NonNull TabPlayer player, @NonNull String bossBar, int duration);
/**
* Temporarily displays registered BossBar to all players for specified amount of time in milliseconds.
* Temporarily displays registered BossBar to all players for specified amount of time in seconds.
*
* @param bossBar
* Name of registered BossBar to show
* @param duration
* In milliseconds for how long should BossBar be displayed
* In seconds for how long should BossBar be displayed
* @throws IllegalArgumentException
* if no BossBar with specified name exists
* @see #sendBossBarTemporarily(TabPlayer, String, int)
@@ -6,6 +6,7 @@ import lombok.NonNull;
* The event bus. This is used to register event listeners/handlers for
* handling events that may be fired by TAB.
*/
@SuppressWarnings("unused") // API class
public interface EventBus {
/**
@@ -1,48 +0,0 @@
package me.neznamy.tab.api.event.plugin;
import lombok.NonNull;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.event.TabEvent;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Called when an unknown placeholder is about to be registered.
* It defaults to a PlaceholderAPI implementation, but can be overridden.
*/
public interface PlaceholderRegisterEvent extends TabEvent {
/**
* Returns placeholder identifier starting and ending with {@code %}
*
* @return placeholder identifier starting and ending with {@code %}
*/
@NotNull String getIdentifier();
/**
* Sets placeholder to specified server placeholder implementation.
*
* @param supplier
* Placeholder replacer
*/
void setServerPlaceholder(@NonNull Supplier<Object> supplier);
/**
* Sets placeholder to specified player placeholder implementation.
*
* @param function
* Placeholder replacer
*/
void setPlayerPlaceholder(@NonNull Function<TabPlayer, Object> function);
/**
* Sets placeholder to specified relational placeholder implementation.
*
* @param function
* Placeholder replacer
*/
void setRelationalPlaceholder(@NonNull BiFunction<TabPlayer, TabPlayer, Object> function);
}
@@ -0,0 +1,85 @@
package me.neznamy.tab.api.integration;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
@Getter
@RequiredArgsConstructor
public abstract class VanishIntegration {
/** List of all registered VanishIntegration handlers */
private static final List<VanishIntegration> HANDLERS = new ArrayList<>();
/** Name of the plugin this integration is associated with */
@NotNull
private final String plugin;
/**
* Determines if a viewer can see a target player.
*
* @param viewer
* The player attempting to view another player
* @param target
* The player being viewed
* @return {@code true} if the viewer can see the target, {@code false} otherwise
*/
public abstract boolean canSee(@NotNull TabPlayer viewer, @NotNull TabPlayer target);
/**
* Checks if the specified player is in a vanished state.
*
* @param player
* The player to check
* @return {@code true} if the player is vanished, {@code false} otherwise
*/
public abstract boolean isVanished(@NotNull TabPlayer player);
/**
* Registers this integration handler to the global list of handlers.
*/
public void register() {
registerHandler(this);
}
/**
* Unregisters this integration handler from the global list of handlers.
*/
public void unregister() {
unregisterHandler(this);
}
/**
* Adds a VanishIntegration handler to the global list of handlers.
*
* @param handler
* The handler to register
*/
public static void registerHandler(@NotNull VanishIntegration handler) {
HANDLERS.add(handler);
}
/**
* Removes a VanishIntegration handler from the global list of handlers.
*
* @param handler
* The handler to unregister
*/
public static void unregisterHandler(@NotNull VanishIntegration handler) {
HANDLERS.remove(handler);
}
/**
* Retrieves the list of all registered VanishIntegration handlers.
*
* @return A list of registered VanishIntegration handlers
*/
@NotNull
public static List<VanishIntegration> getHandlers() {
return HANDLERS;
}
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.api.nametag;
import lombok.NonNull;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -12,6 +13,7 @@ import org.jetbrains.annotations.Nullable;
* This requires the Team feature to be enabled in config, otherwise the method will
* return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface NameTagManager {
/**
@@ -132,7 +134,7 @@ public interface NameTagManager {
* @param prefix
* new prefix value
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
* @see #getOriginalRawPrefix(TabPlayer)
*/
void setPrefix(@NonNull TabPlayer player, @Nullable String prefix);
@@ -146,30 +148,134 @@ public interface NameTagManager {
* @param suffix
* new suffix value
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
* @see #getOriginalRawSuffix(TabPlayer)
*/
void setSuffix(@NonNull TabPlayer player, @Nullable String suffix);
@Nullable String getCustomPrefix(@NonNull TabPlayer player);
/**
* Returns custom prefix assigned using {@link #setPrefix(TabPlayer, String)}.
* If no custom prefix is set, returns {@code null}.
*
* @param player
* Player to get custom prefix of
* @return Custom prefix assigned using the API.
* @see #setPrefix(TabPlayer, String)
* @see #getOriginalRawPrefix(TabPlayer)
*/
@Nullable
String getCustomPrefix(@NonNull TabPlayer player);
@Nullable String getCustomSuffix(@NonNull TabPlayer player);
/**
* Returns custom suffix assigned using {@link #setSuffix(TabPlayer, String)}.
* If no custom suffix is set, returns {@code null}.
*
* @param player
* Player to get custom suffix of
* @return Custom suffix assigned using the API.
* @see #setSuffix(TabPlayer, String)
* @see #getOriginalRawSuffix(TabPlayer)
*/
@Nullable
String getCustomSuffix(@NonNull TabPlayer player);
@NonNull String getOriginalPrefix(@NonNull TabPlayer player);
/**
* Returns original prefix assigned by the plugin using internal logic.
* @param player
* Player to get original prefix of
* @return Original prefix assigned by the plugin's internal logic.
* @see #getCustomPrefix(TabPlayer)
*/
@NotNull
String getOriginalRawPrefix(@NonNull TabPlayer player);
@NonNull String getOriginalSuffix(@NonNull TabPlayer player);
/**
* Returns original suffix assigned by the plugin using internal logic.
* @param player
* Player to get original suffix of
* @return Original suffix assigned by the plugin's internal logic.
* @see #getCustomSuffix(TabPlayer)
*/
@NotNull
String getOriginalRawSuffix(@NonNull TabPlayer player);
/**
* Returns original prefix assigned by the plugin using internal logic with all placeholders replaced.
* @param player
* Player to get replaced original prefix of
* @return Original prefix assigned by the plugin's internal logic.
* @see #getCustomPrefix(TabPlayer)
*/
@NotNull
String getOriginalReplacedPrefix(@NonNull TabPlayer player);
/**
* Returns original suffix assigned by the plugin using internal logic with all placeholders replaced.
* @param player
* Player to get replaced original suffix of
* @return Original suffix assigned by the plugin's internal logic.
* @see #getCustomSuffix(TabPlayer)
*/
@NotNull
String getOriginalReplacedSuffix(@NonNull TabPlayer player);
/**
* Returns original suffix assigned by the plugin using internal logic.
* @param player
* Player to get replaced original prefix of
* @return Original prefix assigned by the plugin's internal logic.
* @see #getCustomPrefix(TabPlayer)
* @deprecated Use {@link #getOriginalRawPrefix(TabPlayer)} instead.
*/
@Deprecated
@NotNull
String getOriginalPrefix(@NonNull TabPlayer player);
/**
* Returns original suffix assigned by the plugin using internal logic.
* @param player
* Player to get replaced original suffix of
* @return Original suffix assigned by the plugin's internal logic.
* @see #getCustomSuffix(TabPlayer)
* @deprecated Use {@link #getOriginalRawSuffix(TabPlayer)} instead.
*/
@Deprecated
@NotNull
String getOriginalSuffix(@NonNull TabPlayer player);
/**
* Toggles name tag visibility view on all players for specified player.
* On first call, name tags of all players will become invisible for specified
* player. On second call, they will become visible again.
* player. On the second call, they will become visible again.
*
* @param player
* player to toggle name tag visibility view for
* @param sendToggleMessage
* {@code true} if configured toggle message should be sent, {@code false} if not
* {@code true} if the configured toggle message should be sent, {@code false} if not
*/
void toggleNameTagVisibilityView(@NonNull TabPlayer player, boolean sendToggleMessage);
/**
* Shows back name tag visibility view on all players for specified player.
* If the view is not hidden, nothing happens.
*
* @param player
* player to show name tag visibility view for
* @param sendToggleMessage
* {@code true} if the configured toggle message should be sent, {@code false} if not
*/
void showNameTagVisibilityView(@NonNull TabPlayer player, boolean sendToggleMessage);
/**
* Hides name tag visibility view on all players for specified player.
* If the view is already hidden, nothing happens.
*
* @param player
* player to show name tag visibility view for
* @param sendToggleMessage
* {@code true} if the configured toggle message should be sent, {@code false} if not
*/
void hideNameTagVisibilityView(@NonNull TabPlayer player, boolean sendToggleMessage);
/**
* Returns {@code true} if player has hidden name tags by either calling
* {@link #toggleNameTagVisibilityView(TabPlayer, boolean)} or using a command,
@@ -1,128 +0,0 @@
package me.neznamy.tab.api.nametag;
import java.util.List;
import lombok.NonNull;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface for manipulating player name tags.
* <p>
* Instance can be obtained using {@link TabAPI#getNameTagManager()} and then casting it.
* This requires both Team and Unlimited name tag features to be enabled in config, otherwise the method will
* return {@code null} or not be an instance of this class.
*/
public interface UnlimitedNameTagManager extends NameTagManager {
/**
* Disables armor stands of specified player and displays vanilla name tags instead.
*
* @param player
* Player to disable armor stands of
* @see #enableArmorStands(TabPlayer)
* @see #hasDisabledArmorStands(TabPlayer)
*/
void disableArmorStands(@NonNull TabPlayer player);
/**
* Enabled back armor stands of player, who was disabled previously using
* {@link #disableArmorStands(TabPlayer)}.
*
* @param player
* Player to enable back
* @see #disableArmorStands(TabPlayer)
* @see #hasDisabledArmorStands(TabPlayer)
*/
void enableArmorStands(@NonNull TabPlayer player);
/**
* Returns {@code true} if player has armor stands disabled using
* {@link #disableArmorStands(TabPlayer)}, {@code false} otherwise.
*
* @param player
* Player to check
* @return {@code true} if disabled, {@code false} if not.
* @see #disableArmorStands(TabPlayer)
* @see #enableArmorStands(TabPlayer)
*/
boolean hasDisabledArmorStands(@NonNull TabPlayer player);
/**
* Changes name to specified value. Set to {@code null} to
* reset back to original value.
*
* @param player
* Player to change name of
* @param customName
* New name, or {@code null} to reset to original value
*/
void setName(@NonNull TabPlayer player, @Nullable String customName);
/**
* Changes line to specified value. Set to {@code null} to reset back to original value.
*
* @param player
* Player to change line of
* @param line
* Name of defined line
* @param value
* Value to use, or {@code null} to reset back to original value
* @see #getDefinedLines()
*/
void setLine(@NonNull TabPlayer player, @NonNull String line, @Nullable String value);
/**
* Returns custom name value set using {@link #setName(TabPlayer, String)}.
*
* @param player
* Player to check
* @return Custom name set previously or {@code null} if not set
* @see #setName(TabPlayer, String)
*/
@Nullable String getCustomName(@NonNull TabPlayer player);
/**
* Returns custom line value set using {@link #setLine(TabPlayer, String, String)}.
*
* @param player
* Player to check
* @param line
* Defined line
* @return Custom name set previously or {@code null} if not set
* @see #setName(TabPlayer, String)
* @see #getDefinedLines()
*/
@Nullable String getCustomLineValue(@NonNull TabPlayer player, @NonNull String line);
/**
* Returns original name value configured in files.
*
* @param player
* Player to check
* @return Original configured name
* @see #setName(TabPlayer, String)
*/
@NotNull String getOriginalName(@NonNull TabPlayer player);
/**
* Returns original line value configured in files.
*
* @param player
* Player to check
* @param line
* Line name
* @return Original configured line
* @see #setLine(TabPlayer, String, String)
*/
@NotNull String getOriginalLineValue(@NonNull TabPlayer player, @NonNull String line);
/**
* Returns list of all defined lines in config, both static and dynamic lines.
*
* @return List of all defined lines in config
*/
@NotNull List<String> getDefinedLines();
}
@@ -8,11 +8,14 @@ import org.jetbrains.annotations.NotNull;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* An interface allowing placeholder registration. Instance can be
* obtained using {@link TabAPI#getPlaceholderManager()}
*/
@SuppressWarnings("unused") // API class
public interface PlaceholderManager {
/**
@@ -29,7 +32,7 @@ public interface PlaceholderManager {
* If {@code identifier} does not start and end with {@code %} or
* {@code refresh} is not divisible by 50
*/
@NotNull ServerPlaceholder registerServerPlaceholder(@NonNull String identifier, int refresh, @NonNull Supplier<Object> supplier);
@NotNull ServerPlaceholder registerServerPlaceholder(@NonNull String identifier, int refresh, @NonNull Supplier<String> supplier);
/**
* Registers a player placeholder (placeholder different output per player)
@@ -45,7 +48,7 @@ public interface PlaceholderManager {
* If {@code identifier} does not start and end with {@code %} or
* {@code refresh} is not divisible by 50
*/
@NotNull PlayerPlaceholder registerPlayerPlaceholder(@NonNull String identifier, int refresh, @NonNull Function<TabPlayer, Object> function);
@NotNull PlayerPlaceholder registerPlayerPlaceholder(@NonNull String identifier, int refresh, @NonNull Function<TabPlayer, String> function);
/**
* Registers a relational placeholder (placeholder with output different for each player duo)
@@ -62,7 +65,15 @@ public interface PlaceholderManager {
* does not start with {@code %rel_} or
* {@code refresh} is not divisible by 50
*/
@NotNull RelationalPlaceholder registerRelationalPlaceholder(@NonNull String identifier, int refresh, @NonNull BiFunction<TabPlayer, TabPlayer, Object> function);
@NotNull RelationalPlaceholder registerRelationalPlaceholder(@NonNull String identifier, int refresh, @NonNull BiFunction<TabPlayer, TabPlayer, String> function);
void registerServerPlaceholder(@NonNull Pattern identifier, int refresh, @NonNull Function<Matcher, Supplier<String>> function);
void registerPlayerPlaceholder(@NonNull Pattern identifier, int refresh,
@NonNull Function<Matcher, Function<me.neznamy.tab.api.TabPlayer, String>> function);
void registerRelationalPlaceholder(@NonNull Pattern identifier, int refresh,
@NonNull Function<Matcher, BiFunction<me.neznamy.tab.api.TabPlayer, me.neznamy.tab.api.TabPlayer, String>> function);
/**
* Returns placeholder from specified identifier. If it does not exist, it is registered
@@ -2,6 +2,7 @@ package me.neznamy.tab.api.placeholder;
import lombok.NonNull;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.Nullable;
/**
* Interface for player placeholders (placeholders returning different value for each player)
@@ -18,7 +19,7 @@ public interface PlayerPlaceholder extends Placeholder {
* @param value
* New value
*/
void updateValue(@NonNull TabPlayer player, @NonNull Object value);
void updateValue(@NonNull TabPlayer player, @Nullable String value);
/**
* Force-updates placeholder value using previously provided update function.
@@ -2,10 +2,12 @@ package me.neznamy.tab.api.placeholder;
import lombok.NonNull;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.Nullable;
/**
* An interface for relational placeholders (returning different value for each player duo)
*/
@SuppressWarnings("unused") // API class
public interface RelationalPlaceholder extends Placeholder {
/**
@@ -20,7 +22,7 @@ public interface RelationalPlaceholder extends Placeholder {
* @param value
* New value
*/
void updateValue(@NonNull TabPlayer viewer, @NonNull TabPlayer target, @NonNull Object value);
void updateValue(@NonNull TabPlayer viewer, @NonNull TabPlayer target, @Nullable String value);
/**
* Force-updates placeholder value using previously provided update function.
@@ -1,6 +1,6 @@
package me.neznamy.tab.api.placeholder;
import lombok.NonNull;
import org.jetbrains.annotations.Nullable;
/**
* An interface for server placeholders (returning same value for all players)
@@ -15,7 +15,7 @@ public interface ServerPlaceholder extends Placeholder {
* @param value
* New value
*/
void updateValue(@NonNull Object value);
void updateValue(@Nullable String value);
/**
* Force-updates placeholder value using previously provided update function.
@@ -11,12 +11,13 @@ import java.util.List;
* {@link ScoreboardManager#createScoreboard(String, String, List)},
* for scoreboards from config {@link ScoreboardManager#getRegisteredScoreboards()}.
*/
@SuppressWarnings("unused") // API class
public interface Scoreboard {
/**
* Returns internal name of this scoreboard. Name is defined in registration,
* scoreboards from config use name they were defined with. This value is
* used internally and in /tab announce scoreboard command.
* used internally and in /tab scoreboard announce command.
*
* @return custom name of this scoreboard
*/
@@ -83,8 +84,24 @@ public interface Scoreboard {
*/
void removeLine(int index);
/**
* Replaces all current lines with the provided list.
* Supports placeholders, which will automatically be refreshed.
* Client can only see up to 15 lines at a time.
* If the new list is identical to the current one, nothing happens.
*
* @param lines
* New list of lines to display
* @throws NullPointerException
* if {@code lines} is {@code null}
* @see #getLines()
* @see #addLine(String)
* @see #removeLine(int)
*/
void setLines(@NonNull List<String> lines);
/**
* Unregisters this scoreboard from all players who can see it.
*/
void unregister();
}
}
@@ -1,13 +1,13 @@
package me.neznamy.tab.api.scoreboard;
import java.util.List;
import java.util.Map;
import lombok.NonNull;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
/**
* Interface allowing work with scoreboards, such as creating,
* sending, toggling and modifying.
@@ -16,6 +16,7 @@ import org.jetbrains.annotations.Nullable;
* This requires the scoreboard feature to be enabled in config,
* the method will return {@code null} otherwise.
*/
@SuppressWarnings("unused") // API class
public interface ScoreboardManager {
/**
@@ -53,6 +54,29 @@ public interface ScoreboardManager {
* @return map of registered scoreboards
*/
@NotNull Map<String, Scoreboard> getRegisteredScoreboards();
/**
* Removes scoreboard with specified name from list of scoreboards. If it was displayed to anyone,
* it will be removed from their display as well. Then, those players will receive a new scoreboard based
* on configuration.
*
* @param name Name of scoreboard to remove
* @throws IllegalArgumentException
* if scoreboard with such name does not exist
*/
void removeScoreboard(@NonNull String name);
/**
* Removes scoreboard from list of scoreboards. If it was displayed to anyone,
* it will be removed from their display as well. Then, those players will receive a new scoreboard based
* on configuration.
*
* @param scoreboard
* Scoreboard to remove
* @throws IllegalArgumentException
* if specified scoreboard is not registered
*/
void removeScoreboard(@NonNull Scoreboard scoreboard);
/**
* Displays scoreboard to defined player. This will disable all display
@@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable;
* This requires the Header/footer feature to be enabled in config, otherwise
* the method will return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface HeaderFooterManager {
/**
@@ -1,10 +1,18 @@
package me.neznamy.tab.api.tablist;
import lombok.NonNull;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface for managing player sorting by directly configuring
* team names of players. Instance can be obtained using
* {@link TabAPI#getSortingManager()}. If the feature is disabled,
* it will return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface SortingManager {
/**
@@ -14,6 +14,7 @@ import org.jetbrains.annotations.Nullable;
* This requires the feature to be enabled in config, otherwise the method will
* return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface TabListFormatManager {
/**
@@ -26,7 +27,7 @@ public interface TabListFormatManager {
* @param prefix
* new prefix value
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
* @see #getOriginalRawPrefix(TabPlayer)
*/
void setPrefix(@NonNull TabPlayer player, @Nullable String prefix);
@@ -40,7 +41,7 @@ public interface TabListFormatManager {
* @param customName
* new customName value
* @see #getCustomName(TabPlayer)
* @see #getOriginalName(TabPlayer)
* @see #getOriginalRawName(TabPlayer)
*/
void setName(@NonNull TabPlayer player, @Nullable String customName);
@@ -54,7 +55,7 @@ public interface TabListFormatManager {
* @param suffix
* new suffix value
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
* @see #getOriginalRawSuffix(TabPlayer)
*/
void setSuffix(@NonNull TabPlayer player, @Nullable String suffix);
@@ -66,7 +67,7 @@ public interface TabListFormatManager {
* player to get custom prefix of
* @return player's custom prefix or {@code null} if not defined
* @see #setPrefix(TabPlayer, String)
* @see #getOriginalPrefix(TabPlayer)
* @see #getOriginalRawPrefix(TabPlayer)
*/
@Nullable String getCustomPrefix(@NonNull TabPlayer player);
@@ -103,8 +104,11 @@ public interface TabListFormatManager {
* @return player's original prefix
* @see #setPrefix(TabPlayer, String)
* @see #getCustomPrefix(TabPlayer)
* @deprecated Use {@link #getOriginalRawPrefix(TabPlayer)} instead.
*/
@NotNull String getOriginalPrefix(@NonNull TabPlayer player);
@NotNull
@Deprecated
String getOriginalPrefix(@NonNull TabPlayer player);
/**
* Returns player's original name applied using plugin's internal logic.
@@ -115,8 +119,11 @@ public interface TabListFormatManager {
* @return player's original name
* @see #setName(TabPlayer, String)
* @see #getCustomName(TabPlayer)
* @deprecated Use {@link #getOriginalRawName(TabPlayer)} instead.
*/
@NotNull String getOriginalName(@NonNull TabPlayer player);
@NotNull
@Deprecated
String getOriginalName(@NonNull TabPlayer player);
/**
* Returns player's original suffix applied using plugin's internal logic.
@@ -127,6 +134,69 @@ public interface TabListFormatManager {
* @return player's original suffix
* @see #setSuffix(TabPlayer, String)
* @see #getCustomSuffix(TabPlayer)
* @deprecated Use {@link #getOriginalRawSuffix(TabPlayer)} instead.
*/
@NotNull String getOriginalSuffix(@NonNull TabPlayer player);
@NotNull
@Deprecated
String getOriginalSuffix(@NonNull TabPlayer player);
/**
* Returns original prefix assigned by the plugin using internal logic.
* @param player
* Player to get original prefix of
* @return Original prefix assigned by the plugin's internal logic.
* @see #getCustomPrefix(TabPlayer)
*/
@NotNull
String getOriginalRawPrefix(@NonNull TabPlayer player);
/**
* Returns original name assigned by the plugin using internal logic.
* @param player
* Player to get original name of
* @return Original name assigned by the plugin's internal logic.
* @see #getCustomName(TabPlayer)
*/
@NotNull
String getOriginalRawName(@NonNull TabPlayer player);
/**
* Returns original suffix assigned by the plugin using internal logic.
* @param player
* Player to get original suffix of
* @return Original suffix assigned by the plugin's internal logic.
* @see #getCustomSuffix(TabPlayer)
*/
@NotNull
String getOriginalRawSuffix(@NonNull TabPlayer player);
/**
* Returns original prefix assigned by the plugin using internal logic with all placeholders replaced.
* @param player
* Player to get replaced original prefix of
* @return Original prefix assigned by the plugin's internal logic.
* @see #getCustomPrefix(TabPlayer)
*/
@NotNull
String getOriginalReplacedPrefix(@NonNull TabPlayer player);
/**
* Returns original name assigned by the plugin using internal logic with all placeholders replaced.
* @param player
* Player to get replaced original name of
* @return Original name assigned by the plugin's internal logic.
* @see #getCustomName(TabPlayer)
*/
@NotNull
String getOriginalReplacedName(@NonNull TabPlayer player);
/**
* Returns original suffix assigned by the plugin using internal logic with all placeholders replaced.
* @param player
* Player to get replaced original suffix of
* @return Original suffix assigned by the plugin's internal logic.
* @see #getCustomSuffix(TabPlayer)
*/
@NotNull
String getOriginalReplacedSuffix(@NonNull TabPlayer player);
}
@@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable;
/**
* Interface representing a layout.
*/
@SuppressWarnings("unused") // API class
public interface Layout {
/**
@@ -64,6 +65,20 @@ public interface Layout {
*/
void addFixedSlot(int slot, @NonNull String text, @NonNull String skin, int ping);
/**
* Adds fixed slot with specified parameters
*
* @param slot
* Slot (1-80)
* @param text
* Text to display
* @param skin
* Skin definition like in config
* @param ping
* Slot's ping, accepts placeholders that evaluate to a number
*/
void addFixedSlot(int slot, @NonNull String text, @NonNull String skin, @NonNull String ping);
/**
* Adds a player group.
*
@@ -3,6 +3,7 @@ package me.neznamy.tab.api.tablist.layout;
import lombok.NonNull;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -10,6 +11,7 @@ import org.jetbrains.annotations.Nullable;
* {@link TabAPI#getLayoutManager()}. If the feature is disabled in
* config, it will return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface LayoutManager {
/**
@@ -19,7 +21,31 @@ public interface LayoutManager {
* Unique layout name
* @return Created layout
*/
Layout createNewLayout(String name);
@NotNull
Layout createNewLayout(@NonNull String name);
/**
* Creates new layout with given name and slot count (1.19.3+ only).
*
* @param name
* Unique layout name
* @param slotCount
* Amount of slots to use for 1.19.3+ players
* @return Created layout
*/
@NotNull
Layout createNewLayout(@NonNull String name, int slotCount);
/**
* Returns defined layout from config by name. If no such layout is defined in config,
* {@code null} is returned.
*
* @param name
* Name of layout defined in config
* @return Layout defined in config by name or {@code null} if no such layout is defined
*/
@Nullable
Layout getLayout(@NonNull String name);
/**
* Sends layout to player. Set to {@code null} to make player not see
+2 -3
View File
@@ -3,7 +3,6 @@ plugins {
}
dependencies {
implementation("com.github.johnrengelman:shadow:8.1.1")
implementation("io.freefair.gradle:lombok-plugin:8.0.1")
implementation("fabric-loom:fabric-loom.gradle.plugin:1.1-SNAPSHOT")
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.4.0")
implementation("io.freefair.gradle:lombok-plugin:9.2.0")
}
+3
View File
@@ -3,6 +3,9 @@ rootProject.name = "build-logic"
dependencyResolutionManagement {
repositories {
maven("https://maven.fabricmc.net/")
maven("https://maven.architectury.dev/")
maven("https://maven.minecraftforge.net/")
maven("https://maven.neoforged.net/releases/")
gradlePluginPortal()
mavenCentral()
}
@@ -24,7 +24,7 @@ tasks {
}
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(8)
options.release.set(17)
options.compilerArgs.addAll(listOf("-nowarn", "-Xlint:-unchecked", "-Xlint:-deprecation"))
}
}
@@ -32,5 +32,6 @@ tasks {
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
vendor.set(JvmVendorSpec.GRAAL_VM)
}
}
@@ -1,15 +1,26 @@
plugins {
id("tab.standard-conventions")
id("com.github.johnrengelman.shadow")
id("com.gradleup.shadow")
}
tasks.withType<Zip>().configureEach {
isZip64 = true
}
tasks {
shadowJar {
archiveFileName.set("TAB-${project.name}-${project.version}.jar")
archiveFileName.set("${rootProject.name}-${project.name}-${project.version}.jar")
relocate("org.bstats", "me.neznamy.tab.libs.org.bstats")
relocate("org.json.simple", "me.neznamy.tab.libs.org.json.simple")
relocate("net.kyori.event", "me.neznamy.tab.libs.net.kyori.event")
relocate("me.neznamy.yamlassist", "me.neznamy.tab.libs.me.neznamy.yamlassist")
relocate("org.yaml.snakeyaml", "me.neznamy.tab.libs.org.yaml.snakeyaml")
relocate("redis.clients.jedis", "me.neznamy.tab.libs.redis.clients.jedis")
relocate("org.apache.commons.pool2", "me.neznamy.tab.libs.org.apache.commons.pool2")
relocate("org.json", "me.neznamy.tab.libs.org.json")
relocate("com.rabbitmq", "me.neznamy.tab.libs.com.rabbitmq")
relocate("com.saicone.delivery4j", "me.neznamy.tab.libs.com.saicone.delivery4j")
relocate("com.mysql", "me.neznamy.tab.libs.com.mysql")
relocate("com.google.protobuf", "me.neznamy.tab.libs.com.google.protobuf")
}
}
@@ -1,25 +1,8 @@
plugins {
id("tab.base-conventions")
`maven-publish`
}
java {
withSourcesJar()
withJavadocJar()
}
publishing {
repositories {
maven {
name = "krypton"
url = uri("https://repo.kryptonmc.org/releases")
credentials(PasswordCredentials::class)
}
}
publications.create<MavenPublication>("mavenJava") {
groupId = rootProject.group as String
artifactId = "tab-${project.name}"
version = rootProject.version as String
from(components["java"])
}
}
}
+27 -17
View File
@@ -1,35 +1,45 @@
plugins {
eclipse
id("tab.parent")
}
allprojects {
group = "me.neznamy"
version = "4.0.7"
version = "5.9.9"
description = "An all-in-one solution that works"
ext.set("id", "tab")
ext.set("website", "https://github.com/NEZNAMY/TAB")
ext.set("website", "https://git.trueog.net/trueog/TAB-OG")
ext.set("author", "NEZNAMY")
ext.set("credits", "Joseph T. McQuigg (JT122406)")
}
val platforms = setOf(
projects.bukkit,
projects.bungeecord,
projects.velocity,
projects.sponge7,
projects.sponge8,
projects.fabric
).map { it.dependencyProject }
val platformPaths = setOf(
":bukkit",
":bukkit:v1_19_R3",
":bungeecord",
":velocity"
)
val special = setOf(
projects.api,
projects.shared
).map { it.dependencyProject }
val specialPaths = setOf(
":api",
":shared"
)
subprojects {
when (this) {
in platforms -> plugins.apply("tab.platform-conventions")
in special -> plugins.apply("tab.standard-conventions")
when (path) {
in platformPaths -> plugins.apply("tab.platform-conventions")
in specialPaths -> plugins.apply("tab.standard-conventions")
else -> plugins.apply("tab.base-conventions")
}
}
val copyShadowJar by tasks.registering(Copy::class) {
dependsOn(":jar:shadowJar")
from(project(":jar").tasks.named("shadowJar"))
into(layout.buildDirectory.dir("libs"))
}
tasks.register("build") {
dependsOn(copyShadowJar)
}
+9 -8
View File
@@ -1,13 +1,14 @@
dependencies {
implementation(projects.shared)
implementation("org.bstats:bstats-bukkit:3.0.1")
compileOnly("org.purpurmc.purpur:purpur-api:1.16.5-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.2")
compileOnly("com.mojang:authlib:1.5.25")
compileOnly("com.github.MilkBowl:VaultAPI:1.7") {
exclude("org.bukkit", "bukkit")
}
api(projects.shared)
implementation("org.bstats:bstats-bukkit:3.1.0")
compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.6")
compileOnly("LibsDisguises:LibsDisguises:10.0.21") {
exclude("org.spigotmc", "spigot")
exclude("org.spigotmc", "spigot-api")
exclude("com.github.dmulloy2", "ProtocolLib")
exclude("org.ow2.asm", "asm")
exclude("net.md-5", "bungeecord-chat")
}
compileOnly("com.github.LeonMangler:PremiumVanishAPI:2.8.8")
}
+28
View File
@@ -0,0 +1,28 @@
plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
}
repositories {
maven("https://jitpack.io") // YamlAssist
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.viaversion.com/")
maven {
name = "luck-repo"
url = uri("https://repo.lucko.me/")
content {
includeModule("me.lucko", "spark-api")
}
}
}
val version = "1.20.6-R0.1-SNAPSHOT" // 1.20.5 has a broken dependency, but there were no changes, so it's ok
dependencies {
implementation(projects.bukkit)
paperweight.paperDevBundle(version)
compileOnly("io.papermc.paper:paper-api:${version}")
}
tasks.compileJava {
options.release.set(21)
}
@@ -0,0 +1,64 @@
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
import me.neznamy.tab.shared.chat.TabStyle;
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
import net.minecraft.network.chat.*;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
/**
* Component converter using direct mojang-mapped code.
*/
public class NMSComponentConverter extends ComponentConverter<Component> {
@Override
@NotNull
public Component newTextComponent(@NotNull String text) {
return Component.literal(text);
}
@Override
@NotNull
public Component newTranslatableComponent(@NotNull String key) {
return Component.translatable(key);
}
@Override
@NotNull
public Component newKeybindComponent(@NotNull String keybind) {
return Component.keybind(keybind);
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabAtlasSprite sprite) {
return Component.literal(TabObjectComponent.ERROR_MESSAGE);
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabPlayerSprite sprite) {
return Component.literal(TabObjectComponent.ERROR_MESSAGE);
}
@Override
public void applyStyle(@NotNull Component nmsComponent, @NotNull TabStyle modifier) {
Style style = Style.EMPTY
.withColor(modifier.getColor() == null ? null : TextColor.fromRgb(modifier.getColor().getRgb()))
.withBold(modifier.getBold())
.withItalic(modifier.getItalic())
.withUnderlined(modifier.getUnderlined())
.withStrikethrough(modifier.getStrikethrough())
.withObfuscated(modifier.getObfuscated())
.withFont(modifier.getFont() == null ? null : ResourceLocation.tryParse(modifier.getFont()));
((MutableComponent)nmsComponent).setStyle(style);
}
@Override
public void addSibling(@NotNull Component parent, @NotNull Component child) {
((MutableComponent)parent).append(child);
}
}
@@ -0,0 +1,52 @@
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
import io.netty.channel.Channel;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Implementation provider using direct Mojang-mapped NMS code for paper 1.20.5 - 1.21.1.
*/
@Getter
public class NMSImplementationProvider implements ImplementationProvider {
@NotNull
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
@Override
@NotNull
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
return new NMSPacketScoreboard(player);
}
@Override
@NotNull
public TabList newTabList(@NotNull BukkitTabPlayer player) {
return new NMSPacketTabList(player);
}
@Override
@NotNull
public Channel getChannel(@NotNull Player player) {
return ((CraftPlayer)player).getHandle().connection.connection.channel;
}
@Override
@NotNull
public TabListEntryTracker newTabListEntryTracker() {
return new NMSTabListEntryTracker();
}
@Override
public int getPing(@NotNull BukkitTabPlayer player) {
return player.getPlayer().getPing();
}
}
@@ -0,0 +1,176 @@
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
/**
* Scoreboard implementation using direct mojang-mapped code.
*/
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
private static final ChatFormatting[] formats = ChatFormatting.values();
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
private static final net.minecraft.world.scores.Team.Visibility[] visibilities = net.minecraft.world.scores.Team.Visibility.values();
private static final Scoreboard dummyScoreboard = new Scoreboard();
private static final Field players = ReflectionUtils.getOnlyField(ClientboundSetPlayerTeamPacket.class, Collection.class);
/**
* Constructs new instance with given player.
*
* @param player
* Player this scoreboard will belong to
*/
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void registerObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = new net.minecraft.world.scores.Objective(
dummyScoreboard,
objective.getName(),
ObjectiveCriteria.DUMMY,
objective.getTitle().convert(),
RenderType.values()[objective.getHealthDisplay().ordinal()],
false,
objective.getNumberFormat() == null ? null : objective.getNumberFormat().toFixedFormat(FixedFormat::new)
);
objective.setPlatformObjective(obj);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.REGISTER));
}
@Override
public void setDisplaySlot(@NonNull Objective objective) {
sendPacket(new ClientboundSetDisplayObjectivePacket(
net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()],
(net.minecraft.world.scores.Objective) objective.getPlatformObjective()
));
}
@Override
public void unregisterObjective(@NonNull Objective objective) {
sendPacket(new ClientboundSetObjectivePacket((net.minecraft.world.scores.Objective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
}
@Override
public void updateObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = (net.minecraft.world.scores.Objective) objective.getPlatformObjective();
obj.setDisplayName(objective.getTitle().convert());
obj.setRenderType(RenderType.values()[objective.getHealthDisplay().ordinal()]);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.UPDATE));
}
@Override
public void setScore(@NonNull Score score) {
sendPacket(new ClientboundSetScorePacket(
score.getHolder(),
score.getObjective().getName(),
score.getValue(),
Optional.ofNullable(score.getDisplayName() == null ? null : score.getDisplayName().convert()),
Optional.ofNullable(score.getNumberFormat() == null ? null : score.getNumberFormat().toFixedFormat(FixedFormat::new))
));
}
@Override
public void removeScore(@NonNull Score score) {
sendPacket(new ClientboundResetScorePacket(score.getHolder(), score.getObjective().getName()));
}
@Override
@NotNull
public Object createTeam(@NonNull String name) {
return new PlayerTeam(dummyScoreboard, name);
}
@Override
public void registerTeam(@NonNull Team team) {
updateTeamProperties(team);
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.getPlayers().addAll(team.getPlayers());
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(t, true));
}
@Override
public void unregisterTeam(@NonNull Team team) {
sendPacket(ClientboundSetPlayerTeamPacket.createRemovePacket((PlayerTeam) team.getPlatformTeam()));
}
@Override
public void updateTeam(@NonNull Team team) {
updateTeamProperties(team);
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket((PlayerTeam) team.getPlatformTeam(), false));
}
private void updateTeamProperties(@NonNull Team team) {
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
t.setSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
t.setColor(formats[team.getColor().ordinal()]);
t.setCollisionRule(collisions[team.getCollision().ordinal()]);
t.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
t.setPlayerPrefix(team.getPrefix().convert());
t.setPlayerSuffix(team.getSuffix().convert());
}
@Override
@SneakyThrows
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
}
if (packet instanceof ClientboundSetObjectivePacket objective) {
TAB.getInstance().getFeatureManager().onObjective(player, objective.getMethod(), objective.getObjectiveName());
}
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
int action = getMethod(team);
if (action != TeamAction.UPDATE) {
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
}
}
return packet;
}
private int getMethod(@NonNull ClientboundSetPlayerTeamPacket team) {
if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 0;
} else if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 1;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 3;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 4;
} else {
return 2;
}
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.sendPacket(packet);
}
}
@@ -0,0 +1,200 @@
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.NonNull;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
import net.minecraft.world.level.GameType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* TabList implementation using direct mojang-mapped code.
*/
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateLatency = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateGameMode = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListed = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
/**
* Constructs new instance.
*
* @param player
* Player this tablist will belong to
*/
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void removeEntry(@NonNull UUID entry) {
sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
sendPacket(updateDisplayName, entry, "", null, false, 0, 0, displayName);
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
sendPacket(updateLatency, entry, "", null, false, latency, 0, null);
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
sendPacket(updateGameMode, entry, "", null, false, 0, gameMode, null);
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
sendPacket(updateListed, entry, "", null, listed, 0, 0, null);
}
@Override
public void updateListOrder(@NonNull UUID entry, int listOrder) {
// Added in 1.21.2
}
@Override
public void updateHat(@NonNull UUID entry, boolean showHat) {
// Added in 1.21.4
}
@Override
public void addEntry0(@NonNull Entry entry) {
sendPacket(addPlayer, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.isListed(), entry.getLatency(),
entry.getGameMode(), entry.getDisplayName());
}
@Override
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
}
@Override
@Nullable
public Skin getSkin() {
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().getProperties().get(TEXTURES_PROPERTY);
if (properties.isEmpty()) return null; // Offline mode
Property property = properties.iterator().next();
return new Skin(property.value(), property.signature());
}
@Override
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundTabListPacket tablist) {
if (header == null || footer == null) return packet;
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
return new ClientboundTabListPacket(header.convert(), footer.convert());
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
boolean rewritePacket = false;
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : info.entries()) {
boolean rewriteEntry = false;
Component displayName = nmsData.displayName();
int latency = nmsData.latency();
int gameMode = nmsData.gameMode().getId();
boolean listed = nmsData.listed();
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
displayName = forcedDisplayName.convert();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
gameMode = 0;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY)) {
if (getForcedLatency() != null) {
latency = getForcedLatency();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
listed = false;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().getName());
}
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
nmsData.chatSession()
) : nmsData);
}
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
}
return packet;
}
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
boolean listed, int latency, int gameMode, @Nullable TabComponent displayName) {
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, new ClientboundPlayerInfoUpdatePacket.Entry(
id,
action.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER) ? createProfile(id, name, skin) : null,
listed,
latency,
GameType.byId(gameMode),
displayName == null ? null : displayName.convert(),
null
));
sendPacket(packet);
}
/**
* Creates GameProfile from given parameters.
*
* @param id
* Profile ID
* @param name
* Profile name
* @param skin
* Player skin
* @return GameProfile from given parameters
*/
@NotNull
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
GameProfile profile = new GameProfile(id, name);
if (skin != null) {
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
}
return profile;
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
}
}
@@ -0,0 +1,30 @@
package me.neznamy.tab.platforms.bukkit.paper_1_20_5;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Implementation of TabListEntryTracker.
*/
public class NMSTabListEntryTracker extends TabListEntryTracker {
@Override
public void onPacketSend(@NotNull Object packet) {
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
for (UUID id : remove.profileIds()) {
tablistEntries.remove(id);
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
tablistEntries.add(nmsData.profileId());
}
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
}
repositories {
maven("https://jitpack.io") // YamlAssist
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.viaversion.com/")
maven {
name = "luck-repo"
url = uri("https://repo.lucko.me/")
content {
includeModule("me.lucko", "spark-api")
}
}
}
val version = "1.21.11-R0.1-SNAPSHOT"
dependencies {
implementation(projects.bukkit)
paperweight.paperDevBundle(version)
compileOnly("io.papermc.paper:paper-api:${version}")
}
tasks.compileJava {
options.release.set(21)
}
@@ -0,0 +1,84 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
import com.google.common.collect.ImmutableMultimap;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.shared.chat.TabStyle;
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
import me.neznamy.tab.shared.platform.TabList;
import net.minecraft.network.chat.*;
import net.minecraft.network.chat.contents.objects.AtlasSprite;
import net.minecraft.network.chat.contents.objects.PlayerSprite;
import net.minecraft.resources.Identifier;
import net.minecraft.world.item.component.ResolvableProfile;
import org.jetbrains.annotations.NotNull;
/**
* Component converter using direct mojang-mapped code.
*/
public class NMSComponentConverter extends ComponentConverter<Component> {
@Override
@NotNull
public Component newTextComponent(@NotNull String text) {
return Component.literal(text);
}
@Override
@NotNull
public Component newTranslatableComponent(@NotNull String key) {
return Component.translatable(key);
}
@Override
@NotNull
public Component newKeybindComponent(@NotNull String keybind) {
return Component.keybind(keybind);
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabAtlasSprite sprite) {
return Component.object(new AtlasSprite(Identifier.parse(sprite.getAtlas()), Identifier.parse(sprite.getSprite())));
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabPlayerSprite sprite) {
ResolvableProfile profile;
if (sprite.getId() != null) {
profile = ResolvableProfile.createUnresolved(sprite.getId());
} else if (sprite.getName() != null) {
profile = ResolvableProfile.createUnresolved(sprite.getName());
} else if (sprite.getSkin() != null) {
ImmutableMultimap.Builder<String, Property> builder = ImmutableMultimap.builder();
builder.put(TabList.TEXTURES_PROPERTY, new Property(TabList.TEXTURES_PROPERTY, sprite.getSkin().getValue(), sprite.getSkin().getSignature()));
profile = ResolvableProfile.createResolved(new GameProfile(NIL_UUID, "", new PropertyMap(builder.build())));
} else {
throw new IllegalStateException("Player head component does not have id, name or skin set");
}
return Component.object(new PlayerSprite(profile, sprite.isShowHat()));
}
@Override
public void applyStyle(@NotNull Component nmsComponent, @NotNull TabStyle modifier) {
Style style = Style.EMPTY
.withColor(modifier.getColor() == null ? null : TextColor.fromRgb(modifier.getColor().getRgb()))
.withBold(modifier.getBold())
.withItalic(modifier.getItalic())
.withUnderlined(modifier.getUnderlined())
.withStrikethrough(modifier.getStrikethrough())
.withObfuscated(modifier.getObfuscated())
.withFont(modifier.getFont() == null ? null : new FontDescription.Resource(Identifier.parse(modifier.getFont())));
if (modifier.getShadowColor() != null) style = style.withShadowColor(modifier.getShadowColor()); // withShadowColor takes int instead of Integer, bug?
((MutableComponent)nmsComponent).setStyle(style);
}
@Override
public void addSibling(@NotNull Component parent, @NotNull Component child) {
((MutableComponent)parent).append(child);
}
}
@@ -0,0 +1,52 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
import io.netty.channel.Channel;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Implementation provider using direct Mojang-mapped NMS code for paper 1.21.11.
*/
@Getter
public class NMSImplementationProvider implements ImplementationProvider {
@NotNull
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
@Override
@NotNull
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
return new NMSPacketScoreboard(player);
}
@Override
@NotNull
public TabList newTabList(@NotNull BukkitTabPlayer player) {
return new NMSPacketTabList(player);
}
@Override
@NotNull
public Channel getChannel(@NotNull Player player) {
return ((CraftPlayer)player).getHandle().connection.connection.channel;
}
@Override
@NotNull
public TabListEntryTracker newTabListEntryTracker() {
return new NMSTabListEntryTracker();
}
@Override
public int getPing(@NotNull BukkitTabPlayer player) {
return player.getPlayer().getPing();
}
}
@@ -0,0 +1,176 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
/**
* Scoreboard implementation using direct mojang-mapped code.
*/
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
private static final ChatFormatting[] formats = ChatFormatting.values();
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
private static final net.minecraft.world.scores.Team.Visibility[] visibilities = net.minecraft.world.scores.Team.Visibility.values();
private static final Scoreboard dummyScoreboard = new Scoreboard();
private static final Field players = ReflectionUtils.getOnlyField(ClientboundSetPlayerTeamPacket.class, Collection.class);
/**
* Constructs new instance with given player.
*
* @param player
* Player this scoreboard will belong to
*/
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void registerObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = new net.minecraft.world.scores.Objective(
dummyScoreboard,
objective.getName(),
ObjectiveCriteria.DUMMY,
objective.getTitle().convert(),
RenderType.values()[objective.getHealthDisplay().ordinal()],
false,
objective.getNumberFormat() == null ? null : objective.getNumberFormat().toFixedFormat(FixedFormat::new)
);
objective.setPlatformObjective(obj);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.REGISTER));
}
@Override
public void setDisplaySlot(@NonNull Objective objective) {
sendPacket(new ClientboundSetDisplayObjectivePacket(
net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()],
(net.minecraft.world.scores.Objective) objective.getPlatformObjective()
));
}
@Override
public void unregisterObjective(@NonNull Objective objective) {
sendPacket(new ClientboundSetObjectivePacket((net.minecraft.world.scores.Objective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
}
@Override
public void updateObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = (net.minecraft.world.scores.Objective) objective.getPlatformObjective();
obj.setDisplayName(objective.getTitle().convert());
obj.setRenderType(RenderType.values()[objective.getHealthDisplay().ordinal()]);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.UPDATE));
}
@Override
public void setScore(@NonNull Score score) {
sendPacket(new ClientboundSetScorePacket(
score.getHolder(),
score.getObjective().getName(),
score.getValue(),
Optional.ofNullable(score.getDisplayName() == null ? null : score.getDisplayName().convert()),
Optional.ofNullable(score.getNumberFormat() == null ? null : score.getNumberFormat().toFixedFormat(FixedFormat::new))
));
}
@Override
public void removeScore(@NonNull Score score) {
sendPacket(new ClientboundResetScorePacket(score.getHolder(), score.getObjective().getName()));
}
@Override
@NotNull
public Object createTeam(@NonNull String name) {
return new PlayerTeam(dummyScoreboard, name);
}
@Override
public void registerTeam(@NonNull Team team) {
updateTeamProperties(team);
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.getPlayers().addAll(team.getPlayers());
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(t, true));
}
@Override
public void unregisterTeam(@NonNull Team team) {
sendPacket(ClientboundSetPlayerTeamPacket.createRemovePacket((PlayerTeam) team.getPlatformTeam()));
}
@Override
public void updateTeam(@NonNull Team team) {
updateTeamProperties(team);
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket((PlayerTeam) team.getPlatformTeam(), false));
}
private void updateTeamProperties(@NonNull Team team) {
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
t.setSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
t.setColor(formats[team.getColor().ordinal()]);
t.setCollisionRule(collisions[team.getCollision().ordinal()]);
t.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
t.setPlayerPrefix(team.getPrefix().convert());
t.setPlayerSuffix(team.getSuffix().convert());
}
@Override
@SneakyThrows
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
}
if (packet instanceof ClientboundSetObjectivePacket objective) {
TAB.getInstance().getFeatureManager().onObjective(player, objective.getMethod(), objective.getObjectiveName());
}
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
int action = getMethod(team);
if (action != TeamAction.UPDATE) {
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
}
}
return packet;
}
private int getMethod(@NonNull ClientboundSetPlayerTeamPacket team) {
if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 0;
} else if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 1;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 3;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 4;
} else {
return 2;
}
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
}
}
@@ -0,0 +1,204 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
import com.google.common.collect.ImmutableMultimap;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import lombok.NonNull;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
import net.minecraft.world.level.GameType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* TabList implementation using direct mojang-mapped code.
*/
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateLatency = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateGameMode = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListed = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateHat = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT);
/**
* Constructs new instance.
*
* @param player
* Player this tablist will belong to
*/
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void removeEntry(@NonNull UUID entry) {
sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
sendPacket(updateDisplayName, entry, "", null, false, 0, 0, displayName, 0, false);
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
sendPacket(updateLatency, entry, "", null, false, latency, 0, null, 0, false);
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
sendPacket(updateGameMode, entry, "", null, false, 0, gameMode, null, 0, false);
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
sendPacket(updateListed, entry, "", null, listed, 0, 0, null, 0, false);
}
@Override
public void updateListOrder(@NonNull UUID entry, int listOrder) {
sendPacket(updateListOrder, entry, "", null, false, 0, 0, null, listOrder, false);
}
@Override
public void updateHat(@NonNull UUID entry, boolean showHat) {
sendPacket(updateHat, entry, "", null, false, 0, 0, null, 0, showHat);
}
@Override
public void addEntry0(@NonNull Entry entry) {
sendPacket(addPlayer, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.isListed(), entry.getLatency(),
entry.getGameMode(), entry.getDisplayName(), entry.getListOrder(), entry.isShowHat());
}
@Override
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
}
@Override
@Nullable
public Skin getSkin() {
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().properties().get(TEXTURES_PROPERTY);
if (properties.isEmpty()) return null; // Offline mode
Property property = properties.iterator().next();
return new Skin(property.value(), property.signature());
}
@Override
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundTabListPacket tablist) {
if (header == null || footer == null) return packet;
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
return new ClientboundTabListPacket(header.convert(), footer.convert());
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
boolean rewritePacket = false;
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : info.entries()) {
boolean rewriteEntry = false;
Component displayName = nmsData.displayName();
int latency = nmsData.latency();
int gameMode = nmsData.gameMode().getId();
boolean listed = nmsData.listed();
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
displayName = forcedDisplayName.convert();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
gameMode = 0;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY)) {
if (getForcedLatency() != null) {
latency = getForcedLatency();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
listed = false;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().name());
}
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
nmsData.showHat(), nmsData.listOrder(), nmsData.chatSession()
) : nmsData);
}
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
}
return packet;
}
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
boolean listed, int latency, int gameMode, @Nullable TabComponent displayName, int listOrder, boolean showHat) {
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, new ClientboundPlayerInfoUpdatePacket.Entry(
id,
action.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER) ? createProfile(id, name, skin) : null,
listed,
latency,
GameType.byId(gameMode),
displayName == null ? null : displayName.convert(),
showHat,
listOrder,
null
));
sendPacket(packet);
}
/**
* Creates GameProfile from given parameters.
*
* @param id
* Profile ID
* @param name
* Profile name
* @param skin
* Player skin
* @return GameProfile from given parameters
*/
@NotNull
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
ImmutableMultimap.Builder<String, Property> builder = ImmutableMultimap.builder();
if (skin != null) {
builder.put(TEXTURES_PROPERTY, new Property(TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
}
return new GameProfile(id, name, new PropertyMap(builder.build()));
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
}
}
@@ -0,0 +1,30 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_11;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Implementation of TabListEntryTracker.
*/
public class NMSTabListEntryTracker extends TabListEntryTracker {
@Override
public void onPacketSend(@NotNull Object packet) {
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
for (UUID id : remove.profileIds()) {
tablistEntries.remove(id);
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
tablistEntries.add(nmsData.profileId());
}
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
}
repositories {
maven("https://jitpack.io") // YamlAssist
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.viaversion.com/")
maven {
name = "luck-repo"
url = uri("https://repo.lucko.me/")
content {
includeModule("me.lucko", "spark-api")
}
}
}
val version = "1.21.3-R0.1-SNAPSHOT" // 1.21.2 causes gradle error, but the versions are interchangeable
dependencies {
implementation(projects.bukkit)
paperweight.paperDevBundle(version)
compileOnly("io.papermc.paper:paper-api:${version}")
}
tasks.compileJava {
options.release.set(21)
}
@@ -0,0 +1,67 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
import me.neznamy.tab.shared.chat.TabStyle;
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextColor;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
/**
* Component converter using direct mojang-mapped code.
*/
public class NMSComponentConverter extends ComponentConverter<Component> {
@Override
@NotNull
public Component newTextComponent(@NotNull String text) {
return Component.literal(text);
}
@Override
@NotNull
public Component newTranslatableComponent(@NotNull String key) {
return Component.translatable(key);
}
@Override
@NotNull
public Component newKeybindComponent(@NotNull String keybind) {
return Component.keybind(keybind);
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabAtlasSprite sprite) {
return Component.literal(TabObjectComponent.ERROR_MESSAGE);
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabPlayerSprite sprite) {
return Component.literal(TabObjectComponent.ERROR_MESSAGE);
}
@Override
public void applyStyle(@NotNull Component nmsComponent, @NotNull TabStyle modifier) {
Style style = Style.EMPTY
.withColor(modifier.getColor() == null ? null : TextColor.fromRgb(modifier.getColor().getRgb()))
.withBold(modifier.getBold())
.withItalic(modifier.getItalic())
.withUnderlined(modifier.getUnderlined())
.withStrikethrough(modifier.getStrikethrough())
.withObfuscated(modifier.getObfuscated())
.withFont(modifier.getFont() == null ? null : ResourceLocation.tryParse(modifier.getFont()));
((MutableComponent)nmsComponent).setStyle(style);
}
@Override
public void addSibling(@NotNull Component parent, @NotNull Component child) {
((MutableComponent)parent).append(child);
}
}
@@ -0,0 +1,52 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
import io.netty.channel.Channel;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Implementation provider using direct Mojang-mapped NMS code for paper 1.21.2 - 1.21.3.
*/
@Getter
public class NMSImplementationProvider implements ImplementationProvider {
@NotNull
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
@Override
@NotNull
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
return new NMSPacketScoreboard(player);
}
@Override
@NotNull
public TabList newTabList(@NotNull BukkitTabPlayer player) {
return new NMSPacketTabList(player);
}
@Override
@NotNull
public Channel getChannel(@NotNull Player player) {
return ((CraftPlayer)player).getHandle().connection.connection.channel;
}
@Override
@NotNull
public TabListEntryTracker newTabListEntryTracker() {
return new NMSTabListEntryTracker();
}
@Override
public int getPing(@NotNull BukkitTabPlayer player) {
return player.getPlayer().getPing();
}
}
@@ -0,0 +1,176 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
/**
* Scoreboard implementation using direct mojang-mapped code.
*/
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
private static final ChatFormatting[] formats = ChatFormatting.values();
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
private static final net.minecraft.world.scores.Team.Visibility[] visibilities = net.minecraft.world.scores.Team.Visibility.values();
private static final Scoreboard dummyScoreboard = new Scoreboard();
private static final Field players = ReflectionUtils.getOnlyField(ClientboundSetPlayerTeamPacket.class, Collection.class);
/**
* Constructs new instance with given player.
*
* @param player
* Player this scoreboard will belong to
*/
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void registerObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = new net.minecraft.world.scores.Objective(
dummyScoreboard,
objective.getName(),
ObjectiveCriteria.DUMMY,
objective.getTitle().convert(),
RenderType.values()[objective.getHealthDisplay().ordinal()],
false,
objective.getNumberFormat() == null ? null : objective.getNumberFormat().toFixedFormat(FixedFormat::new)
);
objective.setPlatformObjective(obj);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.REGISTER));
}
@Override
public void setDisplaySlot(@NonNull Objective objective) {
sendPacket(new ClientboundSetDisplayObjectivePacket(
net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()],
(net.minecraft.world.scores.Objective) objective.getPlatformObjective()
));
}
@Override
public void unregisterObjective(@NonNull Objective objective) {
sendPacket(new ClientboundSetObjectivePacket((net.minecraft.world.scores.Objective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
}
@Override
public void updateObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = (net.minecraft.world.scores.Objective) objective.getPlatformObjective();
obj.setDisplayName(objective.getTitle().convert());
obj.setRenderType(RenderType.values()[objective.getHealthDisplay().ordinal()]);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.UPDATE));
}
@Override
public void setScore(@NonNull Score score) {
sendPacket(new ClientboundSetScorePacket(
score.getHolder(),
score.getObjective().getName(),
score.getValue(),
Optional.ofNullable(score.getDisplayName() == null ? null : score.getDisplayName().convert()),
Optional.ofNullable(score.getNumberFormat() == null ? null : score.getNumberFormat().toFixedFormat(FixedFormat::new))
));
}
@Override
public void removeScore(@NonNull Score score) {
sendPacket(new ClientboundResetScorePacket(score.getHolder(), score.getObjective().getName()));
}
@Override
@NotNull
public Object createTeam(@NonNull String name) {
return new PlayerTeam(dummyScoreboard, name);
}
@Override
public void registerTeam(@NonNull Team team) {
updateTeamProperties(team);
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.getPlayers().addAll(team.getPlayers());
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(t, true));
}
@Override
public void unregisterTeam(@NonNull Team team) {
sendPacket(ClientboundSetPlayerTeamPacket.createRemovePacket((PlayerTeam) team.getPlatformTeam()));
}
@Override
public void updateTeam(@NonNull Team team) {
updateTeamProperties(team);
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket((PlayerTeam) team.getPlatformTeam(), false));
}
private void updateTeamProperties(@NonNull Team team) {
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
t.setSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
t.setColor(formats[team.getColor().ordinal()]);
t.setCollisionRule(collisions[team.getCollision().ordinal()]);
t.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
t.setPlayerPrefix(team.getPrefix().convert());
t.setPlayerSuffix(team.getSuffix().convert());
}
@Override
@SneakyThrows
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
}
if (packet instanceof ClientboundSetObjectivePacket objective) {
TAB.getInstance().getFeatureManager().onObjective(player, objective.getMethod(), objective.getObjectiveName());
}
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
int action = getMethod(team);
if (action != TeamAction.UPDATE) {
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
}
}
return packet;
}
private static int getMethod(@NonNull ClientboundSetPlayerTeamPacket team) {
if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 0;
} else if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 1;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 3;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 4;
} else {
return 2;
}
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.sendPacket(packet);
}
}
@@ -0,0 +1,202 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.NonNull;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
import net.minecraft.world.level.GameType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* TabList implementation using direct mojang-mapped code.
*/
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateLatency = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateGameMode = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListed = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
/**
* Constructs new instance.
*
* @param player
* Player this tablist will belong to
*/
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void removeEntry(@NonNull UUID entry) {
sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
sendPacket(updateDisplayName, entry, "", null, false, 0, 0, displayName, 0);
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
sendPacket(updateLatency, entry, "", null, false, latency, 0, null, 0);
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
sendPacket(updateGameMode, entry, "", null, false, 0, gameMode, null, 0);
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
sendPacket(updateListed, entry, "", null, listed, 0, 0, null, 0);
}
@Override
public void updateListOrder(@NonNull UUID entry, int listOrder) {
sendPacket(updateListOrder, entry, "", null, false, 0, 0, null, listOrder);
}
@Override
public void updateHat(@NonNull UUID entry, boolean showHat) {
// Added in 1.21.4
}
@Override
public void addEntry0(@NonNull Entry entry) {
sendPacket(addPlayer, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.isListed(), entry.getLatency(),
entry.getGameMode(), entry.getDisplayName(), entry.getListOrder());
}
@Override
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
}
@Override
@Nullable
public Skin getSkin() {
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().getProperties().get(TEXTURES_PROPERTY);
if (properties.isEmpty()) return null; // Offline mode
Property property = properties.iterator().next();
return new Skin(property.value(), property.signature());
}
@Override
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundTabListPacket tablist) {
if (header == null || footer == null) return packet;
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
return new ClientboundTabListPacket(header.convert(), footer.convert());
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
boolean rewritePacket = false;
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : info.entries()) {
boolean rewriteEntry = false;
Component displayName = nmsData.displayName();
int latency = nmsData.latency();
int gameMode = nmsData.gameMode().getId();
boolean listed = nmsData.listed();
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
displayName = forcedDisplayName.convert();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
gameMode = 0;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY)) {
if (getForcedLatency() != null) {
latency = getForcedLatency();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
listed = false;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().getName());
}
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
nmsData.listOrder(), nmsData.chatSession()
) : nmsData);
}
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
}
return packet;
}
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
boolean listed, int latency, int gameMode, @Nullable TabComponent displayName, int listOrder) {
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, new ClientboundPlayerInfoUpdatePacket.Entry(
id,
action.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER) ? createProfile(id, name, skin) : null,
listed,
latency,
GameType.byId(gameMode),
displayName == null ? null : displayName.convert(),
listOrder,
null
));
sendPacket(packet);
}
/**
* Creates GameProfile from given parameters.
*
* @param id
* Profile ID
* @param name
* Profile name
* @param skin
* Player skin
* @return GameProfile from given parameters
*/
@NotNull
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
GameProfile profile = new GameProfile(id, name);
if (skin != null) {
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
}
return profile;
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
}
}
@@ -0,0 +1,30 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_2;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Implementation of TabListEntryTracker.
*/
public class NMSTabListEntryTracker extends TabListEntryTracker {
@Override
public void onPacketSend(@NotNull Object packet) {
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
for (UUID id : remove.profileIds()) {
tablistEntries.remove(id);
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
tablistEntries.add(nmsData.profileId());
}
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
}
repositories {
maven("https://jitpack.io") // YamlAssist
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.viaversion.com/")
maven {
name = "luck-repo"
url = uri("https://repo.lucko.me/")
content {
includeModule("me.lucko", "spark-api")
}
}
}
val version = "1.21.4-R0.1-SNAPSHOT"
dependencies {
implementation(projects.bukkit)
paperweight.paperDevBundle(version)
compileOnly("io.papermc.paper:paper-api:${version}")
}
tasks.compileJava {
options.release.set(21)
}
@@ -0,0 +1,65 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
import me.neznamy.tab.shared.chat.TabStyle;
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
import net.minecraft.network.chat.*;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
/**
* Component converter using direct mojang-mapped code.
*/
public class NMSComponentConverter extends ComponentConverter<Component> {
@Override
@NotNull
public Component newTextComponent(@NotNull String text) {
return Component.literal(text);
}
@Override
@NotNull
public Component newTranslatableComponent(@NotNull String key) {
return Component.translatable(key);
}
@Override
@NotNull
public Component newKeybindComponent(@NotNull String keybind) {
return Component.keybind(keybind);
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabAtlasSprite sprite) {
return Component.literal(TabObjectComponent.ERROR_MESSAGE);
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabPlayerSprite sprite) {
return Component.literal(TabObjectComponent.ERROR_MESSAGE);
}
@Override
public void applyStyle(@NotNull Component nmsComponent, @NotNull TabStyle modifier) {
Style style = Style.EMPTY
.withColor(modifier.getColor() == null ? null : TextColor.fromRgb(modifier.getColor().getRgb()))
.withBold(modifier.getBold())
.withItalic(modifier.getItalic())
.withUnderlined(modifier.getUnderlined())
.withStrikethrough(modifier.getStrikethrough())
.withObfuscated(modifier.getObfuscated())
.withFont(modifier.getFont() == null ? null : ResourceLocation.tryParse(modifier.getFont()));
if (modifier.getShadowColor() != null) style = style.withShadowColor(modifier.getShadowColor()); // withShadowColor takes int instead of Integer, bug?
((MutableComponent)nmsComponent).setStyle(style);
}
@Override
public void addSibling(@NotNull Component parent, @NotNull Component child) {
((MutableComponent)parent).append(child);
}
}
@@ -0,0 +1,52 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
import io.netty.channel.Channel;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Implementation provider using direct Mojang-mapped NMS code for paper 1.21.4 - 1.21.8.
*/
@Getter
public class NMSImplementationProvider implements ImplementationProvider {
@NotNull
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
@Override
@NotNull
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
return new NMSPacketScoreboard(player);
}
@Override
@NotNull
public TabList newTabList(@NotNull BukkitTabPlayer player) {
return new NMSPacketTabList(player);
}
@Override
@NotNull
public Channel getChannel(@NotNull Player player) {
return ((CraftPlayer)player).getHandle().connection.connection.channel;
}
@Override
@NotNull
public TabListEntryTracker newTabListEntryTracker() {
return new NMSTabListEntryTracker();
}
@Override
public int getPing(@NotNull BukkitTabPlayer player) {
return player.getPlayer().getPing();
}
}
@@ -0,0 +1,176 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
/**
* Scoreboard implementation using direct mojang-mapped code.
*/
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
private static final ChatFormatting[] formats = ChatFormatting.values();
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
private static final net.minecraft.world.scores.Team.Visibility[] visibilities = net.minecraft.world.scores.Team.Visibility.values();
private static final Scoreboard dummyScoreboard = new Scoreboard();
private static final Field players = ReflectionUtils.getOnlyField(ClientboundSetPlayerTeamPacket.class, Collection.class);
/**
* Constructs new instance with given player.
*
* @param player
* Player this scoreboard will belong to
*/
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void registerObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = new net.minecraft.world.scores.Objective(
dummyScoreboard,
objective.getName(),
ObjectiveCriteria.DUMMY,
objective.getTitle().convert(),
RenderType.values()[objective.getHealthDisplay().ordinal()],
false,
objective.getNumberFormat() == null ? null : objective.getNumberFormat().toFixedFormat(FixedFormat::new)
);
objective.setPlatformObjective(obj);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.REGISTER));
}
@Override
public void setDisplaySlot(@NonNull Objective objective) {
sendPacket(new ClientboundSetDisplayObjectivePacket(
net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()],
(net.minecraft.world.scores.Objective) objective.getPlatformObjective()
));
}
@Override
public void unregisterObjective(@NonNull Objective objective) {
sendPacket(new ClientboundSetObjectivePacket((net.minecraft.world.scores.Objective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
}
@Override
public void updateObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = (net.minecraft.world.scores.Objective) objective.getPlatformObjective();
obj.setDisplayName(objective.getTitle().convert());
obj.setRenderType(RenderType.values()[objective.getHealthDisplay().ordinal()]);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.UPDATE));
}
@Override
public void setScore(@NonNull Score score) {
sendPacket(new ClientboundSetScorePacket(
score.getHolder(),
score.getObjective().getName(),
score.getValue(),
Optional.ofNullable(score.getDisplayName() == null ? null : score.getDisplayName().convert()),
Optional.ofNullable(score.getNumberFormat() == null ? null : score.getNumberFormat().toFixedFormat(FixedFormat::new))
));
}
@Override
public void removeScore(@NonNull Score score) {
sendPacket(new ClientboundResetScorePacket(score.getHolder(), score.getObjective().getName()));
}
@Override
@NotNull
public Object createTeam(@NonNull String name) {
return new PlayerTeam(dummyScoreboard, name);
}
@Override
public void registerTeam(@NonNull Team team) {
updateTeamProperties(team);
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.getPlayers().addAll(team.getPlayers());
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(t, true));
}
@Override
public void unregisterTeam(@NonNull Team team) {
sendPacket(ClientboundSetPlayerTeamPacket.createRemovePacket((PlayerTeam) team.getPlatformTeam()));
}
@Override
public void updateTeam(@NonNull Team team) {
updateTeamProperties(team);
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket((PlayerTeam) team.getPlatformTeam(), false));
}
private void updateTeamProperties(@NonNull Team team) {
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
t.setSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
t.setColor(formats[team.getColor().ordinal()]);
t.setCollisionRule(collisions[team.getCollision().ordinal()]);
t.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
t.setPlayerPrefix(team.getPrefix().convert());
t.setPlayerSuffix(team.getSuffix().convert());
}
@Override
@SneakyThrows
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
}
if (packet instanceof ClientboundSetObjectivePacket objective) {
TAB.getInstance().getFeatureManager().onObjective(player, objective.getMethod(), objective.getObjectiveName());
}
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
int action = getMethod(team);
if (action != TeamAction.UPDATE) {
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
}
}
return packet;
}
private int getMethod(@NonNull ClientboundSetPlayerTeamPacket team) {
if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 0;
} else if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 1;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 3;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 4;
} else {
return 2;
}
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
}
}
@@ -0,0 +1,204 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.NonNull;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
import net.minecraft.world.level.GameType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* TabList implementation using direct mojang-mapped code.
*/
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateLatency = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateGameMode = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListed = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateHat = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT);
/**
* Constructs new instance.
*
* @param player
* Player this tablist will belong to
*/
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void removeEntry(@NonNull UUID entry) {
sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
sendPacket(updateDisplayName, entry, "", null, false, 0, 0, displayName, 0, false);
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
sendPacket(updateLatency, entry, "", null, false, latency, 0, null, 0, false);
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
sendPacket(updateGameMode, entry, "", null, false, 0, gameMode, null, 0, false);
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
sendPacket(updateListed, entry, "", null, listed, 0, 0, null, 0, false);
}
@Override
public void updateListOrder(@NonNull UUID entry, int listOrder) {
sendPacket(updateListOrder, entry, "", null, false, 0, 0, null, listOrder, false);
}
@Override
public void updateHat(@NonNull UUID entry, boolean showHat) {
sendPacket(updateHat, entry, "", null, false, 0, 0, null, 0, showHat);
}
@Override
public void addEntry0(@NonNull Entry entry) {
sendPacket(addPlayer, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.isListed(), entry.getLatency(),
entry.getGameMode(), entry.getDisplayName(), entry.getListOrder(), entry.isShowHat());
}
@Override
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
}
@Override
@Nullable
public Skin getSkin() {
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().getProperties().get(TEXTURES_PROPERTY);
if (properties.isEmpty()) return null; // Offline mode
Property property = properties.iterator().next();
return new Skin(property.value(), property.signature());
}
@Override
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundTabListPacket tablist) {
if (header == null || footer == null) return packet;
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
return new ClientboundTabListPacket(header.convert(), footer.convert());
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
boolean rewritePacket = false;
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : info.entries()) {
boolean rewriteEntry = false;
Component displayName = nmsData.displayName();
int latency = nmsData.latency();
int gameMode = nmsData.gameMode().getId();
boolean listed = nmsData.listed();
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
displayName = forcedDisplayName.convert();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
gameMode = 0;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY)) {
if (getForcedLatency() != null) {
latency = getForcedLatency();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
listed = false;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().getName());
}
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
nmsData.showHat(), nmsData.listOrder(), nmsData.chatSession()
) : nmsData);
}
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
}
return packet;
}
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
boolean listed, int latency, int gameMode, @Nullable TabComponent displayName, int listOrder, boolean showHat) {
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, new ClientboundPlayerInfoUpdatePacket.Entry(
id,
action.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER) ? createProfile(id, name, skin) : null,
listed,
latency,
GameType.byId(gameMode),
displayName == null ? null : displayName.convert(),
showHat,
listOrder,
null
));
sendPacket(packet);
}
/**
* Creates GameProfile from given parameters.
*
* @param id
* Profile ID
* @param name
* Profile name
* @param skin
* Player skin
* @return GameProfile from given parameters
*/
@NotNull
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
GameProfile profile = new GameProfile(id, name);
if (skin != null) {
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
}
return profile;
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
}
}
@@ -0,0 +1,30 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_4;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Implementation of TabListEntryTracker.
*/
public class NMSTabListEntryTracker extends TabListEntryTracker {
@Override
public void onPacketSend(@NotNull Object packet) {
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
for (UUID id : remove.profileIds()) {
tablistEntries.remove(id);
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
tablistEntries.add(nmsData.profileId());
}
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
}
repositories {
maven("https://jitpack.io") // YamlAssist
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.viaversion.com/")
maven {
name = "luck-repo"
url = uri("https://repo.lucko.me/")
content {
includeModule("me.lucko", "spark-api")
}
}
}
val version = "1.21.9-R0.1-SNAPSHOT"
dependencies {
implementation(projects.bukkit)
paperweight.paperDevBundle(version)
compileOnly("io.papermc.paper:paper-api:${version}")
}
tasks.compileJava {
options.release.set(21)
}
@@ -0,0 +1,84 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
import com.google.common.collect.ImmutableMultimap;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.shared.chat.TabStyle;
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
import me.neznamy.tab.shared.platform.TabList;
import net.minecraft.network.chat.*;
import net.minecraft.network.chat.contents.objects.AtlasSprite;
import net.minecraft.network.chat.contents.objects.PlayerSprite;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.component.ResolvableProfile;
import org.jetbrains.annotations.NotNull;
/**
* Component converter using direct mojang-mapped code.
*/
public class NMSComponentConverter extends ComponentConverter<Component> {
@Override
@NotNull
public Component newTextComponent(@NotNull String text) {
return Component.literal(text);
}
@Override
@NotNull
public Component newTranslatableComponent(@NotNull String key) {
return Component.translatable(key);
}
@Override
@NotNull
public Component newKeybindComponent(@NotNull String keybind) {
return Component.keybind(keybind);
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabAtlasSprite sprite) {
return Component.object(new AtlasSprite(ResourceLocation.parse(sprite.getAtlas()), ResourceLocation.parse(sprite.getSprite())));
}
@Override
@NotNull
public Component newObjectComponent(@NotNull TabPlayerSprite sprite) {
ResolvableProfile profile;
if (sprite.getId() != null) {
profile = ResolvableProfile.createUnresolved(sprite.getId());
} else if (sprite.getName() != null) {
profile = ResolvableProfile.createUnresolved(sprite.getName());
} else if (sprite.getSkin() != null) {
ImmutableMultimap.Builder<String, Property> builder = ImmutableMultimap.builder();
builder.put(TabList.TEXTURES_PROPERTY, new Property(TabList.TEXTURES_PROPERTY, sprite.getSkin().getValue(), sprite.getSkin().getSignature()));
profile = ResolvableProfile.createResolved(new GameProfile(NIL_UUID, "", new PropertyMap(builder.build())));
} else {
throw new IllegalStateException("Player head component does not have id, name or skin set");
}
return Component.object(new PlayerSprite(profile, sprite.isShowHat()));
}
@Override
public void applyStyle(@NotNull Component nmsComponent, @NotNull TabStyle modifier) {
Style style = Style.EMPTY
.withColor(modifier.getColor() == null ? null : TextColor.fromRgb(modifier.getColor().getRgb()))
.withBold(modifier.getBold())
.withItalic(modifier.getItalic())
.withUnderlined(modifier.getUnderlined())
.withStrikethrough(modifier.getStrikethrough())
.withObfuscated(modifier.getObfuscated())
.withFont(modifier.getFont() == null ? null : new FontDescription.Resource(ResourceLocation.parse(modifier.getFont())));
if (modifier.getShadowColor() != null) style = style.withShadowColor(modifier.getShadowColor()); // withShadowColor takes int instead of Integer, bug?
((MutableComponent)nmsComponent).setStyle(style);
}
@Override
public void addSibling(@NotNull Component parent, @NotNull Component child) {
((MutableComponent)parent).append(child);
}
}
@@ -0,0 +1,52 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
import io.netty.channel.Channel;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.provider.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Implementation provider using direct Mojang-mapped NMS code for paper 1.21.9 - 1.21.10.
*/
@Getter
public class NMSImplementationProvider implements ImplementationProvider {
@NotNull
private final ComponentConverter<?> componentConverter = new NMSComponentConverter();
@Override
@NotNull
public Scoreboard newScoreboard(@NotNull BukkitTabPlayer player) {
return new NMSPacketScoreboard(player);
}
@Override
@NotNull
public TabList newTabList(@NotNull BukkitTabPlayer player) {
return new NMSPacketTabList(player);
}
@Override
@NotNull
public Channel getChannel(@NotNull Player player) {
return ((CraftPlayer)player).getHandle().connection.connection.channel;
}
@Override
@NotNull
public TabListEntryTracker newTabListEntryTracker() {
return new NMSTabListEntryTracker();
}
@Override
public int getPing(@NotNull BukkitTabPlayer player) {
return player.getPlayer().getPing();
}
}
@@ -0,0 +1,176 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.decorators.SafeScoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.*;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.minecraft.world.scores.criteria.ObjectiveCriteria.RenderType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
/**
* Scoreboard implementation using direct mojang-mapped code.
*/
public class NMSPacketScoreboard extends SafeScoreboard<BukkitTabPlayer> {
private static final ChatFormatting[] formats = ChatFormatting.values();
private static final net.minecraft.world.scores.Team.CollisionRule[] collisions = net.minecraft.world.scores.Team.CollisionRule.values();
private static final net.minecraft.world.scores.Team.Visibility[] visibilities = net.minecraft.world.scores.Team.Visibility.values();
private static final Scoreboard dummyScoreboard = new Scoreboard();
private static final Field players = ReflectionUtils.getOnlyField(ClientboundSetPlayerTeamPacket.class, Collection.class);
/**
* Constructs new instance with given player.
*
* @param player
* Player this scoreboard will belong to
*/
public NMSPacketScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void registerObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = new net.minecraft.world.scores.Objective(
dummyScoreboard,
objective.getName(),
ObjectiveCriteria.DUMMY,
objective.getTitle().convert(),
RenderType.values()[objective.getHealthDisplay().ordinal()],
false,
objective.getNumberFormat() == null ? null : objective.getNumberFormat().toFixedFormat(FixedFormat::new)
);
objective.setPlatformObjective(obj);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.REGISTER));
}
@Override
public void setDisplaySlot(@NonNull Objective objective) {
sendPacket(new ClientboundSetDisplayObjectivePacket(
net.minecraft.world.scores.DisplaySlot.values()[objective.getDisplaySlot().ordinal()],
(net.minecraft.world.scores.Objective) objective.getPlatformObjective()
));
}
@Override
public void unregisterObjective(@NonNull Objective objective) {
sendPacket(new ClientboundSetObjectivePacket((net.minecraft.world.scores.Objective) objective.getPlatformObjective(), ObjectiveAction.UNREGISTER));
}
@Override
public void updateObjective(@NonNull Objective objective) {
net.minecraft.world.scores.Objective obj = (net.minecraft.world.scores.Objective) objective.getPlatformObjective();
obj.setDisplayName(objective.getTitle().convert());
obj.setRenderType(RenderType.values()[objective.getHealthDisplay().ordinal()]);
sendPacket(new ClientboundSetObjectivePacket(obj, ObjectiveAction.UPDATE));
}
@Override
public void setScore(@NonNull Score score) {
sendPacket(new ClientboundSetScorePacket(
score.getHolder(),
score.getObjective().getName(),
score.getValue(),
Optional.ofNullable(score.getDisplayName() == null ? null : score.getDisplayName().convert()),
Optional.ofNullable(score.getNumberFormat() == null ? null : score.getNumberFormat().toFixedFormat(FixedFormat::new))
));
}
@Override
public void removeScore(@NonNull Score score) {
sendPacket(new ClientboundResetScorePacket(score.getHolder(), score.getObjective().getName()));
}
@Override
@NotNull
public Object createTeam(@NonNull String name) {
return new PlayerTeam(dummyScoreboard, name);
}
@Override
public void registerTeam(@NonNull Team team) {
updateTeamProperties(team);
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.getPlayers().addAll(team.getPlayers());
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(t, true));
}
@Override
public void unregisterTeam(@NonNull Team team) {
sendPacket(ClientboundSetPlayerTeamPacket.createRemovePacket((PlayerTeam) team.getPlatformTeam()));
}
@Override
public void updateTeam(@NonNull Team team) {
updateTeamProperties(team);
sendPacket(ClientboundSetPlayerTeamPacket.createAddOrModifyPacket((PlayerTeam) team.getPlatformTeam(), false));
}
private void updateTeamProperties(@NonNull Team team) {
PlayerTeam t = (PlayerTeam) team.getPlatformTeam();
t.setAllowFriendlyFire((team.getOptions() & 0x01) != 0);
t.setSeeFriendlyInvisibles((team.getOptions() & 0x02) != 0);
t.setColor(formats[team.getColor().ordinal()]);
t.setCollisionRule(collisions[team.getCollision().ordinal()]);
t.setNameTagVisibility(visibilities[team.getVisibility().ordinal()]);
t.setPlayerPrefix(team.getPrefix().convert());
t.setPlayerSuffix(team.getSuffix().convert());
}
@Override
@SneakyThrows
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundSetDisplayObjectivePacket display) {
TAB.getInstance().getFeatureManager().onDisplayObjective(player, display.getSlot().ordinal(), display.getObjectiveName());
}
if (packet instanceof ClientboundSetObjectivePacket objective) {
TAB.getInstance().getFeatureManager().onObjective(player, objective.getMethod(), objective.getObjectiveName());
}
if (packet instanceof ClientboundSetPlayerTeamPacket team) {
int action = getMethod(team);
if (action != TeamAction.UPDATE) {
players.set(team, onTeamPacket(action, team.getName(), team.getPlayers() == null ? Collections.emptyList() : team.getPlayers()));
}
}
return packet;
}
private int getMethod(@NonNull ClientboundSetPlayerTeamPacket team) {
if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 0;
} else if (team.getTeamAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 1;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.ADD) {
return 3;
} else if (team.getPlayerAction() == ClientboundSetPlayerTeamPacket.Action.REMOVE) {
return 4;
} else {
return 2;
}
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
}
}
@@ -0,0 +1,206 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
import com.google.common.collect.ImmutableMultimap;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import lombok.NonNull;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.decorators.TrackedTabList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
import net.minecraft.world.level.GameType;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* TabList implementation using direct mojang-mapped code.
*/
public class NMSPacketTabList extends TrackedTabList<BukkitTabPlayer> {
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> addPlayer = EnumSet.allOf(ClientboundPlayerInfoUpdatePacket.Action.class);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateDisplayName = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateLatency = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateGameMode = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListed = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateListOrder = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LIST_ORDER);
private static final EnumSet<ClientboundPlayerInfoUpdatePacket.Action> updateHat = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT);
/**
* Constructs new instance.
*
* @param player
* Player this tablist will belong to
*/
public NMSPacketTabList(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void removeEntry(@NonNull UUID entry) {
sendPacket(new ClientboundPlayerInfoRemovePacket(Collections.singletonList(entry)));
}
@Override
public void updateDisplayName0(@NonNull UUID entry, @Nullable TabComponent displayName) {
sendPacket(updateDisplayName, entry, "", null, false, 0, 0, displayName, 0, false);
}
@Override
public void updateLatency(@NonNull UUID entry, int latency) {
sendPacket(updateLatency, entry, "", null, false, latency, 0, null, 0, false);
}
@Override
public void updateGameMode(@NonNull UUID entry, int gameMode) {
sendPacket(updateGameMode, entry, "", null, false, 0, gameMode, null, 0, false);
}
@Override
public void updateListed(@NonNull UUID entry, boolean listed) {
sendPacket(updateListed, entry, "", null, listed, 0, 0, null, 0, false);
}
@Override
public void updateListOrder(@NonNull UUID entry, int listOrder) {
sendPacket(updateListOrder, entry, "", null, false, 0, 0, null, listOrder, false);
}
@Override
public void updateHat(@NonNull UUID entry, boolean showHat) {
sendPacket(updateHat, entry, "", null, false, 0, 0, null, 0, showHat);
}
@Override
public void addEntry0(@NonNull Entry entry) {
sendPacket(addPlayer, entry.getUniqueId(), entry.getName(), entry.getSkin(), entry.isListed(), entry.getLatency(),
entry.getGameMode(), entry.getDisplayName(), entry.getListOrder(), entry.isShowHat());
}
@Override
public void setPlayerListHeaderFooter0(@NonNull TabComponent header, @NonNull TabComponent footer) {
sendPacket(new ClientboundTabListPacket(header.convert(), footer.convert()));
}
@Override
@Nullable
public Skin getSkin() {
Collection<Property> properties = ((CraftPlayer)player.getPlayer()).getProfile().properties().get(TEXTURES_PROPERTY);
if (properties.isEmpty()) return null; // Offline mode
Property property = properties.iterator().next();
return new Skin(property.value(), property.signature());
}
@Override
@NotNull
public Object onPacketSend(@NonNull Object packet) {
if (packet instanceof ClientboundTabListPacket tablist) {
if (header == null || footer == null) return packet;
if (tablist.header() != header.convert() || tablist.footer() != footer.convert()) {
return new ClientboundTabListPacket(header.convert(), footer.convert());
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket info) {
EnumSet<ClientboundPlayerInfoUpdatePacket.Action> actions = info.actions();
List<ClientboundPlayerInfoUpdatePacket.Entry> updatedList = new ArrayList<>();
boolean rewritePacket = false;
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : info.entries()) {
boolean rewriteEntry = false;
Component displayName = nmsData.displayName();
int latency = nmsData.latency();
int gameMode = nmsData.gameMode().getId();
boolean listed = nmsData.listed();
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME)) {
TabComponent forcedDisplayName = getForcedDisplayNames().get(nmsData.profileId());
if (forcedDisplayName != null && forcedDisplayName.convert() != displayName) {
displayName = forcedDisplayName.convert();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE)) {
if (getBlockedSpectators().contains(nmsData.profileId()) && gameMode == 3) {
gameMode = 0;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY)) {
if (getForcedLatency() != null) {
latency = getForcedLatency();
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED)) {
if (allPlayersHidden && nmsData.profileId().getMostSignificantBits() != 0) { // Filter out layout entries
listed = false;
rewriteEntry = rewritePacket = true;
}
}
if (actions.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
TAB.getInstance().getFeatureManager().onEntryAdd(player, nmsData.profileId(), nmsData.profile().name());
}
updatedList.add(rewriteEntry ? new ClientboundPlayerInfoUpdatePacket.Entry(
nmsData.profileId(), nmsData.profile(), listed, latency, GameType.byId(gameMode), displayName,
nmsData.showHat(), nmsData.listOrder(), nmsData.chatSession()
) : nmsData);
}
if (rewritePacket) return new ClientboundPlayerInfoUpdatePacket(actions, updatedList);
}
return packet;
}
private void sendPacket(@NonNull EnumSet<ClientboundPlayerInfoUpdatePacket.Action> action, @NonNull UUID id, @NonNull String name, @Nullable Skin skin,
boolean listed, int latency, int gameMode, @Nullable TabComponent displayName, int listOrder, boolean showHat) {
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(action, new ClientboundPlayerInfoUpdatePacket.Entry(
id,
action.contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER) ? createProfile(id, name, skin) : null,
listed,
latency,
GameType.byId(gameMode),
displayName == null ? null : displayName.convert(),
showHat,
listOrder,
null
));
sendPacket(packet);
}
/**
* Creates GameProfile from given parameters.
*
* @param id
* Profile ID
* @param name
* Profile name
* @param skin
* Player skin
* @return GameProfile from given parameters
*/
@NotNull
private GameProfile createProfile(@NonNull UUID id, @NonNull String name, @Nullable Skin skin) {
ImmutableMultimap.Builder<String, Property> builder = ImmutableMultimap.builder();
if (skin != null) {
builder.put(TabList.TEXTURES_PROPERTY,
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
}
return new GameProfile(id, name, new PropertyMap(builder.build()));
}
/**
* Sends the packet to the player.
*
* @param packet
* Packet to send
*/
private void sendPacket(@NotNull Packet<?> packet) {
((CraftPlayer)player.getPlayer()).getHandle().connection.send(packet);
}
}
@@ -0,0 +1,30 @@
package me.neznamy.tab.platforms.bukkit.paper_1_21_9;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Implementation of TabListEntryTracker.
*/
public class NMSTabListEntryTracker extends TabListEntryTracker {
@Override
public void onPacketSend(@NotNull Object packet) {
if (packet instanceof ClientboundPlayerInfoRemovePacket remove) {
for (UUID id : remove.profileIds()) {
tablistEntries.remove(id);
}
}
if (packet instanceof ClientboundPlayerInfoUpdatePacket update) {
if (update.actions().contains(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER)) {
for (ClientboundPlayerInfoUpdatePacket.Entry nmsData : update.entries()) {
tablistEntries.add(nmsData.profileId());
}
}
}
}
}
@@ -1,5 +1,6 @@
package me.neznamy.tab.platforms.bukkit;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.EventListener;
@@ -9,7 +10,6 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.jetbrains.annotations.NotNull;
@@ -17,31 +17,53 @@ import org.jetbrains.annotations.NotNull;
/**
* The core for bukkit forwarding events into all enabled features
*/
public class BukkitEventListener extends EventListener<Player> implements Listener {
@RequiredArgsConstructor
public class BukkitEventListener implements EventListener<Player>, Listener {
/** Platform reference */
private final BukkitPlatform platform;
/**
* Listens to player quit event.
*
* @param e
* Disconnect event
*/
@EventHandler(priority = EventPriority.HIGHEST)
public void onQuit(PlayerQuitEvent e) {
quit(e.getPlayer().getUniqueId());
}
/**
* Listens to player join event.
*
* @param e
* Join event
*/
@EventHandler(priority = EventPriority.LOW)
public void onJoin(PlayerJoinEvent e) {
TAB.getInstance().addTablistTracker(
e.getPlayer().getUniqueId(),
platform.getImplementationProvider().getChannel(e.getPlayer()),
platform.getImplementationProvider().newTabListEntryTracker()
);
join(e.getPlayer());
}
/**
* Listens to world change event.
*
* @param e
* World change event
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldChange(PlayerChangedWorldEvent e) {
worldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
}
@EventHandler
public void onCommand(PlayerCommandPreprocessEvent e) {
if (command(e.getPlayer().getUniqueId(), e.getMessage())) e.setCancelled(true);
}
@Override
@NotNull
public TabPlayer createPlayer(@NotNull Player player) {
return new BukkitTabPlayer((BukkitPlatform) TAB.getInstance().getPlatform(), player);
return new BukkitTabPlayer(platform, player);
}
}
@@ -1,27 +1,13 @@
package me.neznamy.tab.platforms.bukkit;
import com.mojang.authlib.GameProfile;
import io.netty.channel.Channel;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.features.injection.NettyPipelineInjector;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
/**
* Pipeline injection for bukkit
* Pipeline injection for Bukkit 1.8+.
*/
public class BukkitPipelineInjector extends NettyPipelineInjector {
@@ -33,148 +19,8 @@ public class BukkitPipelineInjector extends NettyPipelineInjector {
}
@Override
@Nullable
@NotNull
protected Channel getChannel(@NotNull TabPlayer player) {
BukkitTabPlayer bukkit = (BukkitTabPlayer) player;
NMSStorage nms = NMSStorage.getInstance();
try {
if (nms.CHANNEL != null) return (Channel) nms.CHANNEL.get(nms.NETWORK_MANAGER.get(bukkit.getPlayerConnection()));
} catch (IllegalAccessException exception) {
TAB.getInstance().getErrorManager().printError("Failed to get channel of " + bukkit.getName(), exception);
}
return null;
}
@Override
@SneakyThrows
public void onDisplayObjective(@NotNull TabPlayer player, @NotNull Object packet) {
int position;
if (BukkitReflection.is1_20_2Plus()) {
position = ((Enum<?>)PacketScoreboard.DisplayObjective_POSITION.get(packet)).ordinal();
} else {
position = PacketScoreboard.DisplayObjective_POSITION.getInt(packet);
}
TAB.getInstance().getFeatureManager().onDisplayObjective(player, position,
(String) PacketScoreboard.DisplayObjective_OBJECTIVE_NAME.get(packet));
}
@Override
@SneakyThrows
public void onObjective(@NotNull TabPlayer player, @NotNull Object packet) {
TAB.getInstance().getFeatureManager().onObjective(player,
PacketScoreboard.Objective_METHOD.getInt(packet),
(String) PacketScoreboard.Objective_OBJECTIVE_NAME.get(packet));
}
@Override
public boolean isDisplayObjective(@NotNull Object packet) {
return PacketScoreboard.DisplayObjectiveClass.isInstance(packet);
}
@Override
public boolean isObjective(@NotNull Object packet) {
return PacketScoreboard.ObjectivePacketClass.isInstance(packet);
}
@Override
public boolean isTeam(@NotNull Object packet) {
return PacketScoreboard.TeamPacketClass.isInstance(packet);
}
@Override
public boolean isPlayerInfo(@NotNull Object packet) {
return BukkitTabList.PlayerInfoClass.isInstance(packet);
}
@Override
@SneakyThrows
public void onPlayerInfo(@NotNull TabPlayer receiver, @NotNull Object packet) {
List<String> actions;
if (BukkitTabList.ClientboundPlayerInfoRemovePacket != null) {
//1.19.3+
actions = ((EnumSet<?>)BukkitTabList.ACTION.get(packet)).stream().map(Enum::name).collect(Collectors.toList());
} else {
//1.19.2-
actions = Collections.singletonList(BukkitTabList.ACTION.get(packet).toString());
}
List<Object> updatedList = new ArrayList<>();
for (Object nmsData : (List<?>) BukkitTabList.PLAYERS.get(packet)) {
GameProfile profile = (GameProfile) BukkitTabList.PlayerInfoData_Profile.get(nmsData);
UUID id;
if (BukkitReflection.is1_19_3Plus()) {
id = (UUID) BukkitTabList.PlayerInfoData_UUID.get(nmsData);
} else {
id = profile.getId();
}
Object displayName = null;
int latency = 0;
if (actions.contains(TabList.Action.UPDATE_DISPLAY_NAME.name()) || actions.contains(TabList.Action.ADD_PLAYER.name())) {
displayName = BukkitTabList.PlayerInfoData_DisplayName.get(nmsData);
IChatBaseComponent newDisplayName = TAB.getInstance().getFeatureManager().onDisplayNameChange(receiver, id);
if (newDisplayName != null) displayName = ((BukkitTabPlayer)receiver).getPlatform().toComponent(newDisplayName, receiver.getVersion());
if (!BukkitReflection.is1_19_3Plus()) BukkitTabList.PlayerInfoData_DisplayName.set(nmsData, displayName);
}
if (actions.contains(TabList.Action.UPDATE_LATENCY.name()) || actions.contains(TabList.Action.ADD_PLAYER.name())) {
latency = BukkitTabList.PlayerInfoData_Latency.getInt(nmsData);
latency = TAB.getInstance().getFeatureManager().onLatencyChange(receiver, id, latency);
if (!BukkitReflection.is1_19_3Plus()) BukkitTabList.PlayerInfoData_Latency.set(nmsData, latency);
}
if (actions.contains(TabList.Action.ADD_PLAYER.name())) {
TAB.getInstance().getFeatureManager().onEntryAdd(receiver, id, profile.getName());
}
if (BukkitReflection.is1_19_3Plus()) {
// 1.19.3 is using records, which do not allow changing final fields, need to rewrite the list entirely
updatedList.add(BukkitTabList.newPlayerInfoData.newInstance(
id,
profile,
BukkitTabList.PlayerInfoData_Listed.getBoolean(nmsData),
latency,
BukkitTabList.PlayerInfoData_GameMode.get(nmsData),
displayName,
BukkitTabList.PlayerInfoData_RemoteChatSession.get(nmsData)));
}
}
if (BukkitReflection.is1_19_3Plus()) {
BukkitTabList.PLAYERS.set(packet, updatedList);
}
}
@Override
public boolean isLogin(@NotNull Object packet) {
return false;
}
@SuppressWarnings("unchecked")
@Override
@SneakyThrows
public void modifyPlayers(@NotNull Object packetPlayOutScoreboardTeam) {
if (TAB.getInstance().getNameTagManager() == null) return;
int action = PacketScoreboard.TeamPacket_ACTION.getInt(packetPlayOutScoreboardTeam);
if (action == 1 || action == 2 || action == 4) return;
Collection<String> players = (Collection<String>) PacketScoreboard.TeamPacket_PLAYERS.get(packetPlayOutScoreboardTeam);
String teamName = (String) PacketScoreboard.TeamPacket_NAME.get(packetPlayOutScoreboardTeam);
if (players == null) return;
//creating a new list to prevent NoSuchFieldException in minecraft packet encoder when a player is removed
Collection<String> newList = new ArrayList<>();
for (String entry : players) {
TabPlayer p = getPlayer(entry);
if (p == null) {
newList.add(entry);
continue;
}
Sorting sorting = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
String expectedTeam = sorting.getShortTeamName(p);
if (expectedTeam == null) {
newList.add(entry);
continue;
}
if (!((NameTag)TAB.getInstance().getNameTagManager()).getDisableChecker().isDisabledPlayer(p) &&
!TAB.getInstance().getNameTagManager().hasTeamHandlingPaused(p) && !teamName.equals(expectedTeam)) {
logTeamOverride(teamName, p.getName(), expectedTeam);
} else {
newList.add(entry);
}
}
PacketScoreboard.TeamPacket_PLAYERS.set(packetPlayOutScoreboardTeam, newList);
return ((BukkitPlatform)player.getPlatform()).getImplementationProvider().getChannel(((BukkitTabPlayer) player).getPlayer());
}
}
@@ -1,10 +1,7 @@
package me.neznamy.tab.platforms.bukkit;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.platforms.bukkit.platform.FoliaPlatform;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.Bukkit;
@@ -17,42 +14,23 @@ public class BukkitTAB extends JavaPlugin {
@Override
public void onEnable() {
if (!isVersionSupported()) {
Bukkit.getPluginManager().disablePlugin(this);
return;
boolean folia = ReflectionUtils.classExists("io.papermc.paper.threadedregions.RegionizedServer");
try {
TAB.create(folia ? new FoliaPlatform(this) : new BukkitPlatform(this));
} catch (IllegalStateException e) {
Bukkit.getConsoleSender().sendMessage("§c[TAB] ================================================================================");
Bukkit.getConsoleSender().sendMessage("§c[TAB] Your server version (" + Bukkit.getBukkitVersion().split("-")[0] + ") is not supported.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] This jar only supports 1.7.10, 1.8.8, 1.9.4 - 1.15.x, 1.16.5, 1.17.1, 1.18.2 - 1.21.11.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you just updated to a new Minecraft version, check for TAB updates.");
Bukkit.getConsoleSender().sendMessage("§c[TAB] If you are using an unsupported 1.x version, use an older version of TAB (latest TAB 5.x supports all MC 1.x versions).");
Bukkit.getConsoleSender().sendMessage("§c[TAB] Thrown error message: " + e.getMessage());
Bukkit.getConsoleSender().sendMessage("§c[TAB] ================================================================================");
}
TAB.create(ReflectionUtils.classExists("io.papermc.paper.threadedregions.RegionizedServer") ?
new FoliaPlatform(this) : new BukkitPlatform(this));
}
@Override
public void onDisable() {
//null check due to compatibility check making instance not get set on unsupported versions
if (TAB.getInstance() != null) TAB.getInstance().unload();
}
/**
* Initializes all used NMS classes, constructors, fields and methods.
* Returns {@code true} if everything went successfully,
* {@code false} if anything went wrong.
*
* @return {@code true} if server version is compatible, {@code false} if not
*/
private boolean isVersionSupported() {
try {
NMSStorage.setInstance(new NMSStorage());
return true;
} catch (Exception ex) {
if (ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]) == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
Bukkit.getConsoleSender().sendMessage(String.format(
"%s[TAB] Your server version is not compatible. This plugin version was made for %s - %s. Disabling.",
EnumChatFormat.RED.getFormat(), ProtocolVersion.V1_5, ProtocolVersion.LATEST_KNOWN_VERSION
));
} else {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] A compatibility issue " +
"with your server was found. Unless you are running some really weird server software, this is a bug.");
}
return false;
}
if (TAB.getInstance() == null) return;
TAB.getInstance().unload();
}
}
@@ -1,8 +1,9 @@
package me.neznamy.tab.platforms.bukkit;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@@ -22,8 +23,8 @@ public class BukkitTabCommand implements CommandExecutor, TabCompleter {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (TAB.getInstance().isPluginDisabled()) {
for (String message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
sender.sendMessage(EnumChatFormat.color(message));
for (TabComponent message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
sender.sendMessage(((BukkitPlatform)TAB.getInstance().getPlatform()).toBukkitFormat(message));
}
} else {
TabPlayer p = null;
@@ -1,232 +0,0 @@
package me.neznamy.tab.platforms.bukkit;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.header.HeaderFooter;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabList.Entry.Builder;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;
/**
* TabList which support modifying many entries at once
* for significantly better performance. For 1.7 players,
* ViaVersion properly splits the packet into multiple, so
* we don't need to worry about that here.
* <p>
* This class does not support server versions of 1.7 and
* below, because of the massive differences in tab list
* and packet fields.
*/
@RequiredArgsConstructor
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitTabList implements TabList {
// NMS Fields
public static Class<?> PlayerInfoClass;
private static Constructor<?> newPlayerInfo;
public static Field ACTION;
public static Field PLAYERS;
private static Class<Enum> ActionClass;
public static Class<?> ClientboundPlayerInfoRemovePacket;
private static Class<?> RemoteChatSession$Data;
private static Constructor<?> newClientboundPlayerInfoRemovePacket;
private static Class<?> EntityPlayer;
public static Constructor<?> newPlayerInfoData;
public static Field PlayerInfoData_UUID;
public static Field PlayerInfoData_Profile;
public static Field PlayerInfoData_Latency;
public static Field PlayerInfoData_GameMode;
public static Field PlayerInfoData_DisplayName;
public static Field PlayerInfoData_Listed;
public static Field PlayerInfoData_RemoteChatSession;
private static Object[] gameModes;
@Getter
private static boolean available;
/** Player this TabList belongs to */
private final BukkitTabPlayer player;
public static void load() throws NoSuchMethodException, ClassNotFoundException {
if (BukkitReflection.getMinorVersion() < 8) return; // Not supported (yet?)
// Classes
Class<?> playerInfoDataClass;
Class<?> IChatBaseComponent;
Class<Enum> EnumGamemodeClass;
if (BukkitReflection.isMojangMapped()) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.Component");
EntityPlayer = Class.forName("net.minecraft.server.level.ServerPlayer");
EnumGamemodeClass = (Class<Enum>) Class.forName("net.minecraft.world.level.GameType");
if (BukkitReflection.is1_19_3Plus()) {
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
PlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
ActionClass = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action");
playerInfoDataClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry");
RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$Data");
} else {
PlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket");
ActionClass = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$Action");
playerInfoDataClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate");
}
} else if (BukkitReflection.getMinorVersion() >= 17) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
EntityPlayer = Class.forName("net.minecraft.server.level.EntityPlayer");
EnumGamemodeClass = (Class<Enum>) Class.forName("net.minecraft.world.level.EnumGamemode");
if (BukkitReflection.is1_19_3Plus()) {
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
PlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
ActionClass = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$a");
playerInfoDataClass = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$b");
RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$a");
} else {
PlayerInfoClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo");
ActionClass = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
playerInfoDataClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData");
}
} else {
IChatBaseComponent = BukkitReflection.getLegacyClass("IChatBaseComponent");
EntityPlayer = BukkitReflection.getLegacyClass("EntityPlayer");
PlayerInfoClass = BukkitReflection.getLegacyClass("PacketPlayOutPlayerInfo");
ActionClass = (Class<Enum>) BukkitReflection.getLegacyClass("PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
playerInfoDataClass = BukkitReflection.getLegacyClass("PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
EnumGamemodeClass = (Class<Enum>) BukkitReflection.getLegacyClass("EnumGamemode", "WorldSettings$EnumGamemode");
}
PLAYERS = ReflectionUtils.getOnlyField(PlayerInfoClass, List.class);
newPlayerInfoData = playerInfoDataClass.getConstructors()[0]; // #1105, a specific 1.8.8 fork has 2 constructors
PlayerInfoData_Profile = ReflectionUtils.getOnlyField(playerInfoDataClass, GameProfile.class);
PlayerInfoData_Latency = ReflectionUtils.getOnlyField(playerInfoDataClass, int.class);
PlayerInfoData_GameMode = ReflectionUtils.getOnlyField(playerInfoDataClass, EnumGamemodeClass);
PlayerInfoData_DisplayName = ReflectionUtils.getOnlyField(playerInfoDataClass, IChatBaseComponent);
if (BukkitReflection.is1_19_3Plus()) {
newClientboundPlayerInfoRemovePacket = ClientboundPlayerInfoRemovePacket.getConstructor(List.class);
newPlayerInfo = PlayerInfoClass.getConstructor(EnumSet.class, Collection.class);
ACTION = ReflectionUtils.getOnlyField(PlayerInfoClass, EnumSet.class);
PlayerInfoData_Listed = ReflectionUtils.getOnlyField(playerInfoDataClass, boolean.class);
PlayerInfoData_RemoteChatSession = ReflectionUtils.getOnlyField(playerInfoDataClass, RemoteChatSession$Data);
PlayerInfoData_UUID = ReflectionUtils.getOnlyField(playerInfoDataClass, UUID.class);
} else {
newPlayerInfo = PlayerInfoClass.getConstructor(ActionClass, Array.newInstance(EntityPlayer, 0).getClass());
ACTION = ReflectionUtils.getOnlyField(PlayerInfoClass, ActionClass);
}
gameModes = new Object[] {
Enum.valueOf(EnumGamemodeClass, "SURVIVAL"),
Enum.valueOf(EnumGamemodeClass, "CREATIVE"),
Enum.valueOf(EnumGamemodeClass, "ADVENTURE"),
Enum.valueOf(EnumGamemodeClass, "SPECTATOR")
};
available = true;
}
@Override
@SneakyThrows
public void removeEntry(@NotNull UUID entry) {
if (!available) return;
if (ClientboundPlayerInfoRemovePacket != null) {
//1.19.3+
player.sendPacket(newClientboundPlayerInfoRemovePacket.newInstance(Collections.singletonList(entry)));
} else {
//1.19.2-
player.sendPacket(createPacket(Action.REMOVE_PLAYER, new Builder(entry).build()));
}
}
@Override
public void updateDisplayName(@NotNull UUID entry, @Nullable IChatBaseComponent displayName) {
if (!available) return;
player.sendPacket(createPacket(Action.UPDATE_DISPLAY_NAME, new Builder(entry).displayName(displayName).build()));
}
@Override
public void updateLatency(@NotNull UUID entry, int latency) {
if (!available) return;
player.sendPacket(createPacket(Action.UPDATE_LATENCY, new Builder(entry).latency(latency).build()));
}
@Override
public void updateGameMode(@NotNull UUID entry, int gameMode) {
if (!available) return;
player.sendPacket(createPacket(Action.UPDATE_GAME_MODE, new Builder(entry).gameMode(gameMode).build()));
}
@Override
public void addEntry(@NotNull Entry entry) {
if (!available) return;
player.sendPacket(createPacket(Action.ADD_PLAYER, entry));
}
@Override
@SneakyThrows
public void setPlayerListHeaderFooter(@NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
if (HeaderFooter.getInstance() != null) HeaderFooter.getInstance().set(player, header, footer);
}
@SneakyThrows
@NotNull
private Object createPacket(@NotNull Action action, @NotNull Entry entry) {
Object packet;
List<Object> players = new ArrayList<>();
if (BukkitReflection.is1_19_3Plus()) {
EnumSet<?> actions;
if (action == Action.ADD_PLAYER) {
actions = EnumSet.allOf(ActionClass);
} else {
actions = EnumSet.of(Enum.valueOf(ActionClass, action.name()));
}
packet = newPlayerInfo.newInstance(actions, Collections.emptyList());
players.add(newPlayerInfoData.newInstance(
entry.getUniqueId(),
createProfile(entry.getUniqueId(), entry.getName(), entry.getSkin()),
true,
entry.getLatency(),
gameModes[entry.getGameMode()],
entry.getDisplayName() == null ? null : toComponent(entry.getDisplayName()),
null
));
} else {
packet = newPlayerInfo.newInstance(Enum.valueOf(ActionClass, action.name()), Array.newInstance(EntityPlayer, 0));
List<Object> parameters = new ArrayList<>();
if (newPlayerInfoData.getParameterTypes()[0] == PlayerInfoClass) {
parameters.add(packet);
}
parameters.add(createProfile(entry.getUniqueId(), entry.getName(), entry.getSkin()));
parameters.add(entry.getLatency());
parameters.add(gameModes[entry.getGameMode()]);
parameters.add(entry.getDisplayName() == null ? null : toComponent(entry.getDisplayName()));
if (BukkitReflection.getMinorVersion() >= 19) parameters.add(null);
players.add(newPlayerInfoData.newInstance(parameters.toArray()));
}
PLAYERS.set(packet, players);
return packet;
}
private Object toComponent(IChatBaseComponent component) {
return player.getPlatform().toComponent(component, player.getVersion());
}
@NotNull
private GameProfile createProfile(@NotNull UUID id, @Nullable String name, @Nullable Skin skin) {
GameProfile profile = new GameProfile(id, name == null ? "" : name);
if (skin != null) {
profile.getProperties().put(TabList.TEXTURES_PROPERTY,
new Property(TabList.TEXTURES_PROPERTY, skin.getValue(), skin.getSignature()));
}
return profile;
}
}
@@ -1,61 +1,21 @@
package me.neznamy.tab.platforms.bukkit;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import lombok.Getter;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.hook.LibsDisguisesHook;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.platforms.bukkit.nms.PacketEntityView;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import me.neznamy.tab.shared.backend.entityview.EntityView;
import me.neznamy.tab.shared.platform.bossbar.BossBar;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.platforms.bukkit.bossbar.EntityBossBar;
import me.neznamy.tab.platforms.bukkit.bossbar.BukkitBossBar;
import me.neznamy.tab.platforms.bukkit.bossbar.ViaBossBar;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.backend.BackendTabPlayer;
import me.neznamy.tab.shared.chat.component.TabComponent;
import org.bukkit.Statistic;
import org.bukkit.entity.Player;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* TabPlayer implementation for Bukkit platform
*/
@SuppressWarnings("deprecation")
@Getter
public class BukkitTabPlayer extends BackendTabPlayer {
/** Player's NMS handle (EntityPlayer), preloading for speed */
@NotNull
private final Object handle;
/** Player's connection for sending packets, preloading for speed */
@NotNull
private final Object playerConnection;
@NotNull
private final Scoreboard<BukkitTabPlayer> scoreboard = new PacketScoreboard(this);
@NotNull
private final TabList tabList = new BukkitTabList(this);
@NotNull
private final BossBar bossBar = TAB.getInstance().getServerVersion().getMinorVersion() >= 9 ?
new BukkitBossBar(this) : getVersion().getMinorVersion() >= 9 ? new ViaBossBar(this) : new EntityBossBar(this);
@NotNull
private final EntityView entityView = new PacketEntityView(this);
/**
* Constructs new instance with given bukkit player
*
@@ -64,11 +24,8 @@ public class BukkitTabPlayer extends BackendTabPlayer {
* @param p
* bukkit player
*/
@SneakyThrows
public BukkitTabPlayer(@NotNull BukkitPlatform platform, @NotNull Player p) {
super(platform, p, p.getUniqueId(), p.getName(), p.getWorld().getName());
handle = NMSStorage.getInstance().getHandle.invoke(player);
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
super(platform, p, p.getUniqueId(), p.getName(), p.getWorld().getName(), platform.getServerVersion().getNetworkId());
}
@Override
@@ -77,23 +34,13 @@ public class BukkitTabPlayer extends BackendTabPlayer {
}
@Override
@SneakyThrows
public int getPing() {
if (BukkitReflection.getMinorVersion() >= 17) {
return getPlayer().getPing();
}
return NMSStorage.getInstance().PING.getInt(handle);
}
@SneakyThrows
public void sendPacket(@NotNull Object nmsPacket) {
if (!getPlayer().isOnline()) return;
NMSStorage.getInstance().sendPacket.invoke(playerConnection, nmsPacket);
return getPlatform().getImplementationProvider().getPing(this);
}
@Override
public void sendMessage(@NotNull IChatBaseComponent message) {
getPlayer().sendMessage(BukkitUtils.toBukkitFormat(message, getVersion().getMinorVersion() >= 16));
public void sendMessage(@NotNull TabComponent message) {
getPlayer().sendMessage(getPlatform().toBukkitFormat(message));
}
@Override
@@ -106,42 +53,28 @@ public class BukkitTabPlayer extends BackendTabPlayer {
return LibsDisguisesHook.isDisguised(this);
}
@Override
@SneakyThrows
@Nullable
public TabList.Skin getSkin() {
Collection<Property> col = ((GameProfile)NMSStorage.getInstance().getProfile.invoke(handle)).getProperties().get(TabList.TEXTURES_PROPERTY);
if (col.isEmpty()) return null; //offline mode
Property property = col.iterator().next();
if (BukkitReflection.is1_20_2Plus()) {
return new TabList.Skin(
(String) property.getClass().getMethod("value").invoke(property),
(String) property.getClass().getMethod("signature").invoke(property)
);
} else {
return new TabList.Skin(property.getValue(), property.getSignature());
}
}
@Override
@NotNull
public Player getPlayer() {
return (Player) player;
}
@Override
public boolean isOnline() {
return getPlayer().isOnline();
}
@Override
public BukkitPlatform getPlatform() {
return (BukkitPlatform) platform;
}
@Override
public boolean isVanished() {
return getPlayer().getMetadata("vanished").stream().anyMatch(MetadataValue::asBoolean);
public boolean isVanished0() {
for (MetadataValue v : getPlayer().getMetadata("vanished")) {
if (v.asBoolean()) return true;
}
return false;
}
@Override
public int getDeaths() {
return getPlayer().getStatistic(Statistic.DEATHS);
}
@Override
@@ -1,76 +0,0 @@
package me.neznamy.tab.platforms.bukkit;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* Class containing static util methods used on Bukkit.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BukkitUtils {
/**
* Returns online players from Bukkit API. This requires reflection, as return type changed in 1.8,
* and we want to avoid errors.
*
* @return Online players from Bukkit API.
*/
@SuppressWarnings("unchecked")
@SneakyThrows
@NotNull
public static Player[] getOnlinePlayers() {
Object players = Bukkit.class.getMethod("getOnlinePlayers").invoke(null);
if (players instanceof Player[]) {
//1.7-
return (Player[]) players;
} else {
//1.8+
return ((Collection<Player>)players).toArray(new Player[0]);
}
}
/**
* Converts component to legacy string using bukkit RGB format if supported by both server and client.
* If not, closest legacy color is used instead.
*
* @param component
* Component to convert
* @param rgbClient
* Whether client accepts RGB colors or not.
* @return Converted string using bukkit color format
*/
@NotNull
public static String toBukkitFormat(@NotNull IChatBaseComponent component, boolean rgbClient) {
StringBuilder sb = new StringBuilder();
if (component.getModifier().getColor() != null) {
if (BukkitReflection.getMinorVersion() >= 16 && rgbClient) {
String hexCode = component.getModifier().getColor().getHexCode();
char c = EnumChatFormat.COLOR_CHAR;
sb.append(c).append("x").append(c).append(hexCode.charAt(0)).append(c).append(hexCode.charAt(1))
.append(c).append(hexCode.charAt(2)).append(c).append(hexCode.charAt(3))
.append(c).append(hexCode.charAt(4)).append(c).append(hexCode.charAt(5));
} else {
sb.append(component.getModifier().getColor().getLegacyColor().getFormat());
}
}
if (component.getModifier().isBold()) sb.append(EnumChatFormat.BOLD.getFormat());
if (component.getModifier().isStrikethrough()) sb.append(EnumChatFormat.STRIKETHROUGH.getFormat());
if (component.getModifier().isItalic()) sb.append(EnumChatFormat.ITALIC.getFormat());
if (component.getModifier().isObfuscated()) sb.append(EnumChatFormat.OBFUSCATED.getFormat());
if (component.getModifier().isUnderlined()) sb.append(EnumChatFormat.UNDERLINE.getFormat());
if (component.getText() != null) sb.append(component.getText());
for (IChatBaseComponent extra : component.getExtra()) {
sb.append(toBukkitFormat(extra, rgbClient));
}
return sb.toString();
}
}
@@ -1,17 +1,17 @@
package me.neznamy.tab.platforms.bukkit.bossbar;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
import me.neznamy.tab.platforms.bukkit.BukkitUtils;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.bossbar.BossBar;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.platform.decorators.SafeBossBar;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.bukkit.boss.BossBar;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
@@ -19,71 +19,58 @@ import java.util.UUID;
* to allow 1.8 players, it will handle the entity and teleporting by itself.
*/
@RequiredArgsConstructor
public class BukkitBossBar implements BossBar {
public class BukkitBossBar extends SafeBossBar<BossBar> {
/** Color array for fast access */
private static final org.bukkit.boss.BarColor[] colors = new org.bukkit.boss.BarColor[] {
org.bukkit.boss.BarColor.PINK,
org.bukkit.boss.BarColor.BLUE,
org.bukkit.boss.BarColor.RED,
org.bukkit.boss.BarColor.GREEN,
org.bukkit.boss.BarColor.YELLOW,
org.bukkit.boss.BarColor.PURPLE,
org.bukkit.boss.BarColor.WHITE
};
/** Flag indicating whether this class is available or not */
@Getter
private static final boolean available = ReflectionUtils.classExists("org.bukkit.boss.BossBar");
/** Style array because names do not match */
private static final org.bukkit.boss.BarStyle[] styles = available ? org.bukkit.boss.BarStyle.values() : null;
/** Style array for fast access */
private static final org.bukkit.boss.BarStyle[] styles = new org.bukkit.boss.BarStyle[] {
org.bukkit.boss.BarStyle.SOLID,
org.bukkit.boss.BarStyle.SEGMENTED_6,
org.bukkit.boss.BarStyle.SEGMENTED_10,
org.bukkit.boss.BarStyle.SEGMENTED_12,
org.bukkit.boss.BarStyle.SEGMENTED_20
};
/** Player this handler belongs to */
@NotNull
private final BukkitTabPlayer player;
/** Bukkit BossBars the player can currently see */
@NotNull
private final Map<UUID, org.bukkit.boss.BossBar> bossBars = new HashMap<>();
@Override
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
if (bossBars.containsKey(id)) return;
org.bukkit.boss.BossBar bar = Bukkit.createBossBar(
BukkitUtils.toBukkitFormat(IChatBaseComponent.optimizedComponent(title), player.getVersion().getMinorVersion() >= 16),
colors[color.ordinal()],
@NotNull
public BossBar constructBossBar(@NotNull UUID id, @NotNull TabComponent title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
BossBar bar = Bukkit.createBossBar(
player.getPlatform().toBukkitFormat(title),
org.bukkit.boss.BarColor.valueOf(color.name()),
styles[style.ordinal()]
);
bar.setProgress(progress);
bar.addPlayer(player.getPlayer());
bossBars.put(id, bar);
return bar;
}
@Override
public void update(@NotNull UUID id, @NotNull String title) {
bossBars.get(id).setTitle(BukkitUtils.toBukkitFormat(IChatBaseComponent.optimizedComponent(title), player.getVersion().getMinorVersion() >= 16));
public void show(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
bar.getBossBar().addPlayer(player.getPlayer());
}
@Override
public void update(@NotNull UUID id, float progress) {
bossBars.get(id).setProgress(progress);
public void updateTitle(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
bar.getBossBar().setTitle(player.getPlatform().toBukkitFormat(bar.getTitle()));
}
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
bossBars.get(id).setStyle(styles[style.ordinal()]);
public void updateProgress(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
bar.getBossBar().setProgress(bar.getProgress());
}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
bossBars.get(id).setColor(colors[color.ordinal()]);
public void updateStyle(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
bar.getBossBar().setStyle(styles[bar.getStyle().ordinal()]);
}
@Override
public void remove(@NotNull UUID id) {
bossBars.remove(id).removePlayer(player.getPlayer());
public void updateColor(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
bar.getBossBar().setColor(org.bukkit.boss.BarColor.valueOf(bar.getColor().name()));
}
@Override
public void hide(SafeBossBar<BossBar>.@NotNull BossBarInfo bar) {
bar.getBossBar().removePlayer(player.getPlayer());
}
}
@@ -1,65 +0,0 @@
package me.neznamy.tab.platforms.bukkit.bossbar;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.platform.bossbar.BossBar;
import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.DataWatcher;
import me.neznamy.tab.shared.backend.Location;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* BossBar using wither entity for <1.9 players on <1.9 servers.
* Additional logic, such as teleporting the entity must be done
* separately, as this class does not handle it.
*/
@RequiredArgsConstructor
public class EntityBossBar implements BossBar {
/** Player this handler belongs to */
@NotNull
private final BukkitTabPlayer player;
@Override
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
DataWatcher w = new DataWatcher();
float health = 300*progress;
if (health == 0) health = 1;
w.setHealth(health);
w.setCustomName(title, player.getVersion());
w.setEntityFlags((byte) 32);
w.setWitherInvulnerableTime(880); // Magic number
player.getEntityView().spawnEntity(id.hashCode(), new UUID(0, 0), EntityType.WITHER, new Location(0, 0, 0), w);
}
@Override
public void update(@NotNull UUID id, @NotNull String title) {
DataWatcher w = new DataWatcher();
w.setCustomName(title, player.getVersion());
player.getEntityView().updateEntityMetadata(id.hashCode(), w);
}
@Override
public void update(@NotNull UUID id, float progress) {
DataWatcher w = new DataWatcher();
float health = 300*progress;
if (health == 0) health = 1;
w.setHealth(health);
player.getEntityView().updateEntityMetadata(id.hashCode(), w);
}
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {/*Added in 1.9*/}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {/*Added in 1.9*/}
@Override
public void remove(@NotNull UUID id) {
player.getEntityView().destroyEntities(id.hashCode());
}
}
@@ -1,89 +1,70 @@
package me.neznamy.tab.platforms.bukkit.bossbar;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.legacy.bossbar.BossBar;
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.bossbar.BossBar;
import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.platform.decorators.SafeBossBar;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Handler for 1.9+ players on 1.8 server using ViaVersion API.
*/
@RequiredArgsConstructor
public class ViaBossBar implements BossBar {
/** Color array for fast access */
private static final BossColor[] colors = new BossColor[] {
BossColor.PINK,
BossColor.BLUE,
BossColor.RED,
BossColor.GREEN,
BossColor.YELLOW,
BossColor.PURPLE,
BossColor.WHITE
};
public class ViaBossBar extends SafeBossBar<BossBar> {
/** Style array for fast access */
private static final BossStyle[] styles = new BossStyle[] {
BossStyle.SOLID,
BossStyle.SEGMENTED_6,
BossStyle.SEGMENTED_10,
BossStyle.SEGMENTED_12,
BossStyle.SEGMENTED_20
};
private static final BossStyle[] styles = BossStyle.values();
/** Player this handler belongs to */
@NotNull
private final BukkitTabPlayer player;
/** ViaVersion BossBars this 1.9+ player can see on 1.8 server */
@NotNull
private final Map<UUID, com.viaversion.viaversion.api.legacy.bossbar.BossBar> viaBossBars = new HashMap<>();
@Override
public void create(@NotNull UUID id, @NotNull String title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
if (viaBossBars.containsKey(id)) return;
com.viaversion.viaversion.api.legacy.bossbar.BossBar bar = Via.getAPI().legacyAPI().createLegacyBossBar(
IChatBaseComponent.optimizedComponent(title).toString(player.getVersion()),
@NotNull
public BossBar constructBossBar(@NotNull UUID id, @NotNull TabComponent title, float progress, @NotNull BarColor color, @NotNull BarStyle style) {
return Via.getAPI().legacyAPI().createLegacyBossBar(
title.toLegacyText(),
progress,
colors[color.ordinal()],
BossColor.valueOf(color.name()),
styles[style.ordinal()]
);
viaBossBars.put(id, bar);
bar.addPlayer(player.getPlayer().getUniqueId());
}
@Override
public void update(@NotNull UUID id, @NotNull String title) {
viaBossBars.get(id).setTitle(IChatBaseComponent.optimizedComponent(title).toString(player.getVersion()));
public void show(@NotNull BossBarInfo bar) {
bar.getBossBar().addPlayer(player.getPlayer().getUniqueId());
}
@Override
public void update(@NotNull UUID id, float progress) {
viaBossBars.get(id).setHealth(progress);
public void updateTitle(@NotNull BossBarInfo bar) {
bar.getBossBar().setTitle(bar.getTitle().toLegacyText());
}
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
viaBossBars.get(id).setStyle(styles[style.ordinal()]);
public void updateProgress(@NotNull BossBarInfo bar) {
bar.getBossBar().setHealth(bar.getProgress());
}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
viaBossBars.get(id).setColor(colors[color.ordinal()]);
public void updateStyle(@NotNull BossBarInfo bar) {
bar.getBossBar().setStyle(styles[bar.getStyle().ordinal()]);
}
@Override
public void remove(@NotNull UUID id) {
viaBossBars.remove(id).removePlayer(player.getPlayer().getUniqueId());
public void updateColor(@NotNull BossBarInfo bar) {
bar.getBossBar().setColor(BossColor.valueOf(bar.getColor().name()));
}
@Override
public void hide(@NotNull BossBarInfo bar) {
bar.getBossBar().removePlayer(player.getPlayer().getUniqueId());
}
}
@@ -1,167 +0,0 @@
package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.DataWatcher;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Pose;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* The core class for unlimited NameTag mode on Bukkit
*/
public class BukkitNameTagX extends BackendNameTagX implements Listener {
public BukkitNameTagX(@NotNull JavaPlugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSneak(PlayerToggleSneakEvent e) {
sneak(e.getPlayer().getUniqueId(), e.isSneaking());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onRespawn(PlayerRespawnEvent e) {
respawn(e.getPlayer().getUniqueId());
}
@Override
public double getDistance(@NotNull TabPlayer player1, @NotNull TabPlayer player2) {
Location loc1 = ((Player) player1.getPlayer()).getLocation();
Location loc2 = ((Player) player2.getPlayer()).getLocation();
return Math.sqrt(Math.pow(loc1.getX()-loc2.getX(), 2) + Math.pow(loc1.getZ()-loc2.getZ(), 2));
}
@Override
public boolean areInSameWorld(@NotNull TabPlayer player1, @NotNull TabPlayer player2) {
return ((Player) player1.getPlayer()).getWorld() == ((Player) player2.getPlayer()).getWorld();
}
@Override
public boolean canSee(@NotNull TabPlayer viewer, @NotNull TabPlayer target) {
return ((Player)viewer.getPlayer()).canSee((Player) target.getPlayer());
}
@Override
public void unregisterListener() {
HandlerList.unregisterAll(this);
}
@SuppressWarnings("deprecation")
@Override
@NotNull
public List<Integer> getPassengers(@NotNull Object entity) {
Entity vehicle = (Entity) entity;
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 11) {
return vehicle.getPassengers().stream().map(Entity::getEntityId).collect(Collectors.toList());
} else {
if (vehicle.getPassenger() != null) {
return Collections.singletonList(vehicle.getPassenger().getEntityId());
} else {
return Collections.emptyList();
}
}
}
@Override
@Nullable
public Object getVehicle(@NotNull TabPlayer player) {
return ((Player)player.getPlayer()).getVehicle();
}
@Override
public int getEntityId(@NotNull Object entity) {
return ((Entity)entity).getEntityId();
}
@Override
@NotNull
public String getEntityType(@NotNull Object entity) {
return ((Entity) entity).getType().toString().toLowerCase();
}
@Override
public boolean isSneaking(@NotNull TabPlayer player) {
return ((Player)player.getPlayer()).isSneaking();
}
@Override
public boolean isSwimming(@NotNull TabPlayer player) {
Player p = (Player) player.getPlayer();
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 14 && p.getPose() == Pose.SWIMMING) return true;
return TAB.getInstance().getServerVersion().getMinorVersion() == 13 && p.isSwimming();
}
@Override
public boolean isGliding(@NotNull TabPlayer player) {
return TAB.getInstance().getServerVersion().getMinorVersion() >= 9 && ((Player)player.getPlayer()).isGliding();
}
@Override
public boolean isSleeping(@NotNull TabPlayer player) {
return ((Player)player.getPlayer()).isSleeping();
}
@Override
@NotNull
public Object getArmorStandType() {
return EntityType.ARMOR_STAND;
}
@Override
public double getX(@NotNull TabPlayer player) {
return ((Player)player.getPlayer()).getLocation().getX();
}
@Override
public double getY(@NotNull Object entity) {
return ((Entity)entity).getLocation().getY();
}
@Override
public double getZ(@NotNull TabPlayer player) {
return ((Player)player.getPlayer()).getLocation().getZ();
}
@Override
@NotNull
public EntityData createDataWatcher(@NotNull TabPlayer viewer, byte flags, @NotNull String displayName, boolean nameVisible) {
DataWatcher datawatcher = new DataWatcher();
datawatcher.setEntityFlags(flags);
datawatcher.setCustomName(displayName, viewer.getVersion());
datawatcher.setCustomNameVisible(nameVisible);
datawatcher.setArmorStandFlags((byte)16);
return datawatcher;
}
@Override
public void runInEntityScheduler(@NotNull Object entity, @NotNull Runnable task) {
((BukkitPlatform)TAB.getInstance().getPlatform()).runEntityTask((Entity) entity, task);
}
@Override
public boolean isDead(@NotNull TabPlayer player) {
return ((BukkitTabPlayer)player).getPlayer().isDead();
}
}
@@ -3,57 +3,49 @@ package me.neznamy.tab.platforms.bukkit.features;
import lombok.Getter;
import me.clip.placeholderapi.PlaceholderAPI;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.neznamy.tab.shared.TabConstants;
import me.clip.placeholderapi.expansion.Relational;
import me.neznamy.tab.api.placeholder.Placeholder;
import me.neznamy.tab.shared.ProjectVariables;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
import me.neznamy.tab.shared.placeholders.types.RelationalPlaceholderImpl;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
/**
* TAB's expansion for PlaceholderAPI
*/
@Getter
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion {
/** Map holding all values for all players for easy and high-performance access */
@NotNull
private final WeakHashMap<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion, Relational {
/** List of all placeholders offered by the plugin for command suggestions */
@NotNull
private final List<String> placeholders = Arrays.asList(
private final List<String> placeholders = Collections.unmodifiableList(Arrays.asList(
"%tab_tabprefix%",
"%tab_tabsuffix%",
"%tab_tagprefix%",
"%tab_tagsuffix%",
"%tab_customtabname%",
"%tab_customtagname%",
"%tab_belowname%",
"%tab_abovename%",
"%tab_tabprefix_raw%",
"%tab_tabsuffix_raw%",
"%tab_tagprefix_raw%",
"%tab_tagsuffix_raw%",
"%tab_customtabname_raw%",
"%tab_customtagname_raw%",
"%tab_belowname_raw%",
"%tab_abovename_raw%",
"%tab_scoreboard_name%",
"%tab_scoreboard_visible%",
"%tab_bossbar_visible%",
"%tab_nametag_preview%",
"%tab_nametag_visibility%",
"%tab_replace_<placeholder>%",
"%tab_placeholder_<placeholder>%"
);
));
@Override
public boolean persist() {
@@ -63,19 +55,19 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
@Override
@NotNull
public String getAuthor() {
return TabConstants.PLUGIN_AUTHOR;
return ProjectVariables.PLUGIN_AUTHOR;
}
@Override
@NotNull
public String getIdentifier() {
return TabConstants.PLUGIN_ID;
return ProjectVariables.PLUGIN_ID;
}
@Override
@NotNull
public String getVersion() {
return TabConstants.PLUGIN_VERSION;
return ProjectVariables.PLUGIN_VERSION;
}
@Override
@@ -92,24 +84,65 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
String textBefore;
do {
textBefore = text;
for (String placeholder : TAB.getInstance().getPlaceholderManager().detectPlaceholders(text)) {
for (String placeholder : PlaceholderManagerImpl.detectPlaceholders(text)) {
text = text.replace(placeholder, TAB.getInstance().getPlaceholderManager().findReplacement(placeholder,
PlaceholderAPI.setPlaceholders(player, placeholder)));
}
} while (!textBefore.equals(text));
return text;
}
if (identifier.startsWith("placeholder_")) {
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", TAB.getInstance().getPlaceholderManager());
}
if (player == null) return "<Player cannot be null>";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
return values.get(p).get(identifier);
if (p == null) return "<Player is not loaded>";
if (identifier.startsWith("placeholder_")) {
String requestedPlaceholder = "%" + identifier.substring("placeholder_".length()) + "%";
PlaceholderManagerImpl pm = TAB.getInstance().getPlaceholderManager();
pm.addUsedPlaceholder(requestedPlaceholder, pm);
return pm.getPlaceholder(requestedPlaceholder).parse(p);
}
return p.expansionData.getValue(identifier);
}
@Override
public void setValue(@NotNull TabPlayer player, @NotNull String key, @NotNull String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
public String onPlaceholderRequest(@NotNull Player viewer, @NotNull Player target, @NotNull String identifier) {
if (identifier.startsWith("replace_")) {
String text = "%" + identifier.substring(8) + "%";
String textBefore;
do {
textBefore = text;
for (String placeholder : PlaceholderManagerImpl.detectPlaceholders(text)) {
text = text.replace(placeholder, TAB.getInstance().getPlaceholderManager().findReplacement(placeholder,
PlaceholderAPI.setRelationalPlaceholders(viewer, target, placeholder)));
}
} while (!textBefore.equals(text));
return text;
}
if (identifier.startsWith("placeholder_")) {
String requestedPlaceholder = "%" + identifier.substring("placeholder_".length()) + "%";
PlaceholderManagerImpl pm = TAB.getInstance().getPlaceholderManager();
pm.addUsedPlaceholder(requestedPlaceholder, pm);
Placeholder placeholder = pm.getPlaceholder(requestedPlaceholder);
if (placeholder instanceof RelationalPlaceholderImpl) {
RelationalPlaceholderImpl rel = (RelationalPlaceholderImpl) placeholder;
TabPlayer v = TAB.getInstance().getPlayer(viewer.getUniqueId());
TabPlayer t = TAB.getInstance().getPlayer(target.getUniqueId());
if (v == null || t == null) return "<Player is not loaded>";
return rel.getLastValue(v, t);
} else {
return "<Not a relational placeholder: " + requestedPlaceholder + ">";
}
}
return "<Unknown identifier: \"" + identifier + "\">";
}
@Override
@SuppressWarnings({"UnstableApiUsage", "deprecation"})
public void unregisterExpansion() {
if (ReflectionUtils.methodExists(PlaceholderExpansion.class, "unregister")) {
// Added in 2.10.7 (Jul 28, 2020)
unregister();
} else {
PlaceholderAPI.unregisterExpansion(this);
}
}
}
@@ -1,13 +1,15 @@
package me.neznamy.tab.platforms.bukkit.features;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import me.neznamy.tab.platforms.bukkit.BukkitUtils;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.PerWorldPlayerListConfiguration;
import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.TabFeature;
import me.neznamy.tab.shared.features.types.UnLoadable;
import me.neznamy.tab.shared.features.types.VanishListener;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -15,63 +17,88 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.shared.features.types.TabFeature;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map.Entry;
/**
* Per-world-PlayerList feature handler
*/
@SuppressWarnings("deprecation")
public class PerWorldPlayerList extends TabFeature implements Listener, Loadable, UnLoadable {
public class PerWorldPlayerList extends TabFeature implements Listener, Loadable, UnLoadable, VanishListener {
/** Reference to platform */
@NotNull
private final BukkitPlatform platform;
/** Config options */
private final boolean allowBypass = TAB.getInstance().getConfig().getBoolean("per-world-playerlist.allow-bypass-permission", false);
private final List<String> ignoredWorlds = TAB.getInstance().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignored_world", "build"));
private final Map<String, List<String>> sharedWorlds = TAB.getInstance().getConfig().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
@NotNull
private final PerWorldPlayerListConfiguration configuration;
/** Check for presence of modern (1.12.2+) methods that take plugin as argument to avoid conflict */
private final boolean modernMethodsAvailable = ReflectionUtils.methodExists(Player.class, "hidePlayer", Plugin.class, Player.class);
/**
* Constructs new instance and registers events
* Constructs new instance and registers events.
*
* @param plugin
* Plugin instance to register events
* @param platform
* Platform reference
* @param configuration
* Feature configuration
*/
public PerWorldPlayerList(JavaPlugin plugin) {
public PerWorldPlayerList(@NotNull JavaPlugin plugin, @NotNull BukkitPlatform platform, @NotNull PerWorldPlayerListConfiguration configuration) {
this.configuration = configuration;
this.platform = platform;
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@Override
public void load() {
for (Player p : BukkitUtils.getOnlinePlayers()) {
for (Player p : platform.getOnlinePlayers()) {
checkPlayer(p);
}
}
@Override
public void unload() {
for (Player p : BukkitUtils.getOnlinePlayers()) {
for (Player pl : BukkitUtils.getOnlinePlayers()) {
p.showPlayer(pl);
for (Player p : platform.getOnlinePlayers()) {
for (Player pl : platform.getOnlinePlayers()) {
showPlayer(p, pl);
}
}
HandlerList.unregisterAll(this);
}
/**
* Join event listener to synchronously perform hiding.
*
* @param e
* Join event
*/
@EventHandler
public void onJoin(PlayerJoinEvent e) {
long time = System.nanoTime();
checkPlayer(e.getPlayer());
TAB.getInstance().getCPUManager().addTime(this, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
}
/**
* World change listener to synchronously perform player showing/hiding.
*
* @param e
* World change event
*/
@EventHandler
public void onWorldChange(PlayerChangedWorldEvent e) {
long time = System.nanoTime();
checkPlayer(e.getPlayer());
TAB.getInstance().getCPUManager().addTime(this, TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
}
/**
@@ -83,12 +110,34 @@ public class PerWorldPlayerList extends TabFeature implements Listener, Loadable
* Player to update
*/
private void checkPlayer(@NotNull Player p) {
for (Player all : BukkitUtils.getOnlinePlayers()) {
for (Player all : platform.getOnlinePlayers()) {
if (all == p) continue;
if (!shouldSee(p, all) && p.canSee(all)) p.hidePlayer(all);
if (shouldSee(p, all) && !p.canSee(all)) p.showPlayer(all);
if (!shouldSee(all, p) && all.canSee(p)) all.hidePlayer(p);
if (shouldSee(all, p) && !all.canSee(p)) all.showPlayer(p);
if (shouldSee(p, all)) {
showPlayer(p, all);
} else {
hidePlayer(p, all);
}
if (shouldSee(all, p)) {
showPlayer(all, p);
} else {
hidePlayer(all, p);
}
}
}
private void hidePlayer(@NotNull Player viewer, @NotNull Player target) {
if (modernMethodsAvailable) {
viewer.hidePlayer(platform.getPlugin(), target);
} else {
viewer.hidePlayer(target);
}
}
private void showPlayer(@NotNull Player viewer, @NotNull Player target) {
if (modernMethodsAvailable) {
viewer.showPlayer(platform.getPlugin(), target);
} else {
viewer.showPlayer(target);
}
}
@@ -102,10 +151,13 @@ public class PerWorldPlayerList extends TabFeature implements Listener, Loadable
*/
private boolean shouldSee(@NotNull Player viewer, @NotNull Player target) {
if (target == viewer) return true;
if ((allowBypass && viewer.hasPermission(TabConstants.Permission.PER_WORLD_PLAYERLIST_BYPASS)) || ignoredWorlds.contains(viewer.getWorld().getName())) return true;
for (MetadataValue v : target.getMetadata("vanished")) {
if (v.asBoolean() && !viewer.hasPermission(TabConstants.Permission.SEE_VANISHED)) return false;
}
if ((configuration.isAllowBypassPermission() && viewer.hasPermission(TabConstants.Permission.PER_WORLD_PLAYERLIST_BYPASS)) || configuration.getIgnoredWorlds().contains(viewer.getWorld().getName())) return true;
String viewerWorldGroup = viewer.getWorld().getName() + "-default"; //preventing unwanted behavior when some group is called exactly like a world
String targetWorldGroup = target.getWorld().getName() + "-default";
for (Entry<String, List<String>> group : sharedWorlds.entrySet()) {
for (Entry<String, List<String>> group : configuration.getSharedWorlds().entrySet()) {
if (group.getValue() != null) {
if (group.getValue().contains(viewer.getWorld().getName())) viewerWorldGroup = group.getKey();
if (group.getValue().contains(target.getWorld().getName())) targetWorldGroup = group.getKey();
@@ -114,9 +166,14 @@ public class PerWorldPlayerList extends TabFeature implements Listener, Loadable
return viewerWorldGroup.equals(targetWorldGroup);
}
@Override
@NotNull
@Override
public String getFeatureName() {
return "Per world PlayerList";
}
@Override
public void onVanishStatusChange(@NotNull TabPlayer player) {
platform.runSync((Player) player.getPlayer(), () -> checkPlayer((Player) player.getPlayer()));
}
}
@@ -1,87 +0,0 @@
package me.neznamy.tab.platforms.bukkit.features;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.api.bossbar.BossBar;
import me.neznamy.tab.shared.features.types.WorldSwitchListener;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.backend.BackendTabPlayer;
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
/**
* An additional class with additional code for &lt;1.9 servers due to an entity being required
*/
@RequiredArgsConstructor
public class WitherBossBar extends BossBarManagerImpl implements Listener, WorldSwitchListener {
/**
* Constructs new instance and registers events
*
* @param plugin
* Plugin instance to register events
*/
public WitherBossBar(JavaPlugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@Override
public void load() {
//when MC is on fullscreen, BossBar disappears after 1 second of not being seen
//when in a small window, it's about 100ms
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(100,
featureName, TabConstants.CpuUsageCategory.TELEPORTING_WITHER, this::teleport);
super.load();
teleport();
}
/**
* Updates Wither location for all online players
*/
private void teleport() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (p.getVersion().getMinorVersion() > 8) continue; //sending VV packets to those
for (BossBar line : getRegisteredBossBars().values()) {
if (!line.getPlayers().contains(p)) continue;
Location eyeLocation = ((BukkitTabPlayer)p).getPlayer().getEyeLocation();
Location loc = eyeLocation.add(eyeLocation.getDirection().normalize().multiply(60)); // Wither distance
if (loc.getY() < 1) loc.setY(1);
((BackendTabPlayer)p).getEntityView().teleportEntity(line.getUniqueId().hashCode(), new me.neznamy.tab.shared.backend.Location(loc.getX(), loc.getY(), loc.getZ()));
}
}
}
@Override
public void unload() {
super.unload();
HandlerList.unregisterAll(this);
}
/**
* Respawning wither as respawn screen destroys all entities in client
*/
@EventHandler
public void onRespawn(PlayerRespawnEvent e) {
TabPlayer player = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
if (player == null) return;
TAB.getInstance().getCPUManager().runMeasuredTask(featureName, TabConstants.CpuUsageCategory.PLAYER_RESPAWN,
() -> detectBossBarsAndSend(player));
}
@Override
public void onWorldChange(@NotNull TabPlayer p, @NotNull String from, @NotNull String to) {
for (BossBar line : lineValues) {
line.removePlayer(p);
}
detectBossBarsAndSend(p);
}
}
@@ -1,33 +0,0 @@
package me.neznamy.tab.platforms.bukkit.header;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.BukkitUtils;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Header/footer sender using Bukkit API. Despite this feature getting
* added into the game in 1.8, Bukkit team took 4 entire years to add
* it to the API, which eventually made it there in 1.13. <p>
* The input is just a String, meaning Bukkit will have to deserialize it
* into components, which is a massive performance loss. It also does not
* seem to support fonts.
*/
public class BukkitHeaderFooter extends HeaderFooter {
/** Flag tracking whether this class can be used on current server */
@Getter
private static final boolean available = ReflectionUtils.methodExists(Player.class, "setPlayerListHeaderFooter", String.class, String.class);
@Override
@SuppressWarnings("deprecation") // Marked as deprecated by Paper to make us use their methods instead
public void set(@NotNull BukkitTabPlayer player, @NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().setPlayerListHeaderFooter(
BukkitUtils.toBukkitFormat(header, player.getVersion().getMinorVersion() >= 16),
BukkitUtils.toBukkitFormat(footer, player.getVersion().getMinorVersion() >= 16)
);
}
}
@@ -1,57 +0,0 @@
package me.neznamy.tab.platforms.bukkit.header;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A class responsible for sending header/footer to players on request.
*/
public abstract class HeaderFooter {
/** Instance of the class, null if no implementation is available */
@Getter
@Nullable
private static HeaderFooter instance;
/**
* Finds the best available instance for current server software.
*/
public static void findInstance() {
if (PacketHeaderFooter.isAvailable()) {
instance = new PacketHeaderFooter();
} else if (PaperHeaderFooter.isAvailable()) {
instance = new PaperHeaderFooter();
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] Failed to initialize NMS fields for " +
"sending Header/Footer due to a compatibility error. Using fallback solution using Paper API.");
} else if (BukkitHeaderFooter.isAvailable()) {
instance = new BukkitHeaderFooter();
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] Failed to initialize NMS fields for " +
"sending Header/Footer due to a compatibility error. Using fallback solution using Bukkit API. " +
"This will drastically drop performance, as well as miss support for fonts (added in 1.16). " +
"Please update the plugin with to version proper support for your server version.");
} else if (BukkitReflection.getMinorVersion() >= 8){
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] Failed to initialize NMS fields for " +
"sending Header/Footer due to a compatibility error. No fallback solution was found either. This will " +
"result in the feature not working. " +
"Please update the plugin with to version proper support for your server version.");
}
}
/**
* Sends header/footer to player.
*
* @param player
* Player to send header/footer to.
* @param header
* Header to use.
* @param footer
* Footer to use.
*/
public abstract void set(@NotNull BukkitTabPlayer player, @NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer);
}
@@ -1,70 +0,0 @@
package me.neznamy.tab.platforms.bukkit.header;
import lombok.Getter;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* Header/footer sender that uses NMS to send packets. Available
* on all versions since 1.8 when the feature was added into the game.
*/
public class PacketHeaderFooter extends HeaderFooter {
private static Constructor<?> newHeaderFooter;
private static Field HEADER;
private static Field FOOTER;
@Getter
private static boolean available;
static {
try {
if (BukkitReflection.getMinorVersion() >= 8) {
Class<?> IChatBaseComponent;
Class<?> HeaderFooterClass;
if (BukkitReflection.isMojangMapped()) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.Component");
HeaderFooterClass = Class.forName("net.minecraft.network.protocol.game.ClientboundTabListPacket");
} else if (BukkitReflection.getMinorVersion() >= 17) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
HeaderFooterClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter");
} else {
IChatBaseComponent = BukkitReflection.getLegacyClass("IChatBaseComponent");
HeaderFooterClass = BukkitReflection.getLegacyClass("PacketPlayOutPlayerListHeaderFooter");
}
if (BukkitReflection.getMinorVersion() >= 17) {
newHeaderFooter = HeaderFooterClass.getConstructor(IChatBaseComponent, IChatBaseComponent);
} else {
newHeaderFooter = HeaderFooterClass.getConstructor();
HEADER = ReflectionUtils.getFields(HeaderFooterClass, IChatBaseComponent).get(0);
FOOTER = ReflectionUtils.getFields(HeaderFooterClass, IChatBaseComponent).get(1);
}
available = true;
}
} catch (Exception ignored) {
// Print exception to find out what went wrong
}
}
@SneakyThrows
@Override
public void set(@NotNull BukkitTabPlayer player, @NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
Object headerComponent = player.getPlatform().toComponent(header, player.getVersion());
Object footerComponent = player.getPlatform().toComponent(footer, player.getVersion());
if (BukkitReflection.getMinorVersion() >= 17) {
player.sendPacket(newHeaderFooter.newInstance(headerComponent, footerComponent));
} else {
Object packet = newHeaderFooter.newInstance();
HEADER.set(packet, headerComponent);
FOOTER.set(packet, footerComponent);
player.sendPacket(packet);
}
}
}
@@ -1,33 +0,0 @@
package me.neznamy.tab.platforms.bukkit.header;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Header/footer sender using Paper's API added in 1.16.5.
* Due to lack of component cache for adventure components in TAB,
* there is a performance loss compared to using NMS. However, we do not
* need to serialize/deserialize anything ourselves thanks to direct component
* conversion, so in the end it is roughly the same.
*/
public class PaperHeaderFooter extends HeaderFooter {
/** Flag tracking whether this class can be used on current server */
@Getter
private static final boolean available = ReflectionUtils.classExists("net.kyori.adventure.text.Component") &&
ReflectionUtils.methodExists(Player.class, "sendPlayerListHeaderAndFooter", Component.class, Component.class);
@Override
public void set(@NotNull BukkitTabPlayer player, @NotNull IChatBaseComponent header, @NotNull IChatBaseComponent footer) {
player.getPlayer().sendPlayerListHeaderAndFooter(
AdventureHook.toAdventureComponent(header, player.getVersion()),
AdventureHook.toAdventureComponent(footer, player.getVersion())
);
}
}
@@ -0,0 +1,23 @@
package me.neznamy.tab.platforms.bukkit.hook;
import de.myzelyam.api.vanish.VanishAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.hook.PremiumVanishHook;
import org.jetbrains.annotations.NotNull;
/**
* PremiumVanish hook for Bukkit.
*/
public class BukkitPremiumVanishHook extends PremiumVanishHook {
@Override
public boolean canSee(@NotNull TabPlayer viewer, @NotNull TabPlayer target) {
return VanishAPI.canSee(((BukkitTabPlayer)viewer).getPlayer(), ((BukkitTabPlayer)target).getPlayer());
}
@Override
public boolean isVanished(@NotNull TabPlayer player) {
return VanishAPI.isInvisible(((BukkitTabPlayer)player).getPlayer());
}
}
@@ -1,83 +0,0 @@
package me.neznamy.tab.platforms.bukkit.nms;
import lombok.Getter;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public class BukkitReflection {
/** Server's NMS/CraftBukkit package */
@Getter
private static final String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
/** Server's minor version */
@Getter
private static final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
/** Flag determining whether this server jar is mojang mapped or not */
@Getter
private static final boolean mojangMapped = checkMojangMapped();
/** Flag determining whether the server version is at least 1.19.3 or not */
@Getter
private static final boolean is1_19_3Plus = ReflectionUtils.classExists("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
/** Flag determining whether the server version is at least 1.19.4 or not */
@Getter
private static final boolean is1_19_4Plus = is1_19_3Plus && !serverPackage.equals("v1_19_R2");
/** Flag determining whether the server version is at least 1.20.2 or not */
@Getter
private static final boolean is1_20_2Plus = minorVersion >= 20 && !serverPackage.equals("v1_20_R1");
private static boolean checkMojangMapped() {
try {
Class.forName("net.minecraft.network.syncher.SynchedEntityData").getMethod("define",
Class.forName("net.minecraft.network.syncher.EntityDataAccessor"), Object.class);
return true;
} catch (ReflectiveOperationException | NullPointerException ex) {
return false;
}
}
/**
* 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
*/
public static Class<?> getLegacyClass(@NotNull String... names) throws ClassNotFoundException {
for (String name : names) {
try {
return getLegacyClass(name);
} catch (ClassNotFoundException e) {
//not the first class name in array
}
}
throw new ClassNotFoundException("No class found with possible names " + Arrays.toString(names));
}
/**
* Returns class from given name. Supports modded servers, such as Thermos.
*
* @param name
* class name
* @return class from given name
* @throws ClassNotFoundException
* if class was not found
*/
public static Class<?> getLegacyClass(@NotNull String name) throws ClassNotFoundException {
try {
return BukkitReflection.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
} catch (NullPointerException e) {
// nested class not found
throw new ClassNotFoundException(name);
}
}
}
@@ -1,292 +0,0 @@
package me.neznamy.tab.platforms.bukkit.nms;
import lombok.*;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* A class representing the n.m.s.DataWatcher class to make work with it much easier
*/
@ToString
public class DataWatcher implements EntityData {
/** NMS Fields */
public static Class<?> DataWatcher;
private static Constructor<?> newDataWatcher;
private static Method DataWatcher_register;
private static Method DataWatcher_markDirty;
public static Method DataWatcher_packDirty;
private static Class<?> DataWatcherObject;
private static Constructor<?> newDataWatcherObject;
private static Class<?> DataWatcherRegistry;
private static Class<?> DataWatcherSerializer;
private static Object DataWatcherSerializer_BYTE;
private static Object DataWatcherSerializer_FLOAT;
private static Object DataWatcherSerializer_STRING;
private static Object DataWatcherSerializer_OPTIONAL_COMPONENT;
private static Object DataWatcherSerializer_BOOLEAN;
private static final int armorStandFlagsPosition = getArmorStandFlagsPosition();
/** Watched data */
private final Map<Integer, Item> dataValues = new HashMap<>();
/**
* Returns armor stand flags position based on server version
*
* @return armor stand flags position based on server version
*/
private static int getArmorStandFlagsPosition() {
if (BukkitReflection.getMinorVersion() >= 17) {
//1.17.x, 1.18.x, 1.19.x, 1.20.x
return 15;
} else if (BukkitReflection.getMinorVersion() >= 15) {
//1.15.x, 1.16.x
return 14;
} else if (BukkitReflection.getMinorVersion() >= 14) {
//1.14.x
return 13;
} else if (BukkitReflection.getMinorVersion() >= 10) {
//1.10.x - 1.13.x
return 11;
} else {
//1.8.x - 1.9.x
return 10;
}
}
/**
* Loads all required Fields and throws Exception if something went wrong
*/
public static void load() throws ReflectiveOperationException {
int minorVersion = BukkitReflection.getMinorVersion();
Class<?> Entity;
if (BukkitReflection.isMojangMapped()) {
Entity = Class.forName("net.minecraft.world.entity.Entity");
DataWatcher = Class.forName("net.minecraft.network.syncher.SynchedEntityData");
DataWatcherObject = Class.forName("net.minecraft.network.syncher.EntityDataAccessor");
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.EntityDataSerializers");
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.EntityDataSerializer");
} else if (minorVersion >= 17) {
Entity = Class.forName("net.minecraft.world.entity.Entity");
DataWatcher = Class.forName("net.minecraft.network.syncher.DataWatcher");
DataWatcherObject = Class.forName("net.minecraft.network.syncher.DataWatcherObject");
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.DataWatcherRegistry");
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.DataWatcherSerializer");
} else {
Entity = BukkitReflection.getLegacyClass("Entity");
DataWatcher = BukkitReflection.getLegacyClass("DataWatcher");
if (minorVersion >= 9) {
DataWatcherObject = BukkitReflection.getLegacyClass("DataWatcherObject");
DataWatcherRegistry = BukkitReflection.getLegacyClass("DataWatcherRegistry");
DataWatcherSerializer = BukkitReflection.getLegacyClass("DataWatcherSerializer");
}
}
if (minorVersion >= 7) {
newDataWatcher = DataWatcher.getConstructor(Entity);
} else {
newDataWatcher = DataWatcher.getConstructor();
}
if (minorVersion >= 9) {
DataWatcher_register = ReflectionUtils.getMethod(
DataWatcher,
new String[]{"define", "register", "a", "m_135372_"}, // {Bukkit 1.20.2+, Bukkit, Bukkit 1.18+, Mohist 1.18.2}
DataWatcherObject, Object.class
);
} else {
DataWatcher_register = ReflectionUtils.getMethod(DataWatcher, new String[]{"func_75682_a", "a"}, int.class, Object.class); // {Thermos 1.7.10, Bukkit}
}
if (minorVersion >= 19) {
DataWatcher_packDirty = ReflectionUtils.getMethod(DataWatcher, new String[] {"packDirty", "b"}); // {Mojang | 1.20.2+, 1.20.2-}
}
if (BukkitReflection.is1_19_3Plus()) {
DataWatcher_markDirty = ReflectionUtils.getMethods(DataWatcher, void.class, DataWatcherObject).get(0);
}
if (minorVersion >= 9) {
newDataWatcherObject = DataWatcherObject.getConstructor(int.class, DataWatcherSerializer);
DataWatcherSerializer_BYTE = ReflectionUtils.getField(DataWatcherRegistry, "BYTE", "a", "f_135027_").get(null); // Mohist 1.18.2
DataWatcherSerializer_FLOAT = ReflectionUtils.getField(DataWatcherRegistry, "FLOAT", "c", "f_135029_").get(null); // Mohist 1.18.2
DataWatcherSerializer_STRING = ReflectionUtils.getField(DataWatcherRegistry, "STRING", "d", "f_135030_").get(null); // Mohist 1.18.2
if (BukkitReflection.is1_19_3Plus()) {
DataWatcherSerializer_OPTIONAL_COMPONENT = ReflectionUtils.getField(DataWatcherRegistry, "OPTIONAL_COMPONENT", "g").get(null);
if (BukkitReflection.is1_19_4Plus()) {
DataWatcherSerializer_BOOLEAN = ReflectionUtils.getField(DataWatcherRegistry, "BOOLEAN", "k").get(null);
} else {
DataWatcherSerializer_BOOLEAN = ReflectionUtils.getField(DataWatcherRegistry, "BOOLEAN", "j").get(null);
}
} else {
if (minorVersion >= 13) {
DataWatcherSerializer_OPTIONAL_COMPONENT = ReflectionUtils.getField(DataWatcherRegistry,
"OPTIONAL_COMPONENT", "f", "f_135032_").get(null); // Mohist 1.18.2
DataWatcherSerializer_BOOLEAN = ReflectionUtils.getField(DataWatcherRegistry,
"BOOLEAN", "i", "f_135035_").get(null); // Mohist 1.18.2
} else {
DataWatcherSerializer_BOOLEAN = DataWatcherRegistry.getDeclaredField("h").get(null);
}
}
}
}
/**
* Sets value into data values
*
* @param position
* data position
* @param serializer
* Object serializer (1.9+)
* @param value
* value
*/
public void setValue(int position, @Nullable Object serializer, @NotNull Object value) {
dataValues.put(position, new Item(position, serializer, value));
}
/**
* Writes entity byte flags
*
* @param flags
* flags to write
*/
public void setEntityFlags(byte flags) {
setValue(0, DataWatcherSerializer_BYTE, flags);
}
/**
* Writes entity custom name with position based on server version and value depending on client version (RGB or not)
*
* @param customName
* target custom name
* @param clientVersion
* client version
*/
public void setCustomName(@NotNull String customName, @NotNull ProtocolVersion clientVersion) {
if (BukkitReflection.getMinorVersion() >= 13) {
setValue(2, DataWatcherSerializer_OPTIONAL_COMPONENT,
Optional.of(((BukkitPlatform)TAB.getInstance().getPlatform()).toComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
} else if (BukkitReflection.getMinorVersion() >= 8) {
setValue(2, DataWatcherSerializer_STRING, customName);
} else {
//name length is limited to 64 characters on <1.8
String cutName = (customName.length() > 64 ? customName.substring(0, 64) : customName);
if (BukkitReflection.getMinorVersion() >= 6) {
setValue(10, null, cutName);
} else {
setValue(5, null, cutName);
}
}
}
/**
* Writes custom name visibility boolean
*
* @param visible
* if visible or not
*/
public void setCustomNameVisible(boolean visible) {
if (BukkitReflection.getMinorVersion() >= 9) {
setValue(3, DataWatcherSerializer_BOOLEAN, visible);
} else {
setValue(3, null, (byte)(visible?1:0));
}
}
/**
* Writes entity health
*
* @param health
* health of entity
*/
public void setHealth(float health) {
if (BukkitReflection.getMinorVersion() >= 6) {
setValue(6, DataWatcherSerializer_FLOAT, health);
} else {
setValue(16, null, (int)health);
}
}
/**
* Writes armor stand flags
*
* @param flags
* flags to write
*/
public void setArmorStandFlags(byte flags) {
setValue(armorStandFlagsPosition, DataWatcherSerializer_BYTE, flags);
}
/**
* Writes wither invulnerable time
* @param time
* Time, apparently
*/
public void setWitherInvulnerableTime(int time) {
if (BukkitReflection.getMinorVersion() > 8)
throw new UnsupportedOperationException("Not supported on 1.9+");
setValue(20, null, time);
}
/**
* Converts the class into an instance of NMS.DataWatcher
*
* @return an instance of NMS.DataWatcher with same data
*/
@SneakyThrows
public @NotNull Object build() {
Object nmsWatcher;
if (newDataWatcher.getParameterCount() == 1) { //1.7+
nmsWatcher = newDataWatcher.newInstance(new Object[] {null});
} else {
nmsWatcher = newDataWatcher.newInstance();
}
for (Item item : dataValues.values()) {
Object nmsObject = item.createObject();
DataWatcher_register.invoke(nmsWatcher, nmsObject, item.getValue());
if (BukkitReflection.is1_19_3Plus()) DataWatcher_markDirty.invoke(nmsWatcher, nmsObject);
}
return nmsWatcher;
}
@RequiredArgsConstructor
@Getter
public static class Item {
/** Item position */
private final int position;
/** Item serializer (1.9+) */
@Nullable
private final Object serializer;
/** Item value */
@NotNull
private final Object value;
/**
* Creates data watcher object with position and serializer
*
* @return NMS data watcher object
*/
@SneakyThrows
public Object createObject() {
if (BukkitReflection.getMinorVersion() >= 9) {
return newDataWatcherObject.newInstance(position, serializer);
} else {
return position;
}
}
}
}
@@ -1,123 +0,0 @@
package me.neznamy.tab.platforms.bukkit.nms;
import com.mojang.authlib.GameProfile;
import io.netty.channel.Channel;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabList;
import me.neznamy.tab.platforms.bukkit.header.HeaderFooter;
import me.neznamy.tab.platforms.bukkit.scoreboard.PacketScoreboard;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.util.ComponentCache;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.Bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Class holding all NMS classes, methods, fields and constructors used by TAB.
*/
public class NMSStorage {
/** Instance of this class */
@Getter @Setter private static NMSStorage instance;
/** Server's NMS/CraftBukkit package */
private final String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
/** Server's minor version */
@Getter private final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
/** Basic universal values */
protected Class<?> Packet;
protected Class<?> NetworkManager;
public Class<?> EntityPlayer;
protected Class<?> EntityHuman;
protected Class<?> PlayerConnection;
// Ping field for 1.5.2 - 1.16.5, 1.17+ has Player#getPing()
public Field PING;
public Field PLAYER_CONNECTION;
public Field NETWORK_MANAGER;
public Field CHANNEL;
public Method getHandle;
public Method sendPacket;
public Method getProfile;
/** Chat components */
protected Class<?> ChatSerializer;
public Method ChatSerializer_DESERIALIZE;
public final ComponentCache<IChatBaseComponent, Object> componentCache = new ComponentCache<>(1000,
(component, clientVersion) -> ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion)));
/**
* Constructs new instance and attempts to load all fields, methods and constructors.
*/
@SneakyThrows
public NMSStorage() {
ProtocolVersion.UNKNOWN_SERVER_VERSION.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
loadClasses();
if (minorVersion >= 7) {
if (BukkitReflection.is1_20_2Plus()) {
NETWORK_MANAGER = ReflectionUtils.getOnlyField(PlayerConnection.getSuperclass(), NetworkManager);
} else {
NETWORK_MANAGER = ReflectionUtils.getOnlyField(PlayerConnection, NetworkManager);
}
sendPacket = ReflectionUtils.getMethods(PlayerConnection, void.class, Packet).get(0);
} else {
sendPacket = ReflectionUtils.getMethod(PlayerConnection, new String[]{"sendPacket"}, Packet);
}
if (minorVersion >= 8) {
ChatSerializer_DESERIALIZE = ReflectionUtils.getMethods(ChatSerializer, Object.class, String.class).get(0);
CHANNEL = ReflectionUtils.getOnlyField(NetworkManager, Channel.class);
// There is only supposed to be one, however there are exceptions:
// #1 - CatServer adds another method
// #2 - Random mods may perform deep hack into the server and add another one (see #1089)
// Get first and hope for the best, alternatively players may not have correct skins in layout, but who cares
getProfile = ReflectionUtils.getMethods(EntityHuman, GameProfile.class).get(0);
}
PLAYER_CONNECTION = ReflectionUtils.getOnlyField(EntityPlayer, PlayerConnection);
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
BukkitTabList.load();
DataWatcher.load();
PacketEntityView.load();
PacketScoreboard.load();
HeaderFooter.findInstance();
if (minorVersion < 17) {
PING = ReflectionUtils.getField(EntityPlayer, "ping", "field_71138_i"); // 1.5.2 - 1.16.5, 1.7.10 Thermos
}
}
private void loadClasses() throws ClassNotFoundException {
if (minorVersion >= 17 && ReflectionUtils.classExists("net.minecraft.ChatFormatting")) {
ChatSerializer = Class.forName("net.minecraft.network.chat.Component$Serializer");
EntityHuman = Class.forName("net.minecraft.world.entity.player.Player");
NetworkManager = Class.forName("net.minecraft.network.Connection");
Packet = Class.forName("net.minecraft.network.protocol.Packet");
EntityPlayer = Class.forName("net.minecraft.server.level.ServerPlayer");
PlayerConnection = Class.forName("net.minecraft.server.network.ServerGamePacketListenerImpl");
} else if (minorVersion >= 17) {
ChatSerializer = Class.forName("net.minecraft.network.chat.IChatBaseComponent$ChatSerializer");
EntityHuman = Class.forName("net.minecraft.world.entity.player.EntityHuman");
NetworkManager = Class.forName("net.minecraft.network.NetworkManager");
Packet = Class.forName("net.minecraft.network.protocol.Packet");
EntityPlayer = Class.forName("net.minecraft.server.level.EntityPlayer");
PlayerConnection = Class.forName("net.minecraft.server.network.PlayerConnection");
} else {
EntityHuman = BukkitReflection.getLegacyClass("EntityHuman");
Packet = BukkitReflection.getLegacyClass("Packet");
EntityPlayer = BukkitReflection.getLegacyClass("EntityPlayer");
PlayerConnection = BukkitReflection.getLegacyClass("PlayerConnection");
NetworkManager = BukkitReflection.getLegacyClass("NetworkManager");
if (minorVersion >= 7) {
ChatSerializer = BukkitReflection.getLegacyClass("IChatBaseComponent$ChatSerializer", "ChatSerializer");
}
}
}
}
@@ -1,392 +0,0 @@
package me.neznamy.tab.platforms.bukkit.nms;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.backend.Location;
import me.neznamy.tab.shared.backend.entityview.EntityView;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.UUID;
@RequiredArgsConstructor
@SuppressWarnings("unchecked")
public class PacketEntityView implements EntityView {
/** PacketPlayOutEntityDestroy */
private static Class<?> EntityDestroyClass;
private static Constructor<?> newEntityDestroy;
private static Field EntityDestroy_Entities;
/** PacketPlayOutEntityMetadata */
private static Constructor<?> newEntityMetadata;
/** PacketPlayOutEntityTeleport */
private static Class<?> EntityTeleportClass;
private static Constructor<?> newEntityTeleport;
private static Field EntityTeleport_EntityId;
private static Field EntityTeleport_X;
private static Field EntityTeleport_Y;
private static Field EntityTeleport_Z;
/** PacketPlayOutSpawnEntityLiving */
private static Class<?> SpawnEntityClass;
private static Constructor<?> newSpawnEntity;
/** 1.17+ */
private static Object Vec3D_Empty;
private static Object EntityTypes_ARMOR_STAND;
/** 1.16.5- */
private static Field SpawnEntity_EntityId;
private static Field SpawnEntity_EntityType;
private static Field SpawnEntity_UUID;
private static Field SpawnEntity_X;
private static Field SpawnEntity_Y;
private static Field SpawnEntity_Z;
private static Field SpawnEntity_DataWatcher;
private static final EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
/** Other entity packets */
private static Class<?> PacketPlayOutEntity;
private static Field PacketPlayOutEntity_ENTITYID;
private static Class<?> PacketPlayOutEntityLook;
private static Class<?> PacketPlayOutNamedEntitySpawn;
private static Field PacketPlayOutNamedEntitySpawn_ENTITYID;
private static Object dummyEntity;
@Getter
private static boolean available;
/** Player this view belongs to */
private final BukkitTabPlayer player;
/**
* Loads all required classes and fields and throws Exception if something went wrong
*
* @throws ReflectiveOperationException
* If something fails
*/
public static void load() throws ReflectiveOperationException {
loadEntityMetadata();
loadEntityDestroy();
loadEntityTeleport();
loadEntityMove();
loadEntitySpawn();
available = true;
}
private static void loadEntityMetadata() throws ReflectiveOperationException {
// Class
Class<?> entityMetadataClass = getClass("network.protocol.game.ClientboundSetEntityDataPacket",
"network.protocol.game.PacketPlayOutEntityMetadata", "PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
// Constructor
if (BukkitReflection.is1_19_3Plus()) {
newEntityMetadata = entityMetadataClass.getConstructor(int.class, List.class);
} else {
newEntityMetadata = entityMetadataClass.getConstructor(int.class, DataWatcher.DataWatcher, boolean.class);
}
}
private static void loadEntityDestroy() throws ReflectiveOperationException {
// Class
EntityDestroyClass = getClass("network.protocol.game.ClientboundRemoveEntitiesPacket",
"network.protocol.game.PacketPlayOutEntityDestroy", "PacketPlayOutEntityDestroy", "Packet29DestroyEntity");
// Constructor
try {
newEntityDestroy = EntityDestroyClass.getConstructor(int[].class);
} catch (NoSuchMethodException e) {
//1.17.0
newEntityDestroy = EntityDestroyClass.getConstructor(int.class);
}
// Field
EntityDestroy_Entities = ReflectionUtils.getOnlyField(EntityDestroyClass);
}
private static void loadEntityTeleport() throws ReflectiveOperationException {
// Class
EntityTeleportClass = getClass("network.protocol.game.ClientboundTeleportEntityPacket",
"network.protocol.game.PacketPlayOutEntityTeleport", "PacketPlayOutEntityTeleport", "Packet34EntityTeleport");
// Constructor
if (BukkitReflection.getMinorVersion() >= 17) {
newEntityTeleport = EntityTeleportClass.getConstructor(getClass("world.entity.Entity"));
} else {
newEntityTeleport = EntityTeleportClass.getConstructor();
}
// Fields
EntityTeleport_EntityId = ReflectionUtils.getFields(EntityTeleportClass, int.class).get(0);
if (BukkitReflection.getMinorVersion() >= 9) {
EntityTeleport_X = ReflectionUtils.getFields(EntityTeleportClass, double.class).get(0);
EntityTeleport_Y = ReflectionUtils.getFields(EntityTeleportClass, double.class).get(1);
EntityTeleport_Z = ReflectionUtils.getFields(EntityTeleportClass, double.class).get(2);
} else {
EntityTeleport_X = ReflectionUtils.getFields(EntityTeleportClass, int.class).get(1);
EntityTeleport_Y = ReflectionUtils.getFields(EntityTeleportClass, int.class).get(2);
EntityTeleport_Z = ReflectionUtils.getFields(EntityTeleportClass, int.class).get(3);
}
// Dummy armor stand for constructor
if (BukkitReflection.getMinorVersion() >= 17) {
Class<?> world = getClass("world.level.Level", "world.level.World", "World");
Class<?> entityArmorStand = getClass("world.entity.decoration.ArmorStand",
"world.entity.decoration.EntityArmorStand", "EntityArmorStand");
Constructor<?> newEntityArmorStand = entityArmorStand.getConstructor(world, double.class, double.class, double.class);
Method World_getHandle = Class.forName("org.bukkit.craftbukkit." + BukkitReflection.getServerPackage() + ".CraftWorld").getMethod("getHandle");
dummyEntity = newEntityArmorStand.newInstance(World_getHandle.invoke(Bukkit.getWorlds().get(0)), 0, 0, 0);
}
}
private static void loadEntityMove() throws ReflectiveOperationException {
// Classes
PacketPlayOutEntity = getClass("network.protocol.game.ClientboundMoveEntityPacket",
"network.protocol.game.PacketPlayOutEntity", "PacketPlayOutEntity", "Packet30Entity");
PacketPlayOutEntityLook = getClass("network.protocol.game.ClientboundMoveEntityPacket$Rot",
"network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntity$PacketPlayOutEntityLook",
"PacketPlayOutEntityLook", "Packet32EntityLook");
// Field
PacketPlayOutEntity_ENTITYID = ReflectionUtils.getFields(PacketPlayOutEntity, int.class).get(0);
}
private static void loadEntitySpawn() throws ReflectiveOperationException {
SpawnEntityClass = getClass("network.protocol.game.ClientboundAddEntityPacket",
"network.protocol.game.PacketPlayOutSpawnEntity", "PacketPlayOutSpawnEntityLiving", "Packet24MobSpawn");
SpawnEntity_EntityId = ReflectionUtils.getFields(SpawnEntityClass, int.class).get(0);
if (BukkitReflection.getMinorVersion() >= 17) {
Class<?> Vec3D = getClass("world.phys.Vec3", "world.phys.Vec3D");
Vec3D_Empty = ReflectionUtils.getOnlyField(Vec3D, Vec3D).get(null);
Class<?> EntityTypes = getClass("world.entity.EntityType", "world.entity.EntityTypes");
if (BukkitReflection.getMinorVersion() >= 19) {
EntityTypes_ARMOR_STAND = ReflectionUtils.getField(EntityTypes, "ARMOR_STAND", "d").get(null);
} else {
EntityTypes_ARMOR_STAND = ReflectionUtils.getField(EntityTypes, "ARMOR_STAND", "c", "f_20529_").get(null); // Mohist 1.18.2
}
if (BukkitReflection.getMinorVersion() >= 19) {
newSpawnEntity = SpawnEntityClass.getConstructor(int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, EntityTypes, int.class, Vec3D, double.class);
} else {
newSpawnEntity = SpawnEntityClass.getConstructor(int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, EntityTypes, int.class, Vec3D);
}
} else {
newSpawnEntity = SpawnEntityClass.getConstructor();
if (BukkitReflection.getMinorVersion() >= 9) {
SpawnEntity_UUID = ReflectionUtils.getOnlyField(SpawnEntityClass, UUID.class);
SpawnEntity_X = ReflectionUtils.getFields(SpawnEntityClass, double.class).get(0);
SpawnEntity_Y = ReflectionUtils.getFields(SpawnEntityClass, double.class).get(1);
SpawnEntity_Z = ReflectionUtils.getFields(SpawnEntityClass, double.class).get(2);
} else {
SpawnEntity_X = ReflectionUtils.getFields(SpawnEntityClass, int.class).get(2);
SpawnEntity_Y = ReflectionUtils.getFields(SpawnEntityClass, int.class).get(3);
SpawnEntity_Z = ReflectionUtils.getFields(SpawnEntityClass, int.class).get(4);
}
SpawnEntity_EntityType = ReflectionUtils.getFields(SpawnEntityClass, int.class).get(1);
if (BukkitReflection.getMinorVersion() <= 14) {
SpawnEntity_DataWatcher = ReflectionUtils.getOnlyField(SpawnEntityClass, DataWatcher.DataWatcher);
}
}
if (BukkitReflection.getMinorVersion() >= 13) {
entityIds.put(EntityType.ARMOR_STAND, 1);
} else {
entityIds.put(EntityType.WITHER, 64);
if (BukkitReflection.getMinorVersion() >= 8) {
entityIds.put(EntityType.ARMOR_STAND, 30);
}
}
if (!BukkitReflection.is1_20_2Plus()) {
PacketPlayOutNamedEntitySpawn = getClass("network.protocol.game.ClientboundAddPlayerPacket",
"network.protocol.game.PacketPlayOutNamedEntitySpawn", "PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
PacketPlayOutNamedEntitySpawn_ENTITYID = ReflectionUtils.getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
}
}
private static int floor(double paramDouble) {
int i = (int)paramDouble;
return paramDouble < i ? i - 1 : i;
}
@SneakyThrows
@Override
public void spawnEntity(int entityId, @NotNull UUID id, @NotNull Object entityType, @NotNull Location l, @NotNull EntityData data) {
int minorVersion = BukkitReflection.getMinorVersion();
if (minorVersion >= 19) {
player.sendPacket(newSpawnEntity.newInstance(entityId, id, l.getX(), l.getY(), l.getZ(), 0, 0, EntityTypes_ARMOR_STAND, 0, Vec3D_Empty, 0d));
} else if (minorVersion >= 17) {
player.sendPacket(newSpawnEntity.newInstance(entityId, id, l.getX(), l.getY(), l.getZ(), 0, 0, EntityTypes_ARMOR_STAND, 0, Vec3D_Empty));
} else {
Object nmsPacket = newSpawnEntity.newInstance();
SpawnEntity_EntityId.set(nmsPacket, entityId);
if (minorVersion <= 14) {
SpawnEntity_DataWatcher.set(nmsPacket, data.build());
}
if (minorVersion >= 9) {
SpawnEntity_UUID.set(nmsPacket, id);
SpawnEntity_X.set(nmsPacket, l.getX());
SpawnEntity_Y.set(nmsPacket, l.getY());
SpawnEntity_Z.set(nmsPacket, l.getZ());
} else {
SpawnEntity_X.set(nmsPacket, floor(l.getX()*32));
SpawnEntity_Y.set(nmsPacket, floor(l.getY()*32));
SpawnEntity_Z.set(nmsPacket, floor(l.getZ()*32));
}
SpawnEntity_EntityType.set(nmsPacket, entityIds.get((EntityType) entityType));
player.sendPacket(nmsPacket);
}
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 15) {
updateEntityMetadata(entityId, data);
}
}
@SneakyThrows
@Override
public void updateEntityMetadata(int entityId, @NotNull EntityData data) {
if (newEntityMetadata.getParameterCount() == 2) {
//1.19.3+
player.sendPacket(newEntityMetadata.newInstance(entityId, DataWatcher.DataWatcher_packDirty.invoke(data.build())));
} else {
player.sendPacket(newEntityMetadata.newInstance(entityId, data.build(), true));
}
}
@SneakyThrows
@Override
public void teleportEntity(int entityId, @NotNull Location location) {
Object nmsPacket;
if (BukkitReflection.getMinorVersion() >= 17) {
nmsPacket = newEntityTeleport.newInstance(dummyEntity);
} else {
nmsPacket = newEntityTeleport.newInstance();
}
EntityTeleport_EntityId.set(nmsPacket, entityId);
if (BukkitReflection.getMinorVersion() >= 9) {
EntityTeleport_X.set(nmsPacket, location.getX());
EntityTeleport_Y.set(nmsPacket, location.getY());
EntityTeleport_Z.set(nmsPacket, location.getZ());
} else {
EntityTeleport_X.set(nmsPacket, floor(location.getX()*32));
EntityTeleport_Y.set(nmsPacket, floor(location.getY()*32));
EntityTeleport_Z.set(nmsPacket, floor(location.getZ()*32));
}
player.sendPacket(nmsPacket);
}
@SneakyThrows
@Override
public void destroyEntities(int... entities) {
if (newEntityDestroy.getParameterTypes()[0] != int.class) {
player.sendPacket(newEntityDestroy.newInstance(new Object[]{entities}));
} else {
//1.17.0 Mojank
for (int entity : entities) {
player.sendPacket(newEntityDestroy.newInstance(entity));
}
}
}
@Override
public boolean isDestroyPacket(@NotNull Object packet) {
return EntityDestroyClass.isInstance(packet);
}
@Override
public boolean isTeleportPacket(@NotNull Object packet) {
return EntityTeleportClass.isInstance(packet);
}
@Override
public boolean isNamedEntitySpawnPacket(@NotNull Object packet) {
if (BukkitReflection.is1_20_2Plus()) {
return SpawnEntityClass.isInstance(packet);
} else {
return PacketPlayOutNamedEntitySpawn.isInstance(packet);
}
}
@Override
public boolean isMovePacket(@NotNull Object packet) {
return PacketPlayOutEntity.isInstance(packet);
}
@Override
public boolean isLookPacket(@NotNull Object packet) {
return PacketPlayOutEntityLook.isInstance(packet);
}
@Override
@SneakyThrows
public int getTeleportEntityId(@NotNull Object teleportPacket) {
return EntityTeleport_EntityId.getInt(teleportPacket);
}
@Override
@SneakyThrows
public int getMoveEntityId(@NotNull Object movePacket) {
return PacketPlayOutEntity_ENTITYID.getInt(movePacket);
}
@Override
@SneakyThrows
public int getSpawnedPlayer(@NotNull Object playerSpawnPacket) {
if (BukkitReflection.is1_20_2Plus()) {
return SpawnEntity_EntityId.getInt(playerSpawnPacket);
} else {
return PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(playerSpawnPacket);
}
}
@Override
@SneakyThrows
public int[] getDestroyedEntities(@NotNull Object destroyPacket) {
Object entities = PacketEntityView.EntityDestroy_Entities.get(destroyPacket);
if (BukkitReflection.getMinorVersion() >= 17) {
if (entities instanceof List) {
return ((List<Integer>)entities).stream().mapToInt(i -> i).toArray();
} else {
//1.17.0
return new int[]{(int) entities};
}
} else {
return (int[]) entities;
}
}
/**
* Returns class from given potential names. Supports modded servers, such as Thermos.
*
* @param names
* Potential class names
* @return class from given name
* @throws ClassNotFoundException
* if class was not found
*/
public static Class<?> getClass(@NotNull String... names) throws ClassNotFoundException {
for (String name : names) {
try {
if (BukkitReflection.getMinorVersion() >= 17) {
return PacketEntityView.class.getClassLoader().loadClass("net.minecraft." + name);
} else {
return PacketEntityView.class.getClassLoader().loadClass("net.minecraft.server." + BukkitReflection.getServerPackage() + "." + name);
}
} catch (ClassNotFoundException | NullPointerException e) {
// Wrong class name
}
}
throw new ClassNotFoundException("No class found with potential names " + Arrays.toString(names));
}
}
@@ -1,47 +1,61 @@
package me.neznamy.tab.platforms.bukkit.platform;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.platforms.bukkit.*;
import me.neznamy.tab.shared.GroupManager;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.features.injection.PipelineInjector;
import me.neznamy.tab.shared.features.types.TabFeature;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.neznamy.tab.platforms.bukkit.BukkitEventListener;
import me.neznamy.tab.platforms.bukkit.BukkitPipelineInjector;
import me.neznamy.tab.platforms.bukkit.BukkitTabCommand;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.bossbar.BukkitBossBar;
import me.neznamy.tab.platforms.bukkit.bossbar.ViaBossBar;
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.platforms.bukkit.features.WitherBossBar;
import me.neznamy.tab.platforms.bukkit.features.BukkitNameTagX;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.platforms.bukkit.hook.BukkitPremiumVanishHook;
import me.neznamy.tab.platforms.bukkit.provider.ImplementationProvider;
import me.neznamy.tab.shared.*;
import me.neznamy.tab.shared.backend.BackendPlatform;
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.hook.LuckPermsHook;
import me.neznamy.tab.shared.placeholders.PlayerPlaceholderImpl;
import me.neznamy.tab.shared.chat.TabTextColor;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.chat.component.TabKeybindComponent;
import me.neznamy.tab.shared.chat.component.TabTextComponent;
import me.neznamy.tab.shared.chat.component.TabTranslatableComponent;
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
import me.neznamy.tab.shared.features.PerWorldPlayerListConfiguration;
import me.neznamy.tab.shared.features.injection.PipelineInjector;
import me.neznamy.tab.shared.features.types.TabFeature;
import me.neznamy.tab.shared.placeholders.expansion.EmptyTabExpansion;
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
import me.neznamy.tab.shared.placeholders.types.PlayerPlaceholderImpl;
import me.neznamy.tab.shared.platform.BossBar;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.platform.impl.AdventureBossBar;
import me.neznamy.tab.shared.platform.impl.DummyBossBar;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.permission.Permission;
import net.kyori.adventure.audience.Audience;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.BiConsumer;
/**
* Implementation of Platform interface for Bukkit platform
@@ -53,14 +67,14 @@ public class BukkitPlatform implements BackendPlatform {
@NotNull
private final JavaPlugin plugin;
/** Server version */
private final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]);
/** Variables checking presence of other plugins to hook into */
private final boolean placeholderAPI = ReflectionUtils.classExists("me.clip.placeholderapi.PlaceholderAPI");
/** NMS server to get TPS from on spigot */
@Nullable private Object server;
/** TPS field */
@Nullable private Field spigotTps;
/** Spigot field for tracking TPS, the array is final and only being modified instead of re-instantiated */
private double[] recentTps;
/** Detection for presence of Paper's TPS getter */
private final boolean paperTps = ReflectionUtils.methodExists(Bukkit.class, "getTPS");
@@ -68,58 +82,125 @@ public class BukkitPlatform implements BackendPlatform {
/** Detection for presence of Paper's MSPT getter */
private final boolean paperMspt = ReflectionUtils.methodExists(Bukkit.class, "getAverageTickTime");
/** Package name of the server implementation, null on Paper 1.20.5+ / Spigot 26+ */
@Nullable
private final String serverPackage;
/** Implementation for creating new instances using content available on the server */
@NotNull
@Setter
private ImplementationProvider implementationProvider;
private final boolean modernOnlinePlayers;
/** Command map for dynamic command registering */
private final SimpleCommandMap commandMap;
private final Map<String, Command> knownCommands;
/** List of custom commands registered to be able to unregister them on reload */
private final List<Command> customCommands = new ArrayList<>();
/**
* Constructs new instance with given plugin.
*
* @param plugin
* Plugin
*/
@SneakyThrows
@SuppressWarnings("unchecked")
public BukkitPlatform(@NotNull JavaPlugin plugin) {
this.plugin = plugin;
modernOnlinePlayers = Bukkit.class.getMethod("getOnlinePlayers").getReturnType() == Collection.class;
String CRAFTBUKKIT_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
String[] array = CRAFTBUKKIT_PACKAGE.split("\\.");
serverPackage = array.length > 3 ? array[3] : null;
implementationProvider = findImplementationProvider();
logInfo(new TabTextComponent("Found NMS implementation: " + implementationProvider.getClass().getName(), TabTextColor.GRAY));
try {
server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
spigotTps = server.getClass().getField("recentTps");
} catch (ReflectiveOperationException e) {
Object server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
recentTps = ((double[]) server.getClass().getField("recentTps").get(server));
} catch (ReflectiveOperationException ignored) {
//not spigot
}
if (Bukkit.getPluginManager().isPluginEnabled("PremiumVanish")) {
new BukkitPremiumVanishHook().register();
}
commandMap = (SimpleCommandMap) Bukkit.getServer().getClass().getMethod("getCommandMap").invoke(Bukkit.getServer());
knownCommands = (Map<String, Command>) ReflectionUtils.getField(SimpleCommandMap.class, "knownCommands").get(commandMap);
}
/**
* Finds implementation provider for current server software and version.
*
* @return Implementation provider for current server
* @throws IllegalStateException
* If no implementation was found
*/
@NotNull
private ImplementationProvider findImplementationProvider() {
if (serverPackage != null) {
// Paper <1.20.5 or Spigot 1.x
try {
// Does not actually support flat 1.19, but whatever, no one is using it anyway
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + serverPackage + ".NMSImplementationProvider").getConstructor().newInstance();
} catch (ReflectiveOperationException ignored) {
throw new IllegalStateException("Implementation for version \"" + serverPackage + "\" was not found.");
}
}
Map<ProtocolVersion, String> spigotVersions = new LinkedHashMap<>();
spigotVersions.put(ProtocolVersion.V26_1, "v26_1");
Map<ProtocolVersion, String> paperVersions = new LinkedHashMap<>();
paperVersions.put(ProtocolVersion.V1_20_5, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_20_6, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21_1, "paper_1_20_5");
paperVersions.put(ProtocolVersion.V1_21_2, "paper_1_21_2");
paperVersions.put(ProtocolVersion.V1_21_3, "paper_1_21_2");
paperVersions.put(ProtocolVersion.V1_21_4, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_5, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_6, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_7, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_8, "paper_1_21_4");
paperVersions.put(ProtocolVersion.V1_21_9, "paper_1_21_9");
paperVersions.put(ProtocolVersion.V1_21_10, "paper_1_21_9");
paperVersions.put(ProtocolVersion.V1_21_11, "paper_1_21_11");
String software;
Map<ProtocolVersion, String> versions;
if (paperTps) { // This needs a better check
software = "Paper";
versions = paperVersions;
} else {
software = "Spigot";
versions = spigotVersions;
}
String serverVersionString = Bukkit.getBukkitVersion().split("-")[0];
String implementation = versions.get(serverVersion);
if (implementation == null) {
throw new IllegalStateException(String.format("No implementation is available for your server version (%s %s).", software, serverVersionString));
}
try {
return (ImplementationProvider) Class.forName("me.neznamy.tab.platforms.bukkit." + implementation + ".NMSImplementationProvider").getConstructor().newInstance();
} catch (ClassNotFoundException e) {
throw new IllegalStateException(String.format("Your server version (%s %s) is marked as compatible, but the implementation does not exist. This is probably a bug.", software, serverVersionString), e);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(String.format("Failed to initialize implementation for %s %s. This is probably a bug.", software, serverVersionString), e);
}
}
@Override
@NotNull
public BossBarManagerImpl getLegacyBossBar() {
return new WitherBossBar(plugin);
}
@Override
public void loadPlayers() {
for (Player p : BukkitUtils.getOnlinePlayers()) {
for (Player p : getOnlinePlayers()) {
TAB.getInstance().addPlayer(new BukkitTabPlayer(this, p));
}
}
@Override
public void registerPlaceholders() {
PlaceholderManagerImpl manager = TAB.getInstance().getPlaceholderManager();
manager.registerServerPlaceholder("%vault-prefix%", -1, () -> "");
manager.registerServerPlaceholder("%vault-suffix%", -1, () -> "");
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
RegisteredServiceProvider<Chat> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
if (rspChat != null) {
Chat chat = rspChat.getProvider();
manager.registerPlayerPlaceholder("%vault-prefix%", 1000, p -> chat.getPlayerPrefix((Player) p.getPlayer()));
manager.registerPlayerPlaceholder("%vault-suffix%", 1000, p -> chat.getPlayerSuffix((Player) p.getPlayer()));
}
}
// Override for the PAPI placeholder to prevent console errors on unsupported server versions when ping field changes
manager.registerPlayerPlaceholder("%player_ping%", manager.getRefreshInterval("%player_ping%"),
p -> ((TabPlayer)p).getPing());
BackendPlatform.super.registerPlaceholders();
}
@Override
@Nullable
public PipelineInjector createPipelineInjector() {
return NMSStorage.getInstance().getMinorVersion() >= 8 ? new BukkitPipelineInjector() : null;
}
@Override
@NotNull
public NameTag getUnlimitedNameTags() {
return new BukkitNameTagX(plugin);
return ReflectionUtils.classExists("io.netty.channel.Channel") ? new BukkitPipelineInjector() : null;
}
@Override
@@ -135,96 +216,89 @@ public class BukkitPlatform implements BackendPlatform {
@Override
@Nullable
public TabFeature getPerWorldPlayerList() {
return new PerWorldPlayerList(plugin);
public TabFeature getPerWorldPlayerList(@NotNull PerWorldPlayerListConfiguration configuration) {
return new PerWorldPlayerList(plugin, this, configuration);
}
@Override
public void registerUnknownPlaceholder(@NotNull String identifier) {
if (!placeholderAPI) {
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, -1, () -> identifier);
registerDummyPlaceholder(identifier);
return;
}
PlaceholderManagerImpl pl = TAB.getInstance().getPlaceholderManager();
int refresh = pl.getRefreshInterval(identifier);
if (identifier.startsWith("%rel_")) {
//relational placeholder
TAB.getInstance().getPlaceholderManager().registerRelationalPlaceholder(identifier, pl.getRefreshInterval(identifier), (viewer, target) ->
TAB.getInstance().getPlaceholderManager().registerRelationalPlaceholder(identifier, (viewer, target) ->
PlaceholderAPI.setRelationalPlaceholders((Player) viewer.getPlayer(), (Player) target.getPlayer(), identifier));
} else if (identifier.startsWith("%sync:")) {
registerSyncPlaceholder(identifier, refresh);
registerSyncPlaceholder(identifier);
} else if (identifier.startsWith("%server_")) {
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, refresh,
() -> PlaceholderAPI.setPlaceholders(null, identifier));
boolean stripLeadingAsterisk = identifier.startsWith("%server_tps");
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier,
() -> {
String out = PlaceholderAPI.setPlaceholders(null, identifier);
return stripLeadingAsterisk && out != null && out.startsWith("*") ? out.substring(1) : out;
});
} else {
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, refresh,
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier,
p -> PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), identifier));
}
}
public void registerSyncPlaceholder(@NotNull String identifier, int refresh) {
/**
* Registers a sync placeholder with given identifier and automatically decided refresh.
*
* @param identifier
* Placeholder identifier
*/
public void registerSyncPlaceholder(@NotNull String identifier) {
String syncedPlaceholder = "%" + identifier.substring(6);
PlayerPlaceholderImpl[] ppl = new PlayerPlaceholderImpl[1];
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, refresh, p -> {
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, p -> {
Bukkit.getScheduler().runTask(plugin, () -> {
long time = System.nanoTime();
ppl[0].updateValue(p, placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), syncedPlaceholder) : identifier);
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, System.nanoTime()-time);
long totalTime = System.nanoTime()-time;
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, totalTime);
TAB.getInstance().getCpu().addTime(TAB.getInstance().getPlaceholderManager().getFeatureName(), TabConstants.CpuUsageCategory.PLACEHOLDER_REQUEST, totalTime);
});
return null;
});
}
@Override
public void logInfo(@NotNull IChatBaseComponent message) {
Bukkit.getConsoleSender().sendMessage("[TAB] " + BukkitUtils.toBukkitFormat(message, true));
public void logInfo(@NotNull TabComponent message) {
Bukkit.getConsoleSender().sendMessage("[TAB] " + toBukkitFormat(message));
}
@Override
public void logWarn(@NotNull IChatBaseComponent message) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED.getFormat() + "[TAB] [WARN] " + BukkitUtils.toBukkitFormat(message, true));
}
@Override
@NotNull
public String getServerVersionInfo() {
return "[Bukkit] " + Bukkit.getName() + " - " +
Bukkit.getBukkitVersion().split("-")[0] +
" (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")";
public void logWarn(@NotNull TabComponent message) {
Bukkit.getConsoleSender().sendMessage("§c[TAB] [WARN] " + toBukkitFormat(message));
}
@Override
public void registerListener() {
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(), plugin);
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(this), plugin);
}
@Override
public void registerCommand() {
PluginCommand command = Bukkit.getPluginCommand(TabConstants.COMMAND_BACKEND);
PluginCommand command = Bukkit.getPluginCommand(getCommand());
if (command != null) {
BukkitTabCommand cmd = new BukkitTabCommand();
command.setExecutor(cmd);
command.setTabCompleter(cmd);
} else {
logWarn(new IChatBaseComponent("Failed to register command, is it defined in plugin.yml?"));
logWarn(new TabTextComponent("Failed to register command, is it defined in plugin.yml?", TabTextColor.RED));
}
}
@Override
public void startMetrics() {
Metrics metrics = new Metrics(plugin, 5304);
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.UNLIMITED_NAME_TAG_MODE_ENABLED,
() -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
Metrics metrics = new Metrics(plugin, TabConstants.BSTATS_PLUGIN_ID_BUKKIT);
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM,
() -> TAB.getInstance().getGroupManager().getPermissionPlugin()));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.SERVER_VERSION,
() -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
}
@Override
@NotNull
public ProtocolVersion getServerVersion() {
return ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]);
metrics.addCustomChart(new SimplePie("tab_6_0_0_servers", serverVersion::getFriendlyName));
}
@Override
@@ -233,41 +307,97 @@ public class BukkitPlatform implements BackendPlatform {
return plugin.getDataFolder();
}
/**
* Converts internal component class to platform's component class
*
* @param component
* Component to convert
* @param version
* Game version to convert component for
* @return Converted component
*/
public Object toComponent(@NotNull IChatBaseComponent component, @NotNull ProtocolVersion version) {
return NMSStorage.getInstance().componentCache.get(component, version);
@Override
@NotNull
public Object convertComponent(@NotNull TabComponent component) {
return implementationProvider.getComponentConverter().convert(component);
}
@Override
@NotNull
public GroupManager detectPermissionPlugin() {
if (LuckPermsHook.getInstance().isInstalled()) {
return new GroupManager("LuckPerms", LuckPermsHook.getInstance().getGroupFunction());
}
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
RegisteredServiceProvider<Permission> provider = Bukkit.getServicesManager().getRegistration(Permission.class);
if (provider != null && !provider.getProvider().getName().equals("SuperPerms")) {
return new GroupManager(provider.getProvider().getName(), p -> provider.getProvider().getPrimaryGroup((Player) p.getPlayer()));
}
}
return new GroupManager("None", p -> TabConstants.NO_GROUP);
public Scoreboard createScoreboard(@NotNull TabPlayer player) {
return implementationProvider.newScoreboard((BukkitTabPlayer) player);
}
@Override
@NotNull
public BossBar createBossBar(@NotNull TabPlayer player) {
//noinspection ConstantValue
if (AdventureBossBar.isAvailable() && Audience.class.isAssignableFrom(Player.class)) return new AdventureBossBar(player);
// 1.9+ server, handle using API, potential 1.8 players are handled by ViaVersion
if (BukkitBossBar.isAvailable()) return new BukkitBossBar((BukkitTabPlayer) player);
// 1.9+ player on 1.8 server, handle using ViaVersion API
if (player.getVersion().getNetworkId() >= ProtocolVersion.V1_9.getNetworkId()) return new ViaBossBar((BukkitTabPlayer) player);
// 1.8- server and player, no implementation
return new DummyBossBar();
}
@Override
@NotNull
public TabList createTabList(@NotNull TabPlayer player) {
return implementationProvider.newTabList((BukkitTabPlayer) player);
}
@Override
public boolean supportsScoreboards() {
return true;
}
@Override
public boolean supportsListed() {
return serverVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId();
}
@Override
public boolean supportsListOrder() {
return serverVersion.getNetworkId() >= ProtocolVersion.V1_21_2.getNetworkId();
}
@Override
public boolean isSafeFromPacketEventsBug() {
return serverVersion.getMinorVersion() >= 13;
}
@Override
public void registerCustomCommand(@NotNull String commandName, @NotNull BiConsumer<TabPlayer, String[]> function) {
Command cmd = new BukkitCommand(commandName) {
@Override
public boolean execute(@NotNull CommandSender commandSender, @NotNull String alias, @NotNull String[] args) {
if (commandSender instanceof Player) {
TabPlayer p = TAB.getInstance().getPlayer(((Player) commandSender).getUniqueId());
if (p == null) return false; //player not loaded correctly
function.accept(p, args);
} else {
commandSender.sendMessage(toBukkitFormat(
TabComponent.fromColoredText(TAB.getInstance().getConfiguration().getMessages().getCommandOnlyFromGame())
));
}
return false;
}
};
commandMap.register(commandName, cmd);
customCommands.add(cmd);
}
@Override
public void unregisterAllCustomCommands() {
for (Command command : customCommands) {
knownCommands.remove(command.getName());
knownCommands.remove(command.getName() + ":" + command.getName());
command.unregister(commandMap);
}
}
@Override
@SneakyThrows
public double getTPS() {
if (paperTps) {
if (recentTps != null) {
return recentTps[0];
} else if (paperTps) {
return Bukkit.getTPS()[0];
} else if (spigotTps != null) {
return ((double[]) spigotTps.get(server))[0];
} else {
return -1;
}
@@ -275,11 +405,98 @@ public class BukkitPlatform implements BackendPlatform {
@Override
public double getMSPT() {
if (paperMspt) return Bukkit.getAverageTickTime();
return -1;
if (paperMspt) return Bukkit.getAverageTickTime();
return -1;
}
public void runEntityTask(@NotNull Entity entity, @NotNull Runnable task) {
task.run();
/**
* Runs task in the main thread for given entity.
*
* @param entity
* Entity's main thread
* @param task
* Task to run
*/
public void runSync(@NotNull Entity entity, @NotNull Runnable task) {
Bukkit.getScheduler().runTask(plugin, task);
}
}
/**
* Converts component to string using bukkit RGB format if supported by the server.
* If not, closest legacy color is used instead.
*
* @param component
* Component to convert
* @return Converted string using bukkit color format
*/
@NotNull
public String toBukkitFormat(@NotNull TabComponent component) {
StringBuilder sb = new StringBuilder();
if (component.getModifier().getColor() != null) {
if (serverVersion.getNetworkId() >= ProtocolVersion.V1_16.getNetworkId()) {
String hexCode = component.getModifier().getColor().getHexCode();
sb.append('§').append("x").append('§').append(hexCode.charAt(0)).append('§').append(hexCode.charAt(1))
.append('§').append(hexCode.charAt(2)).append('§').append(hexCode.charAt(3))
.append('§').append(hexCode.charAt(4)).append('§').append(hexCode.charAt(5));
} else {
sb.append('§').append(component.getModifier().getColor().getLegacyColor().getCharacter());
}
}
sb.append(component.getModifier().getMagicCodes());
if (component instanceof TabTextComponent) {
sb.append(((TabTextComponent) component).getText());
} else if (component instanceof TabTranslatableComponent) {
sb.append(((TabTranslatableComponent) component).getKey());
} else if (component instanceof TabKeybindComponent) {
sb.append(((TabKeybindComponent) component).getKeybind());
} else if (component instanceof TabObjectComponent) {
sb.append(component.toLegacyText());
} else {
throw new IllegalStateException("Unexpected component type: " + component.getClass().getName());
}
for (TabComponent extra : component.getExtra()) {
sb.append(toBukkitFormat(extra));
}
return sb.toString();
}
/**
* Returns online players from Bukkit API.
* This method may use reflections, because the return type changed in 1.7.10,
* and we want to avoid errors.
*
* @return Online players from Bukkit API.
*/
@SneakyThrows
@NotNull
public Collection<? extends Player> getOnlinePlayers() {
if (modernOnlinePlayers) {
return Bukkit.getOnlinePlayers();
}
return Arrays.asList((Player[]) Bukkit.class.getMethod("getOnlinePlayers").invoke(null));
}
@Override
@NotNull
public Object dump() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("server-type", "Bukkit");
map.put("server-name", Bukkit.getName());
map.put("server-version", Bukkit.getBukkitVersion().split("-")[0]);
map.put("craftbukkit-package", serverPackage);
map.put("tab-version", ProjectVariables.PLUGIN_VERSION);
Map<String, Object> plugins = new LinkedHashMap<>();
for (Plugin p : Bukkit.getPluginManager().getPlugins()) {
plugins.put(p.getDescription().getName(), p.getDescription().getVersion());
}
map.put("plugins", plugins);
if (placeholderAPI) {
Map<String, String> expansions = new LinkedHashMap<>();
for (PlaceholderExpansion p : PlaceholderAPIPlugin.getInstance().getLocalExpansionManager().getExpansions()) {
expansions.put(p.getIdentifier(), p.getVersion());
}
map.put("placeholderapi-expansions", expansions);
}
return map;
}
}
@@ -1,13 +1,16 @@
package me.neznamy.tab.platforms.bukkit.platform;
import lombok.NonNull;
import lombok.SneakyThrows;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.placeholders.PlayerPlaceholderImpl;
import me.neznamy.tab.shared.cpu.TimedCaughtTask;
import me.neznamy.tab.shared.data.World;
import me.neznamy.tab.shared.placeholders.types.PlayerPlaceholderImpl;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerChangedWorldEvent;
@@ -15,13 +18,22 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Platform override for Folia
* Platform override for Folia.
*/
public class FoliaPlatform extends BukkitPlatform {
/**
* Constructs new instance with given plugin.
*
* @param plugin
* Plugin
*/
public FoliaPlatform(@NotNull JavaPlugin plugin) {
super(plugin);
}
@@ -30,40 +42,88 @@ public class FoliaPlatform extends BukkitPlatform {
public void loadPlayers() {
super.loadPlayers();
// Values are never updated in the API, warn users
logWarn(new IChatBaseComponent("Folia never updates MSPT and TPS values in the API, making " +
"%mspt% and %tps% return the default values (0 and 20)."));
// Folia never calls PlayerChangedWorldEvent, this is a workaround
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(100, "Folia compatibility", "Refreshing world", () -> {
TAB.getInstance().getCpu().getProcessingThread().repeatTask(new TimedCaughtTask(TAB.getInstance().getCpu(), () -> {
for (TabPlayer player : TAB.getInstance().getOnlinePlayers()) {
String bukkitWorld = ((Player)player.getPlayer()).getWorld().getName();
if (!player.getWorld().equals(bukkitWorld)) {
TAB.getInstance().getFeatureManager().onWorldChange(player.getUniqueId(), bukkitWorld);
World actualWorld = World.byName(((Player) player.getPlayer()).getWorld().getName());
if (player.world != actualWorld) {
TAB.getInstance().getFeatureManager().onWorldChange(player.getUniqueId(), actualWorld);
PerWorldPlayerList pwp = TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST);
if (pwp != null) {
runSync((Entity) player.getPlayer(), () -> pwp.onWorldChange(new PlayerChangedWorldEvent((Player) player.getPlayer(), ((Player) player.getPlayer()).getWorld())));
}
}
}
});
}, "Folia compatibility", "Refreshing world"), 100);
}
@Override
public void registerSyncPlaceholder(@NotNull String identifier, int refresh) {
public void registerPlaceholders() {
super.registerPlaceholders();
DecimalFormat decimal2;
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator('.');
decimal2 = new DecimalFormat("#.##", symbols);
registerInternalSyncPlaceholder(TabConstants.Placeholder.MSPT, p -> decimal2.format(Bukkit.getAverageTickTime()));
registerInternalSyncPlaceholder(TabConstants.Placeholder.TPS, p -> decimal2.format(Math.min(20, Bukkit.getTPS()[0])));
}
@Override
public void registerSyncPlaceholder(@NotNull String identifier) {
String syncedPlaceholder = "%" + identifier.substring(6);
PlayerPlaceholderImpl[] ppl = new PlayerPlaceholderImpl[1];
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, refresh, p -> {
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, p -> {
runSync((Entity) p.getPlayer(), () -> {
long time = System.nanoTime();
String output = isPlaceholderAPI() ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), syncedPlaceholder) : identifier;
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, System.nanoTime()-time);
long totalTime = System.nanoTime()-time;
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, totalTime);
TAB.getInstance().getCpu().addTime(TAB.getInstance().getPlaceholderManager().getFeatureName(), TabConstants.CpuUsageCategory.PLACEHOLDER_REQUEST, totalTime);
TAB.getInstance().getCPUManager().runTask(() -> ppl[0].updateValue(p, output)); // To ensure player is loaded
});
return null;
});
}
private void registerInternalSyncPlaceholder(@NonNull String identifier, @NonNull Function<TabPlayer, String> function) {
PlayerPlaceholderImpl[] ppl = new PlayerPlaceholderImpl[1];
ppl[0] = TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, p -> {
runSync((Entity) p.getPlayer(), () -> {
long time = System.nanoTime();
String output = function.apply((TabPlayer) p);
long totalTime = System.nanoTime()-time;
TAB.getInstance().getCPUManager().addPlaceholderTime(identifier, totalTime);
TAB.getInstance().getCpu().addTime(TAB.getInstance().getPlaceholderManager().getFeatureName(), TabConstants.CpuUsageCategory.PLACEHOLDER_REQUEST, totalTime);
TAB.getInstance().getCPUManager().runTask(() -> ppl[0].updateValue(p, output)); // To ensure player is loaded
});
return null;
});
}
/**
* Overriding the method to fix initial error caused by ServerPlaceholder implementation trying to
* retrieve the value in constructor, but folia does not support that. Overriding this function to avoid the
* MSPT function being called in the wrong thread.
*
* @return -1
*/
@Override
public double getMSPT() {
return -1;
}
/**
* Overriding the method to fix initial error caused by ServerPlaceholder implementation trying to
* retrieve the value in constructor, but folia does not support that. Overriding this function to avoid the
* TPS function being called in the wrong thread.
*
* @return -1
*/
@Override
public double getTPS() {
return -1;
}
/**
* Runs task using player's entity scheduler. It's using reflection, because
* Folia uses Java 17 while TAB maintains Java 8 compatibility for compatibility
@@ -74,17 +134,13 @@ public class FoliaPlatform extends BukkitPlatform {
* @param task
* Task to run
*/
@Override
@SneakyThrows
@SuppressWarnings("JavaReflectionMemberAccess")
private void runSync(@NotNull Entity entity, @NotNull Runnable task) {
public void runSync(@NotNull Entity entity, @NotNull Runnable task) {
Object entityScheduler = Entity.class.getMethod("getScheduler").invoke(entity);
Consumer<?> consumer = $ -> task.run(); // Reflection and lambdas don't go together
entityScheduler.getClass().getMethod("run", Plugin.class, Consumer.class, Runnable.class)
.invoke(entityScheduler, getPlugin(), consumer, null);
}
@Override
public void runEntityTask(@NotNull Entity entity, @NotNull Runnable task) {
runSync(entity, task);
}
}
@@ -0,0 +1,134 @@
package me.neznamy.tab.platforms.bukkit.provider;
import me.neznamy.tab.shared.chat.TabStyle;
import me.neznamy.tab.shared.chat.component.TabKeybindComponent;
import me.neznamy.tab.shared.chat.component.TabComponent;
import me.neznamy.tab.shared.chat.component.TabTextComponent;
import me.neznamy.tab.shared.chat.component.TabTranslatableComponent;
import me.neznamy.tab.shared.chat.component.object.TabAtlasSprite;
import me.neznamy.tab.shared.chat.component.object.TabObjectComponent;
import me.neznamy.tab.shared.chat.component.object.TabPlayerSprite;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Interface for converting TAB components into NMS components (1.7+).
*
* @param <T>
* NMS component type
*/
public abstract class ComponentConverter<T> {
/** Empty UUID for creating dummy profiles */
protected final UUID NIL_UUID = new UUID(0, 0);
/**
* Converts TAB component to NMS component.
*
* @param component
* Component to convert
* @return Converted component
*/
@NotNull
public T convert(@NotNull TabComponent component) {
// Component type
T nmsComponent;
if (component instanceof TabTextComponent) {
nmsComponent = newTextComponent(((TabTextComponent) component).getText());
} else if (component instanceof TabTranslatableComponent) {
nmsComponent = newTranslatableComponent(((TabTranslatableComponent) component).getKey());
} else if (component instanceof TabKeybindComponent) {
nmsComponent = newKeybindComponent(((TabKeybindComponent)component).getKeybind());
} else if (component instanceof TabObjectComponent) {
if ((((TabObjectComponent) component).getContents() instanceof TabAtlasSprite)) {
nmsComponent = newObjectComponent((TabAtlasSprite) ((TabObjectComponent) component).getContents());
} else if ((((TabObjectComponent) component).getContents() instanceof TabPlayerSprite)) {
nmsComponent = newObjectComponent((TabPlayerSprite) ((TabObjectComponent) component).getContents());
} else {
throw new IllegalArgumentException("Unexpected object component type: " + ((TabObjectComponent) component).getContents().getClass().getName());
}
} else {
throw new IllegalArgumentException("Unexpected component type: " + component.getClass().getName());
}
// Component style
applyStyle(nmsComponent, component.getModifier());
// Extra
for (TabComponent extra : component.getExtra()) {
addSibling(nmsComponent, convert(extra));
}
return nmsComponent;
}
/**
* Creates a new text component with given text.
*
* @param text
* Text to display
* @return Text component with given text
*/
@NotNull
public abstract T newTextComponent(@NotNull String text);
/**
* Creates a new translatable component with the given key.
*
* @param key
* Key to translate
* @return Translatable component with the given key
*/
@NotNull
public abstract T newTranslatableComponent(@NotNull String key);
/**
* Creates a new keybind component with given keybind.
*
* @param keybind
* Keybind to show
* @return Keybind component with given keybind
*/
@NotNull
public abstract T newKeybindComponent(@NotNull String keybind);
/**
* Creates a new object component with given atlas and sprite.
*
* @param sprite Sprite to use
* @return Object component with given atlas and sprite
*/
@NotNull
public abstract T newObjectComponent(@NotNull TabAtlasSprite sprite);
/**
* Creates a new head component with given skin.
*
* @param sprite
* Skin to use
* @return Head component with given skin
*/
@NotNull
public abstract T newObjectComponent(@NotNull TabPlayerSprite sprite);
/**
* Converts given chat modifier to minecraft style and applies it to the component.
*
* @param nmsComponent
* Component to apply style to
* @param style
* Style to convert and apply
*/
public abstract void applyStyle(@NotNull T nmsComponent, @NotNull TabStyle style);
/**
* Appends child to the given parent component.
*
* @param parent
* Parent to append the child to
* @param child
* Child component to append
*/
public abstract void addSibling(@NotNull T parent, @NotNull T child);
}
@@ -0,0 +1,71 @@
package me.neznamy.tab.platforms.bukkit.provider;
import io.netty.channel.Channel;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.TabListEntryTracker;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* An interface to provide platform-specific implementations of various features in various Minecraft versions.
*/
public interface ImplementationProvider {
/**
* Creates a new instance of scoreboard for the given player.
*
* @param player
* Player to create scoreboard for
* @return Newly created scoreboard
*/
@NotNull
Scoreboard newScoreboard(@NotNull BukkitTabPlayer player);
/**
* Creates a new instance of tab list for the given player.
*
* @param player
* Player to create tab list for
* @return Newly created tab list
*/
@NotNull
TabList newTabList(@NotNull BukkitTabPlayer player);
/**
* Returns component converter to be used for converting TAB components to NMS components.
*
* @return Component converter
*/
@NotNull
ComponentConverter<?> getComponentConverter();
/**
* Gets player's channel. If not available, returns {@code null}.
*
* @param player
* Player to get channel of
* @return Player's channel or {@code null} if not supported
*/
@Nullable
Channel getChannel(@NotNull Player player);
/**
* Creates a new instance of tab list entry tracker.
*
* @return Newly created tab list entry tracker
*/
@NotNull
TabListEntryTracker newTabListEntryTracker();
/**
* Returns ping of the given player.
*
* @param player
* Player to get ping of
* @return Player's ping
*/
int getPing(@NotNull BukkitTabPlayer player);
}
@@ -1,219 +0,0 @@
package me.neznamy.tab.platforms.bukkit.scoreboard;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.BukkitUtils;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.scoreboard.NameTagVisibility;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.RenderType;
import org.bukkit.scoreboard.Team;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* Scoreboard implementation using Bukkit API (1.5.2+). It has several issues:
* #1 - Limitations on legacy versions are forced in the API.
* While this may not seem like a problem, it enforces those
* limits even for 1.13+ players (if using ViaVersion).<p>
* #2 - Modern versions no longer have any limits, but md_5
* decided to add some random limits for absolutely no reason
* at all. Scoreboard title received a random 128 characters
* limit including color codes. Together with the almighty bukkit
* RGB format using 14 characters for 1 color code, this makes
* gradients just impossible to use. Team prefix/suffix also
* received a 64 characters limit (excluding color codes at least),
* however that might not be enough for displaying a line of text
* in sidebar, which would require splitting the text into prefix
* and suffix, which is just begging for bugs to be introduced.<p>
* #3 - Other plugins can decide to put players into their own
* scoreboard, automatically destroying all visuals made by the
* plugin. They might also put all players into the same scoreboard,
* making per-player view of teams, especially sidebar not working.<p>
*/
// Throw the NPE if something is not as expected, parent class should ensure it anyway
@SuppressWarnings({"ConstantConditions", "deprecation"})
public class BukkitScoreboard extends Scoreboard<BukkitTabPlayer> {
/** Flag tracking whether this implementation is available for use */
@Getter
private static final boolean available = ReflectionUtils.methodExists(Bukkit.class, "getScoreboardManager");
/** Server's minor version */
private final int serverMinorVersion = BukkitReflection.getMinorVersion();
@NotNull
protected final org.bukkit.scoreboard.Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
public BukkitScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
// Put player into a different scoreboard for per-player view
player.getPlayer().setScoreboard(scoreboard);
}
@Override
public void setDisplaySlot(@NotNull DisplaySlot slot, @NotNull String objective) {
checkPlayerScoreboard();
scoreboard.getObjective(objective).setDisplaySlot(org.bukkit.scoreboard.DisplaySlot.valueOf(slot.name()));
}
@Override
public void setScore0(@NotNull String objective, @NotNull String playerName, int score) {
checkPlayerScoreboard();
if (serverMinorVersion >= 7 && TAB.getInstance().getServerVersion().getNetworkId() >= ProtocolVersion.V1_7_8.getNetworkId()) {
scoreboard.getObjective(objective).getScore(playerName).setScore(score);
} else {
scoreboard.getObjective(objective).getScore(Bukkit.getOfflinePlayer(playerName)).setScore(score);
}
}
@Override
public void removeScore0(@NotNull String objective, @NotNull String playerName) {
checkPlayerScoreboard();
if (serverMinorVersion >= 7 && TAB.getInstance().getServerVersion().getNetworkId() >= ProtocolVersion.V1_7_8.getNetworkId()) {
scoreboard.resetScores(playerName);
} else {
scoreboard.resetScores(Bukkit.getOfflinePlayer(playerName));
}
}
@Override
public void registerObjective0(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
checkPlayerScoreboard();
newObjective(objectiveName, "dummy", title, display);
}
@Override
public void unregisterObjective0(@NotNull String objectiveName) {
checkPlayerScoreboard();
scoreboard.getObjective(objectiveName).unregister();
}
@Override
public void updateObjective0(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
checkPlayerScoreboard();
Objective obj = scoreboard.getObjective(objectiveName);
setDisplayName(obj, title);
if (serverMinorVersion >= 14) obj.setRenderType(RenderType.valueOf(display.name()));
}
@Override
public void registerTeam0(@NotNull String name, @NotNull String prefix, @NotNull String suffix,
@NotNull NameVisibility visibility, @NotNull CollisionRule collision,
@NotNull Collection<String> players, int options) {
checkPlayerScoreboard();
Team team = scoreboard.registerNewTeam(name);
setPrefix(team, prefix);
setSuffix(team, suffix);
if (serverMinorVersion >= 8)
team.setNameTagVisibility(NameTagVisibility.valueOf(visibility.name()));
if (serverMinorVersion >= 9)
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.values()[collision.ordinal()]);
if (serverMinorVersion >= 13)
team.setColor(ChatColor.valueOf(EnumChatFormat.lastColorsOf(prefix).name()));
if (serverMinorVersion >= 7 && TAB.getInstance().getServerVersion().getNetworkId() >= ProtocolVersion.V1_7_8.getNetworkId()) {
players.forEach(team::addEntry);
} else {
players.forEach(player -> team.addPlayer(Bukkit.getOfflinePlayer(player)));
}
team.setAllowFriendlyFire((options & 0x01) != 0);
team.setCanSeeFriendlyInvisibles((options & 0x02) != 0);
}
@Override
public void unregisterTeam0(@NotNull String name) {
checkPlayerScoreboard();
scoreboard.getTeam(name).unregister();
}
@Override
public void updateTeam0(@NotNull String name, @NotNull String prefix, @NotNull String suffix,
@NotNull NameVisibility visibility, @NotNull CollisionRule collision, int options) {
checkPlayerScoreboard();
Team team = scoreboard.getTeam(name);
setPrefix(team, prefix);
setSuffix(team, suffix);
if (serverMinorVersion >= 8)
team.setNameTagVisibility(NameTagVisibility.valueOf(visibility.name()));
if (serverMinorVersion >= 9)
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.values()[collision.ordinal()]);
if (serverMinorVersion >= 13)
team.setColor(ChatColor.valueOf(EnumChatFormat.lastColorsOf(prefix).name()));
team.setAllowFriendlyFire((options & 0x01) != 0);
team.setCanSeeFriendlyInvisibles((options & 0x02) != 0);
}
public void newObjective(String objectiveName, String criteria, String title, @NotNull HealthDisplay display) {
if (serverMinorVersion >= 14) {
scoreboard.registerNewObjective(objectiveName, criteria, transform(title, 128, 32), RenderType.valueOf(display.name()));
} else {
setDisplayName(scoreboard.registerNewObjective(objectiveName, display == HealthDisplay.HEARTS ? "health" : "dummy"), title);
}
}
public void setDisplayName(@NotNull Objective objective, @NotNull String displayName) {
objective.setDisplayName(transform(displayName, 128, 32));
}
public void setPrefix(@NotNull Team team, @NotNull String prefix) {
team.setPrefix(transform(prefix, 64, 16));
}
public void setSuffix(@NotNull Team team, @NotNull String suffix) {
team.setSuffix(transform(suffix, 64, 16));
}
/**
* Transforms text into format using bukkit RGB syntax if server version is 1.16+
*
* @param text
* Text to transform
* @param maxLengthModern
* Maximum text length defined by bukkit API on 1.13+
* @param maxLengthLegacy
* Maximum text length defined by bukkit API on 1.12-
* @return Converted text
*/
@NotNull
private String transform(@NotNull String text, int maxLengthModern, int maxLengthLegacy) {
String transformed = BukkitUtils.toBukkitFormat(IChatBaseComponent.optimizedComponent(text), player.getVersion().getMinorVersion() >= 16);
if (serverMinorVersion >= 16 && maxLengthModern < 128) { // Scoreboard title is not stripping colors
while (ChatColor.stripColor(transformed).length() > maxLengthModern)
transformed = transformed.substring(0, transformed.length()-1);
} else if (serverMinorVersion >= 13) {
if (transformed.length() > maxLengthModern)
transformed = transformed.substring(0, maxLengthModern);
} else {
if (transformed.length() > maxLengthLegacy)
transformed = transformed.substring(0, maxLengthLegacy);
}
return transformed;
}
/**
* Makes sure player is in the correct scoreboard.
* If not, puts the player into correct scoreboard and prints
* a warning.
* The only possible reason for this to happen is another plugin
* putting a player into a different scoreboard.
* Sadly there is no efficient solution to this, which md_5 fails
* to understand and keeps saying you don't need packets for scoreboards.
*/
private void checkPlayerScoreboard() {
if (player.getPlayer().getScoreboard() != scoreboard) {
player.getPlayer().setScoreboard(scoreboard);
TAB.getInstance().getErrorManager().printError("Player " + player.getName() + " was in a different scoreboard " +
"than expected. This means another plugin changed player's scoreboard.");
}
}
}
@@ -1,432 +0,0 @@
package me.neznamy.tab.platforms.bukkit.scoreboard;
import lombok.Getter;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import static me.neznamy.tab.platforms.bukkit.nms.BukkitReflection.getLegacyClass;
/**
* Scoreboard implementation for Bukkit, which uses packets
* to send scoreboards to use the full potential on all versions
* and server software without any artificial limits.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public class PacketScoreboard extends Scoreboard<BukkitTabPlayer> {
@Getter
private static boolean available;
private static Object emptyScoreboard;
private static Field IScoreboardCriteria_self;
// PacketPlayOutScoreboardDisplayObjective
public static Class<?> DisplayObjectiveClass;
private static Constructor<?> newDisplayObjective;
public static Field DisplayObjective_POSITION;
public static Field DisplayObjective_OBJECTIVE_NAME;
// PacketPlayOutScoreboardScore
private static Class<Enum> EnumScoreboardAction;
private static Constructor<?> newScorePacket_1_13;
private static Constructor<?> newScorePacket_String;
private static Constructor<?> newScorePacket;
private static Constructor<?> newScoreboardScore;
private static Method ScoreboardScore_setScore;
// PacketPlayOutScoreboardObjective
public static Class<?> ObjectivePacketClass;
private static Constructor<?> newObjectivePacket;
public static Field Objective_OBJECTIVE_NAME;
public static Field Objective_METHOD;
private static Field Objective_RENDER_TYPE;
private static Field Objective_DISPLAY_NAME;
private static Class<Enum> EnumScoreboardHealthDisplay;
private static Constructor<?> newScoreboardObjective;
// PacketPlayOutScoreboardTeamStorage
public static Class<?> TeamPacketClass;
private static Constructor<?> newTeamPacket;
private static Method TeamPacketConstructor_of;
private static Method TeamPacketConstructor_ofBoolean;
public static Field TeamPacket_NAME;
public static Field TeamPacket_ACTION;
public static Field TeamPacket_PLAYERS;
private static Class<Enum> EnumNameTagVisibility;
private static Class<Enum> EnumTeamPush;
private static Constructor<?> newScoreboardTeam;
private static Method ScoreboardTeam_getPlayerNameSet;
private static Method ScoreboardTeam_setNameTagVisibility;
private static Method ScoreboardTeam_setCollisionRule;
private static Method ScoreboardTeam_setPrefix;
private static Method ScoreboardTeam_setSuffix;
private static Method ScoreboardTeam_setColor;
private static Method ScoreboardTeam_setAllowFriendlyFire;
private static Method ScoreboardTeam_setCanSeeFriendlyInvisibles;
private static Enum[] scoreboardActions;
private static Enum[] displaySlots;
private static Enum[] nameVisibilities;
private static Enum[] collisionRules;
private static Enum[] healthDisplays;
private static Enum[] chatFormats;
public static void load() throws ReflectiveOperationException {
int minorVersion = BukkitReflection.getMinorVersion();
Class<?> scoreboardTeam;
Class<?> scoreboardObjective;
Class<?> scoreboardScoreClass;
Class<?> IScoreboardCriteria;
Class<?> scoreboard;
Class<?> scorePacketClass;
Class<?> IChatBaseComponent = null;
Class<Enum> enumChatFormatClass;
if (BukkitReflection.isMojangMapped()) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.Component");
enumChatFormatClass = (Class<Enum>) Class.forName("net.minecraft.ChatFormatting");
DisplayObjectiveClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket");
ObjectivePacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket");
scoreboard = Class.forName("net.minecraft.world.scores.Scoreboard");
scorePacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetScorePacket");
scoreboardObjective = Class.forName("net.minecraft.world.scores.Objective");
scoreboardScoreClass = Class.forName("net.minecraft.world.scores.Score");
IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria");
EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType");
EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ServerScoreboard$Method");
TeamPacketClass = Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket");
scoreboardTeam = Class.forName("net.minecraft.world.scores.PlayerTeam");
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$Visibility");
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$CollisionRule");
} else if (minorVersion >= 17) {
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
enumChatFormatClass = (Class<Enum>) Class.forName("net.minecraft.EnumChatFormat");
DisplayObjectiveClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective");
ObjectivePacketClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective");
scoreboard = Class.forName("net.minecraft.world.scores.Scoreboard");
scorePacketClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore");
scoreboardObjective = Class.forName("net.minecraft.world.scores.ScoreboardObjective");
scoreboardScoreClass = Class.forName("net.minecraft.world.scores.ScoreboardScore");
IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria");
EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay");
EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ScoreboardServer$Action");
TeamPacketClass = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam");
scoreboardTeam = Class.forName("net.minecraft.world.scores.ScoreboardTeam");
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility");
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush");
} else {
enumChatFormatClass = (Class<Enum>) getLegacyClass("EnumChatFormat");
DisplayObjectiveClass = getLegacyClass("PacketPlayOutScoreboardDisplayObjective", "Packet208SetScoreboardDisplayObjective");
ObjectivePacketClass = getLegacyClass("PacketPlayOutScoreboardObjective", "Packet206SetScoreboardObjective");
TeamPacketClass = getLegacyClass("PacketPlayOutScoreboardTeam", "Packet209SetScoreboardTeam");
scorePacketClass = getLegacyClass("PacketPlayOutScoreboardScore", "Packet207SetScoreboardScore");
scoreboard = getLegacyClass("Scoreboard");
scoreboardObjective = getLegacyClass("ScoreboardObjective");
scoreboardScoreClass = getLegacyClass("ScoreboardScore");
IScoreboardCriteria = getLegacyClass("IScoreboardCriteria", "IObjective"); // 1.5.1+, 1.5
scoreboardTeam = getLegacyClass("ScoreboardTeam");
if (minorVersion >= 7) {
IChatBaseComponent = getLegacyClass("IChatBaseComponent");
}
if (minorVersion >= 8) {
EnumScoreboardHealthDisplay = (Class<Enum>) getLegacyClass("IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
EnumScoreboardAction = (Class<Enum>) getLegacyClass("ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
EnumNameTagVisibility = (Class<Enum>) getLegacyClass("ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
}
if (minorVersion >= 9) {
EnumTeamPush = (Class<Enum>) getLegacyClass("ScoreboardTeamBase$EnumTeamPush");
}
}
emptyScoreboard = scoreboard.getConstructor().newInstance();
IScoreboardCriteria_self = ReflectionUtils.getFields(IScoreboardCriteria, IScoreboardCriteria).get(0);
if (BukkitReflection.is1_20_2Plus()) {
Class<?> DisplaySlot = Class.forName("net.minecraft.world.scores.DisplaySlot");
DisplayObjective_POSITION = ReflectionUtils.getOnlyField(DisplayObjectiveClass, DisplaySlot);
newDisplayObjective = DisplayObjectiveClass.getConstructor(DisplaySlot, scoreboardObjective);
displaySlots = (Enum[]) DisplaySlot.getDeclaredMethod("values").invoke(null);
} else {
DisplayObjective_POSITION = ReflectionUtils.getOnlyField(DisplayObjectiveClass, int.class);
newDisplayObjective = DisplayObjectiveClass.getConstructor(int.class, scoreboardObjective);
}
DisplayObjective_OBJECTIVE_NAME = ReflectionUtils.getOnlyField(DisplayObjectiveClass, String.class);
newScoreboardObjective = ReflectionUtils.getOnlyConstructor(scoreboardObjective);
Objective_OBJECTIVE_NAME = ReflectionUtils.getFields(ObjectivePacketClass, String.class).get(0);
List<Field> list = ReflectionUtils.getFields(ObjectivePacketClass, int.class);
Objective_METHOD = list.get(list.size()-1);
newScoreboardScore = scoreboardScoreClass.getConstructor(scoreboard, scoreboardObjective, String.class);
newScoreboardTeam = scoreboardTeam.getConstructor(scoreboard, String.class);
TeamPacket_NAME = ReflectionUtils.getFields(TeamPacketClass, String.class).get(0);
TeamPacket_ACTION = ReflectionUtils.getInstanceFields(TeamPacketClass, int.class).get(0);
TeamPacket_PLAYERS = ReflectionUtils.getOnlyField(TeamPacketClass, Collection.class);
ScoreboardTeam_getPlayerNameSet = ReflectionUtils.getOnlyMethod(scoreboardTeam, Collection.class);
if (minorVersion >= 13) {
newScorePacket_1_13 = scorePacketClass.getConstructor(EnumScoreboardAction, String.class, String.class, int.class);
newObjectivePacket = ObjectivePacketClass.getConstructor(scoreboardObjective, int.class);
Objective_DISPLAY_NAME = ReflectionUtils.getOnlyField(ObjectivePacketClass, IChatBaseComponent);
ScoreboardTeam_setColor = ReflectionUtils.getOnlyMethod(scoreboardTeam, void.class, enumChatFormatClass);
scoreboardActions = new Enum[] {
Enum.valueOf(EnumScoreboardAction, "CHANGE"),
Enum.valueOf(EnumScoreboardAction, "REMOVE")
};
chatFormats = (Enum[]) enumChatFormatClass.getMethod("values").invoke(null);
} else {
newScorePacket_String = scorePacketClass.getConstructor(String.class);
newObjectivePacket = ObjectivePacketClass.getConstructor();
Objective_DISPLAY_NAME = ReflectionUtils.getFields(ObjectivePacketClass, String.class).get(1);
if (minorVersion >= 8) {
newScorePacket = scorePacketClass.getConstructor(scoreboardScoreClass);
Objective_RENDER_TYPE = ReflectionUtils.getOnlyField(ObjectivePacketClass, EnumScoreboardHealthDisplay);
} else {
newScorePacket = scorePacketClass.getConstructor(scoreboardScoreClass, int.class);
}
}
if (minorVersion >= 8) {
nameVisibilities = new Enum[] {
Enum.valueOf(EnumNameTagVisibility, "ALWAYS"),
Enum.valueOf(EnumNameTagVisibility, "NEVER"),
Enum.valueOf(EnumNameTagVisibility, "HIDE_FOR_OTHER_TEAMS"),
Enum.valueOf(EnumNameTagVisibility, "HIDE_FOR_OWN_TEAM")
};
healthDisplays = new Enum[] {
Enum.valueOf(EnumScoreboardHealthDisplay, "INTEGER"),
Enum.valueOf(EnumScoreboardHealthDisplay, "HEARTS")
};
}
if (minorVersion >= 9) {
ScoreboardTeam_setCollisionRule = ReflectionUtils.getOnlyMethod(scoreboardTeam, void.class, EnumTeamPush);
collisionRules = (Enum[]) EnumTeamPush.getMethod("values").invoke(null); // <1.13 has wrong enum constant names
}
if (minorVersion >= 17) {
TeamPacketConstructor_of = ReflectionUtils.getOnlyMethod(TeamPacketClass, TeamPacketClass, scoreboardTeam);
TeamPacketConstructor_ofBoolean = ReflectionUtils.getOnlyMethod(TeamPacketClass, TeamPacketClass, scoreboardTeam, boolean.class);
} else {
newTeamPacket = TeamPacketClass.getConstructor(scoreboardTeam, int.class);
}
ScoreboardScore_setScore = ReflectionUtils.getMethod(
scoreboardScoreClass,
new String[] {"func_96647_c", "setScore", "b", "c", "m_83402_"}, // {Thermos, 1.5.1 - 1.17.1 & 1.20.2+, 1.18 - 1.20.1, 1.5, Mohist 1.18.2}
int.class
);
ScoreboardTeam_setAllowFriendlyFire = ReflectionUtils.getMethod(
scoreboardTeam,
new String[] {"func_96660_a", "setAllowFriendlyFire", "a", "m_83355_"}, // {Thermos, 1.5.1+, 1.5 & 1.18+, Mohist 1.18.2}
boolean.class
);
ScoreboardTeam_setCanSeeFriendlyInvisibles = ReflectionUtils.getMethod(
scoreboardTeam,
new String[] {"func_98300_b", "setCanSeeFriendlyInvisibles", "b", "m_83362_", "setSeeFriendlyInvisibles"}, // {Thermos, 1.5.1+, 1.5 & 1.18+, Mohist 1.18.2, 1.20.2+}
boolean.class
);
if (minorVersion >= 13) {
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(
scoreboardTeam,
new String[]{"setPrefix", "b", "m_83360_", "setPlayerPrefix"}, // {1.17.1-, 1.18 - 1.20.1, Mohist 1.18.2, 1.20.2+}
IChatBaseComponent
);
ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(
scoreboardTeam,
new String[]{"setSuffix", "c", "m_83365_", "setPlayerSuffix"}, // {1.17.1-, 1.18 - 1.20.1, Mohist 1.18.2, 1.20.2+}
IChatBaseComponent
);
} else {
ScoreboardTeam_setPrefix = ReflectionUtils.getMethod(
scoreboardTeam,
new String[] {"func_96666_b", "setPrefix", "b"}, // {Thermos, 1.5.1+, 1.5}
String.class
);
ScoreboardTeam_setSuffix = ReflectionUtils.getMethod(
scoreboardTeam,
new String[] {"func_96662_c", "setSuffix", "c"}, // {Thermos, 1.5.1+, 1.5}
String.class
);
}
if (minorVersion >= 8) {
ScoreboardTeam_setNameTagVisibility = ReflectionUtils.getMethod(
scoreboardTeam,
new String[] {"setNameTagVisibility", "a", "m_83346_"}, // {1.8.1+, 1.8 & 1.18+, Mohist 1.18.2}
EnumNameTagVisibility
);
}
available = true;
}
public PacketScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
@SneakyThrows
public void setDisplaySlot(@NotNull DisplaySlot slot, @NotNull String objective) {
Object displaySlot;
if (BukkitReflection.is1_20_2Plus()) {
displaySlot = displaySlots[slot.ordinal()];
} else {
displaySlot = slot.ordinal();
}
player.sendPacket(newDisplayObjective.newInstance(displaySlot, newScoreboardObjective(objective)));
}
@Override
@SneakyThrows
public void registerObjective0(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
player.sendPacket(buildObjective(0, objectiveName, title, display));
}
@Override
public void unregisterObjective0(@NotNull String objectiveName) {
player.sendPacket(buildObjective(1, objectiveName, "", HealthDisplay.INTEGER));
}
@Override
public void updateObjective0(@NotNull String objectiveName, @NotNull String title, @NotNull HealthDisplay display) {
player.sendPacket(buildObjective(2, objectiveName, title, display));
}
@SneakyThrows
private Object buildObjective(int action, String objectiveName, String title, @NotNull HealthDisplay display) {
if (BukkitReflection.getMinorVersion() >= 13) {
return newObjectivePacket.newInstance(
newScoreboardObjective.newInstance(
null,
objectiveName,
null,
toComponent(title),
healthDisplays[display.ordinal()]
),
action
);
}
Object nmsPacket = newObjectivePacket.newInstance();
Objective_OBJECTIVE_NAME.set(nmsPacket, objectiveName);
Objective_DISPLAY_NAME.set(nmsPacket, title);
if (BukkitReflection.getMinorVersion() >= 8) {
Objective_RENDER_TYPE.set(nmsPacket, healthDisplays[display.ordinal()]);
}
Objective_METHOD.set(nmsPacket, action);
return nmsPacket;
}
@Override
@SneakyThrows
public void registerTeam0(@NotNull String name, @NotNull String prefix, @NotNull String suffix,
@NotNull NameVisibility visibility, @NotNull CollisionRule collision,
@NotNull Collection<String> players, int options) {
Object team = createTeam(name, prefix, suffix, visibility, collision, options);
((Collection<String>)ScoreboardTeam_getPlayerNameSet.invoke(team)).addAll(players);
if (BukkitReflection.getMinorVersion() >= 17) {
player.sendPacket(TeamPacketConstructor_ofBoolean.invoke(null, team, true));
} else {
player.sendPacket(newTeamPacket.newInstance(team, 0));
}
}
@Override
@SneakyThrows
public void unregisterTeam0(@NotNull String name) {
Object team = newScoreboardTeam.newInstance(emptyScoreboard, name);
if (BukkitReflection.getMinorVersion() >= 17) {
player.sendPacket(TeamPacketConstructor_of.invoke(null, team));
} else {
player.sendPacket(newTeamPacket.newInstance(team, 1));
}
}
@Override
@SneakyThrows
public void updateTeam0(@NotNull String name, @NotNull String prefix, @NotNull String suffix,
@NotNull NameVisibility visibility, @NotNull CollisionRule collision, int options) {
Object team = createTeam(name, prefix, suffix, visibility, collision, options);
if (BukkitReflection.getMinorVersion() >= 17) {
player.sendPacket(TeamPacketConstructor_ofBoolean.invoke(null, team, false));
} else {
player.sendPacket(newTeamPacket.newInstance(team, 2));
}
}
@SneakyThrows
private Object createTeam(@NotNull String teamName, @NotNull String prefix, @NotNull String suffix,
@NotNull NameVisibility visibility, @NotNull CollisionRule collision, int options) {
Object team = newScoreboardTeam.newInstance(emptyScoreboard, teamName);
ScoreboardTeam_setAllowFriendlyFire.invoke(team, (options & 0x1) > 0);
ScoreboardTeam_setCanSeeFriendlyInvisibles.invoke(team, (options & 0x2) > 0);
if (BukkitReflection.getMinorVersion() >= 13) {
ScoreboardTeam_setPrefix.invoke(team, toComponent(prefix));
ScoreboardTeam_setSuffix.invoke(team, toComponent(suffix));
ScoreboardTeam_setColor.invoke(team, chatFormats[EnumChatFormat.lastColorsOf(prefix).ordinal()]);
} else {
ScoreboardTeam_setPrefix.invoke(team, prefix);
ScoreboardTeam_setSuffix.invoke(team, suffix);
}
if (BukkitReflection.getMinorVersion() >= 8)
ScoreboardTeam_setNameTagVisibility.invoke(team, nameVisibilities[visibility.ordinal()]);
if (BukkitReflection.getMinorVersion() >= 9)
ScoreboardTeam_setCollisionRule.invoke(team, collisionRules[collision.ordinal()]);
return team;
}
@Override
@SneakyThrows
public void setScore0(@NotNull String objective, @NotNull String playerName, int score) {
if (BukkitReflection.getMinorVersion() >= 13) {
player.sendPacket(newScorePacket_1_13.newInstance(scoreboardActions[0], objective, playerName, score));
} else {
Object scoreboardScore = newScoreboardScore.newInstance(emptyScoreboard, newScoreboardObjective(objective), playerName);
ScoreboardScore_setScore.invoke(scoreboardScore, score);
if (BukkitReflection.getMinorVersion() >= 8) {
player.sendPacket(newScorePacket.newInstance(scoreboardScore));
} else {
player.sendPacket(newScorePacket.newInstance(scoreboardScore, 0));
}
}
}
@Override
@SneakyThrows
public void removeScore0(@NotNull String objective, @NotNull String playerName) {
if (BukkitReflection.getMinorVersion() >= 13) {
player.sendPacket(newScorePacket_1_13.newInstance(scoreboardActions[1], objective, playerName, 0));
} else {
player.sendPacket(newScorePacket_String.newInstance(playerName));
}
}
/**
* Creates a new Scoreboard Objective with given name.
*
* @param objectiveName
* Objective name
* @return NMS Objective
*/
@SneakyThrows
public Object newScoreboardObjective(@NotNull String objectiveName) {
if (BukkitReflection.getMinorVersion() >= 13) {
return newScoreboardObjective.newInstance(
null,
objectiveName,
null,
toComponent(""),
null
);
}
return newScoreboardObjective.newInstance(
null,
objectiveName,
IScoreboardCriteria_self.get(null)
);
}
private Object toComponent(String text) {
return player.getPlatform().toComponent(IChatBaseComponent.optimizedComponent(text), player.getVersion());
}
}
@@ -1,65 +0,0 @@
package me.neznamy.tab.platforms.bukkit.scoreboard;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.shared.chat.IChatBaseComponent;
import me.neznamy.tab.shared.hook.AdventureHook;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.RenderType;
import org.bukkit.scoreboard.Team;
import org.jetbrains.annotations.NotNull;
/**
* Scoreboard handler using Paper API, which got added
* in early 1.16.5 builds. Unlike the Bukkit API, the new
* methods using adventure components do not have any
* pointless artificial limits added in.
* <p>
* However, it still inherits the issue with compatibility
* with other plugins that create scoreboards.
*/
public class PaperScoreboard extends BukkitScoreboard {
/** Flag tracking whether this implementation is available for use */
@Getter
private static final boolean available = ReflectionUtils.classExists("net.kyori.adventure.text.Component") &&
ReflectionUtils.methodExists(Team.class, "prefix", Component.class);
public PaperScoreboard(@NotNull BukkitTabPlayer player) {
super(player);
}
@Override
public void newObjective(String objectiveName, String criteria, String title, @NotNull HealthDisplay display) {
scoreboard.registerNewObjective(objectiveName, criteria, toAdventure(title), RenderType.valueOf(display.name()));
}
@Override
public void setDisplayName(@NotNull Objective objective, @NotNull String displayName) {
objective.displayName(toAdventure(displayName));
}
@Override
public void setPrefix(@NotNull Team team, @NotNull String prefix) {
team.prefix(toAdventure(prefix));
}
@Override
public void setSuffix(@NotNull Team team, @NotNull String suffix) {
team.suffix(toAdventure(suffix));
}
/**
* Converts raw text into adventure component
*
* @param text
* Text to convert
* @return Converted component
*/
@NotNull
private Component toAdventure(@NotNull String text) {
return AdventureHook.toAdventureComponent(IChatBaseComponent.optimizedComponent(text), player.getVersion());
}
}
+9 -19
View File
@@ -5,12 +5,11 @@ description: @description@
api-version: 1.13 # Defining to avoid legacy plugin warn
load: POSTWORLD
author: @author@
website: "@website@"
depend: []
softdepend: [PlaceholderAPI, LuckPerms, LibsDisguises, ViaVersion, ViaRewind, floodgate]
loadbefore: []
prefix: TAB
libraries: []
website: "@website@"
depend:
- LuckPerms
softdepend: [PlaceholderAPI, LibsDisguises, ViaVersion, ViaRewind, floodgate]
prefix: TAB
folia-supported: true
commands:
tab:
@@ -28,8 +27,8 @@ permissions:
tab.cpu:
description: Allows player to run /tab cpu
default: op
tab.debug:
description: Allows player to run /tab debug [player]
tab.dump:
description: Allows player to run /tab dump <player>
default: op
tab.groupinfo:
description: Allows player to run /tab group <group>
@@ -49,11 +48,8 @@ permissions:
tab.change.*:
description: Allows player to change property using command
default: op
tab.width:
description: Allows player to run /tab width
default: op
tab.announce.bar:
description: Allows player to run /tab announce bar
description: Allows player to run /tab bossbar announce
default: op
tab.bossbar.toggle:
description: Allows player to run /tab bossbar
@@ -73,12 +69,6 @@ permissions:
tab.nametag.toggle.other:
description: Allows player to run /tab nametag toggle [other player]
default: op
tab.nametag.preview:
description: Allows player to run /tab nametag preview
default: op
tab.nametag.preview.other:
description: Allows player to run /tab nametag preview [other player]
default: op
tab.announce.scoreboard:
description: Allows player to run /tab scoreboard announce
default: op
@@ -108,4 +98,4 @@ permissions:
default: op
tab.spectatorbypass:
description: Makes player bypass spectator fix
default: op
default: op
Loaded 100 of 729 files, more files were not shown because too many files have changed in this diff. Show more