Compare commits

...
1285 Commits
Author SHA1 Message Date
NEZNAMY 6b9063c789 4.1.5 2024-05-27 15:58:52 +02:00
NEZNAMY ce0a45b76c [Bukkit] Add support for latest 1.21 snapshot 2024-05-27 13:46:47 +02:00
NEZNAMY 41a0bd37ce [Sorting] Fix number compression bug (#1297) 2024-05-26 12:38:28 +02:00
NEZNAMY 449d3cb3ee [BossBar] Fix error when trying to display bossbar that was not visible on join 2024-05-22 10:14:52 +02:00
NEZNAMY a1a3852b08 [BossBar] Fix Network Protocol Error disconnect on server switch 2024-05-20 12:41:07 +02:00
NEZNAMY ef63df5175 [BossBar] Make %countdown% return 0 after announcement is over instead of going to negative numbers 2024-05-20 11:08:08 +02:00
NEZNAMY 17e57f9d0c [Bug fix] Fix disabled tablist formatting referencing wrong variable 2024-05-16 21:00:01 +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 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
NEZNAMY d5c5502abe 4.0.7 2023-10-16 20:45:35 +02:00
NEZNAMY 194e8ffbd9 Code style 2023-10-15 20:39:06 +02:00
NEZNAMY ef39458abd With all the problems on 1.20.2 BungeeCord it turns out that Velocity isn't that bad. 2023-10-15 17:39:21 +02:00
NEZNAMY 6a6907fb0a Process login packet with a delay to avoid problems 2023-10-15 17:33:06 +02:00
NEZNAMY 8bb7c4a4a6 Adapt redis support subfeatures to recent changes 2023-10-15 17:00:55 +02:00
NEZNAMY 91700e7ed0 Fix global playerlist not readding players for 1.20.2 clients on server switch 2023-10-15 17:00:16 +02:00
NEZNAMY 661c2e6fc3 Simplify 2023-10-15 15:37:38 +02:00
NEZNAMY 3d053a6149 Implement backup solutions if header/footer fields fail to initialize 2023-10-15 15:35:25 +02:00
NEZNAMY 34179e4f62 Fix startup on old versions due to wrong enum names 2023-10-15 14:56:40 +02:00
NEZNAMY a23e5d9f50 Remove toComponent method from Platform interface and only keep it in platforms that actually use it 2023-10-15 14:26:09 +02:00
NEZNAMY 1cbcbf70d9 Fix aligned playerlist attempting to send packets to players who just left 2023-10-15 14:08:35 +02:00
NEZNAMY 0a7371aaec Micro optimizations with enum conversions 2023-10-14 13:55:52 +02:00
NEZNAMY da13e2d861 Fix Bridge still injecting placeholders that are not used on proxy anymore 2023-10-13 12:27:12 +02:00
NEZNAMY 5b18fa8c25 Fix PingSpoof spoofing everything instead of just online players (or their respective layout entries) 2023-10-13 11:34:38 +02:00
NEZNAMY 347231850a Fix error 2023-10-13 09:51:40 +02:00
NEZNAMY b1f9bdc26c Resend BossBar on server switch for 1.20.2 players as Login packet now clears it 2023-10-12 20:51:50 +02:00
NEZNAMY 03c9b95041 Listen to login packet instead of server switch delay for 1.20.2 players for better consistency (also fix layout not being resent on server switch on 1.20.2) 2023-10-12 19:20:22 +02:00
NEZNAMY 6e7e1d783b Fix rare placeholder formatting error 2023-10-12 16:33:58 +02:00
NEZNAMY cfb9b0a029 Fix compatibility with some random 1.8.8 overpriced fork (#1105) 2023-10-12 14:32:21 +02:00
NEZNAMY b7ea6e5af6 Fix wrong exception being caught causing the try/catch to not work (#1092) 2023-10-12 09:33:50 +02:00
NEZNAMY 44104df712 Change error message about invalid team/objective actions to a debug message. They don't cause any issues as they are properly caught and debugging is hard, especially if reporter has no interest in cooperating (#1083) 2023-10-11 19:35:57 +02:00
NEZNAMY 5de20d1c58 Add try/catch for BungeeCord sendPacket method to avoid a NPE in BungeeCord code (#1090, #1092) 2023-10-11 16:24:43 +02:00
NEZNAMY d156303740 Remove warn when placeholders use too much CPU. Since they have been moved to another thread, they will no longer prevent the plugin from working. While the warning can be useful for more advanced users, this is a public plugin used by many and most of them don't care about performance at all. This has resulted in having to answer people who would have never contacted me otherwise. 2023-10-10 15:04:00 +02:00
NEZNAMY 6e36eb3554 Add back fabric class for multi-version code for easier backport 2023-10-09 21:49:21 +02:00
NEZNAMY f7f3891817 Avoid attempts to send plugin message to offline players on bungeecord (#1090? #1092?) that may? cause errors on 1.20.2 2023-10-08 12:41:12 +02:00
NEZNAMY 1ec28856ff Avoid attempts to send packets to offline players on bungeecord (#1090? #1092?) that may? cause errors on 1.20.2 2023-10-06 11:41:32 +02:00
NEZNAMY 9658e0b74d Add debug message showing how long bridge took to respond to player join plugin message 2023-10-06 11:31:09 +02:00
NEZNAMY 86cd8d4151 Make feature loading more readable 2023-10-05 17:39:27 +02:00
NEZNAMY 2418eb206e Improve unknown version handling code & error messages 2023-10-05 13:57:56 +02:00
NEZNAMY 08fa715d26 Fix runtime warns false triggering on proxy when placeholders are not loaded due to bridge not having a chance to respond yet (#1088) 2023-10-04 14:16:08 +02:00
NEZNAMY 52426de283 Remove double method call 2023-10-04 14:04:57 +02:00
NEZNAMY 64b3da14c4 It's called disable condition, not display condition 2023-10-02 20:38:21 +02:00
NEZNAMY bf7341c52c Change way getProfile method is retrieved (#1089) 2023-10-02 19:09:53 +02:00
NEZNAMY e3490564a2 4.0.7-SNAPSHOT 2023-10-02 19:07:05 +02:00
NEZNAMY 6218f0ed16 4.0.6 2023-10-01 16:35:39 +02:00
NEZNAMY 54e8e1ea98 Add a temporary workaround for https://github.com/SpigotMC/BungeeCord/issues/3542 2023-10-01 16:35:33 +02:00
NEZNAMY 4cd50fb3d5 Use sendPacketQueued method on BungeeCord to avoid error on 1.20.2 clients 2023-10-01 16:31:25 +02:00
NEZNAMY 590286a6c2 4.0.5 2023-09-27 21:12:58 +02:00
NEZNAMY 6c96650dcc Add support for new Spigot build that uses mojang mappings for fields and methods 2023-09-27 21:12:22 +02:00
NEZNAMY 7c83a74fd8 Fix plugin trying to send scoreboard packets to players who left already 2023-09-27 19:20:32 +02:00
NEZNAMY 7b3f60cb3b 4.0.5-SNAPSHOT 2023-09-23 16:37:02 +02:00
NEZNAMY bfc845088a Merge pull request #1084 from GsTio86/master
Fix Arclight compatibility
2023-09-23 12:36:32 +02:00
Galen Huang e67d93a592 Update DataWatcher.java 2023-09-23 16:58:19 +08:00
gstio86 b518b980ae Fix Arclight compatibility 2023-09-23 14:35:06 +08:00
NEZNAMY 744bc1403a Remove tps placeholder from config entirely, the negation was wrong as well 2023-09-22 11:01:35 +02:00
NEZNAMY e11113073f 4.0.4 2023-09-22 09:00:37 +02:00
NEZNAMY 3615818812 Add condition to default config to prevent %tps% being displayed on BungeeCord in scoreboard, as the message is too long 2023-09-22 08:59:31 +02:00
NEZNAMY 0cdef12caf Add support for BungeeCord 1.20.2 2023-09-22 08:56:19 +02:00
NEZNAMY dda8c6c053 Fix another error on Bukkit 1.20.2 2023-09-21 22:21:46 +02:00
NEZNAMY 8c3327f553 Fix error on Bukkit 1.20.2 2023-09-21 20:49:29 +02:00
NEZNAMY c037a14e1f Update Bukkit to 1.20.2 2023-09-21 19:45:06 +02:00
NEZNAMY bd661360ee Delete %server_uptime% from default config as it's a PlaceholderAPI placeholder, which is not always guaranteed to work. (#1077) 2023-09-21 18:33:27 +02:00
NEZNAMY 317fabbc87 Update Fabric to 1.20.2 2023-09-21 18:31:49 +02:00
NEZNAMY 87161446c5 Add 1.20.2 to protocol versions 2023-09-21 18:25:22 +02:00
NEZNAMY 7db8b2e84b Send warn if trying to use parse command from the console with "me" player name argument 2023-09-21 15:04:03 +02:00
NEZNAMY 5f3fa2b317 Layout - Fix ROWS direction type not working properly & avoid real players appearing on top when teams are disabled 2023-09-19 22:14:37 +02:00
NEZNAMY 64c15730a6 Add missing correct Placeholder#update implementations (#1075) 2023-09-17 18:48:41 +02:00
NEZNAMY e5ab46b8ad Initialize scoreboard placeholders to avoid them rarely not parsing due to not being set 2023-09-17 15:01:51 +02:00
NEZNAMY 3060d8cc2b Add override for %player_ping% placeholder because 1.20.2 will have a breaking change to prevent github issues reporting server expansion not supporting new version 2023-09-16 10:30:05 +02:00
NEZNAMY f5d6ba462e Remove unnecessary fully qualified class names 2023-09-15 15:21:39 +02:00
NEZNAMY 81fdeece96 Port new Velocity anti-override to Sponge as well 2023-09-15 14:21:20 +02:00
NEZNAMY 8f60439ef3 Fix multiple sorting types with same priority separated with | not actually working 2023-09-14 16:13:44 +02:00
NEZNAMY c5420ead90 Send gamemode update packet on world switch when spectator fix is enabled as the server may resend gamemode on world switch back to spectator (#1069) 2023-09-13 16:55:21 +02:00
NEZNAMY 99d372d03c Fix refresh interval config conversion ignoring random invalid user-made maps and then causing error on load 2023-09-12 14:32:15 +02:00
NEZNAMY 1219a9822f Remove unused dependency 2023-09-10 10:42:37 +02:00
NEZNAMY d40c182af9 Clean up repositories 2023-09-10 10:39:12 +02:00
NEZNAMY 785e986a72 Fix component cache including child components 2023-09-09 21:04:41 +02:00
NEZNAMY e8b80f455b Replace converter methods with arrays for fast access 2023-09-09 11:33:16 +02:00
NEZNAMY 2d545f1bf1 Fix potential blocking of other plugins in new anti-override for Velocity 2023-09-09 08:22:45 +02:00
NEZNAMY d4713dc7f4 Use direct code for LibsDisguises hook instead of reflection and move it into a separate class 2023-09-08 23:54:21 +02:00
NEZNAMY cd96c13670 Remove unused imports 2023-09-08 23:07:13 +02:00
NEZNAMY 808607faf0 Add try/catch for Fabric loading to send warn on unsupported versions instead of throwing an error (#1068) 2023-09-08 21:30:54 +02:00
NEZNAMY bc403f5c23 Fix ConcurrentModificationException on Velocity 2023-09-08 19:04:00 +02:00
NEZNAMY bfc67e1953 Allow the new anti-override to be disabled with standard settings 2023-09-08 08:21:42 +02:00
NEZNAMY a186c16c34 Implement anti-override for tablist display names on Velocity 2023-09-07 21:37:33 +02:00
NEZNAMY f9be9d4c4b Improve check to reduce errors thrown 2023-09-07 18:09:38 +02:00
NEZNAMY 79fc3ce33f Fix BossBars getting resent when output of placeholder used in display condition changed even if condition result didn't change (#1065) 2023-09-06 21:14:35 +02:00
NEZNAMY 055ff8749e Add support for custom fonts in configuration 2023-09-05 21:38:38 +02:00
NEZNAMY d36410e851 Simplify 2023-09-05 21:32:01 +02:00
NEZNAMY 0fed5dca7f Add font field and conversion to component class and quality of life changes for component conversion 2023-09-05 15:30:42 +02:00
NEZNAMY dd34ffc5b2 Create internal enum for scoreboard display type instead of using a boolean 2023-09-04 21:00:17 +02:00
NEZNAMY 2cd5ac85f9 Simplify 2023-09-04 18:20:17 +02:00
NEZNAMY 571f949f50 Remove unnecessary player version check 2023-09-04 17:58:38 +02:00
NEZNAMY 81e32957b2 Fix startup error 2023-09-04 17:57:20 +02:00
NEZNAMY 4639016ffb Preload NMS GameModes instead of calling Enum#valueOf every time 2023-09-04 17:41:26 +02:00
NEZNAMY dabc2b49dc Remove unused fields and methods 2023-09-03 17:52:29 +02:00
NEZNAMY 2dec389c8c Load NMS method by parameter types instead of name 2023-09-01 11:26:05 +02:00
NEZNAMY bfe5b5960e Fix error in per world playerlist on <1.8 2023-08-31 21:04:36 +02:00
NEZNAMY e352967a17 Improve parse command and fix color-related issues on bukkit 2023-08-31 18:48:04 +02:00
NEZNAMY 930917f285 Fill auto-complete suggestions in plugin's PlaceholderAPI expansion 2023-08-30 21:04:49 +02:00
NEZNAMY b9747f7fbe Fix NPE when user deletes default-refresh-interval option from config (1052) 2023-08-28 13:39:39 +02:00
NEZNAMY 5ac9dcec37 Fix startup on Sponge 7 2023-08-28 13:00:11 +02:00
NEZNAMY 8328b43783 Fix NPE when using relational placeholder with multiple proxies 2023-08-25 22:28:23 +02:00
NEZNAMY 131fcfe144 Remove redundant annotation 2023-08-24 17:36:16 +02:00
NEZNAMY 709cb967b5 Fix plugin sometimes trying to format tablist entries for 1.7 players 2023-08-24 12:16:16 +02:00
NEZNAMY 3370b83087 Fix issue in config conversion 2023-08-24 12:06:04 +02:00
NEZNAMY 5c44bd500c Simplify 2023-08-24 10:05:21 +02:00
NEZNAMY ab8a345944 Small style changes 2023-08-24 09:45:23 +02:00
NEZNAMY 337dd56c87 Simplify 2023-08-24 09:38:54 +02:00
NEZNAMY de78ae30db Copy the previous fix to aligned playerlist as well 2023-08-22 14:29:04 +02:00
NEZNAMY 3f64185389 Fix disable condition not being respected in playerlist in one method (#1037) 2023-08-22 14:23:32 +02:00
NEZNAMY 5077cbbc9a Return empty list instead of new list 2023-08-22 14:01:50 +02:00
NEZNAMY 4d522ba5f5 Improve bossbar command to match scoreboard command features 2023-08-22 13:57:45 +02:00
NEZNAMY 4f1180b3f5 Boost performance of tablist getter on BungeeCord 2023-08-17 17:53:22 +02:00
NEZNAMY 577a3f2551 Add support for Mohist 1.18.2 2023-08-13 17:54:42 +02:00
NEZNAMY 8623ab053e Remove some unnecessary local variables 2023-08-12 20:51:25 +02:00
NEZNAMY 6d0f99eaf1 Change WeakHashMap key to use own player object instead of server's to prevent map getting big when another plugin has a memory leak 2023-08-12 19:04:30 +02:00
NEZNAMY f9490d2652 Move field into local variable 2023-08-12 15:08:15 +02:00
NEZNAMY 9d321e4a31 Remove unused parameter and simplify 2023-08-11 22:12:00 +02:00
NEZNAMY b8c2625b80 Only show placeholder retrieve time message if debug is enabled 2023-08-09 20:22:38 +02:00
NEZNAMY 8e42f328dc Register plugin's PlaceholderAPI expansion automatically without having to enable a config option 2023-08-09 20:13:26 +02:00
NEZNAMY 0c6b4efba5 Simplify 2023-08-09 17:02:25 +02:00
NEZNAMY 0821ed4dae Bukkit - Make NMS loading less dependent on the main NMS class 2023-08-09 17:00:20 +02:00
NEZNAMY 8bc38e25f9 Fix empty line detection sometimes not working 2023-08-07 21:51:43 +02:00
NEZNAMY 7a7a4e6da2 Global playerlist - Do not remove & add self on server switch 2023-08-07 14:04:25 +02:00
NEZNAMY f7d703ed1e Placeholders - Decrease return time warn threshold from 1000ms to 50ms 2023-08-07 13:40:35 +02:00
NEZNAMY 443d7fd082 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2023-08-07 13:38:47 +02:00
NEZNAMY b6114625c1 Remove duplicate check 2023-08-07 12:18:13 +02:00
NEZNAMY 4a3eb78f12 Add back ping spoof feature 2023-08-06 11:32:02 +02:00
NEZNAMY fa117a671a Simplify placeholder registration 2023-08-05 23:04:36 +02:00
NEZNAMY 6448fb975a Fix scoreboard title limit 2023-08-05 14:41:40 +02:00
NEZNAMY 1c685ce0bb Bukkit - Properly play around Bukkit API limits in scoreboard 2023-08-05 14:36:14 +02:00
NEZNAMY 1662fa8d60 Bukkit - Implement scoreboard handler using Bukkit API (still unused) 2023-08-05 13:23:03 +02:00
NEZNAMY 2787b1735b Header/Footer - Fix being sent on server switch even if disable condition is met & fix being sent twice 2023-08-04 22:07:06 +02:00
NEZNAMY e58390e30b Add some missing NotNull / Nullable annotations 2023-08-04 20:43:58 +02:00
NEZNAMY 41a10a61bf Small refactoring 2023-08-03 22:56:05 +02:00
NEZNAMY fdcc7d5e7c Global Playerlist - Add support for * symbol in server-groups 2023-08-01 13:35:06 +02:00
NEZNAMY 50b39e462f Remove unnecessary full manual import 2023-07-28 23:18:49 +02:00
NEZNAMY 4bc781f0da Refresh placeholders in a different thread, so they can no longer kill the plugin 2023-07-28 22:33:33 +02:00
NEZNAMY aca4c00083 Do not refresh placeholders in PlaceholderManager on join, as it can cause error when updating nested placeholder in a feature that's not loaded yet 2023-07-28 17:17:07 +02:00
NEZNAMY 77b820ad4d Do not spawn armor stands of dead players on join (#1005) 2023-07-28 17:02:42 +02:00
NEZNAMY 328f604236 Add pipeline injection for fabric 2023-07-28 17:02:13 +02:00
NEZNAMY 2f77bf3d10 Create a separate class for CPU report 2023-07-28 13:05:04 +02:00
NEZNAMY 23eb55aa06 Add back support for mojang-mapped server jars 2023-07-26 16:19:21 +02:00
NEZNAMY 32b7356bc0 Send console hint if both layout and prevent-spectator-effect are enabled 2023-07-25 18:22:34 +02:00
NEZNAMY 6091641ed9 Add warn if both layout and yellow-number features are enabled 2023-07-25 18:16:01 +02:00
NEZNAMY e368213d18 Add misconfiguration check for scoreboard chaining 2023-07-25 15:17:41 +02:00
NEZNAMY 58f9bb6fca Add unlimited nametag mode to Fabric (with a few missing things) 2023-07-24 22:26:24 +02:00
NEZNAMY 79798199b8 More abstraction of unlimited nametag mode 2023-07-24 20:53:14 +02:00
NEZNAMY c8d20f8479 Move NMS loading of classes 2023-07-24 17:59:17 +02:00
NEZNAMY c0498a60b4 Remove some console messages on startup 2023-07-24 16:46:00 +02:00
NEZNAMY faa01ddfc2 Add back compatibility attempt for nick plugins (#974) 2023-07-23 12:47:16 +02:00
NEZNAMY a2f628e267 Add try/catch for PremiumVanish hook due to its bug (#993) 2023-07-23 11:54:04 +02:00
NEZNAMY 66007b9121 Decrease component cache size to 1000 entries 2023-07-22 01:25:28 +02:00
NEZNAMY 9e0056b603 Improve source code parity between platforms (Sponge 7, Sponge 8 and Fabric) and improvements to Fabric 2023-07-22 01:22:13 +02:00
NEZNAMY b15cc3a6af Improve source code parity between platforms (BungeeCord and Velocity) 2023-07-21 22:33:55 +02:00
NEZNAMY 5393f869e7 Remove option to disable global playerlist latency update. The performance drop is almost zero and updating is actually needed as initial ping value can be invalid ((#990) 2023-07-20 18:05:39 +02:00
NEZNAMY f8088402e8 Make it more obvious that %tps% is a backend-only placeholder (#990) 2023-07-20 17:48:27 +02:00
NEZNAMY 979ff7d2b8 Move %mspt% placeholder registration into backend package 2023-07-19 23:29:07 +02:00
NEZNAMY bdbde58f42 Drop krypton platform as author abandoned it 2023-07-19 21:33:07 +02:00
NEZNAMY 1e26e0bd13 Change backend placeholder registration method 2023-07-19 19:34:36 +02:00
NEZNAMY be3c00d17e Fix colors in error messages 2023-07-16 12:53:46 +02:00
NEZNAMY d542bb67de Add a console warn if anti-override is disabled 2023-07-16 12:09:16 +02:00
NEZNAMY a79cdfaa1c Add a console warn if placeholder used in numeric sorting did not return a valid number 2023-07-16 11:46:07 +02:00
NEZNAMY 42db715421 Fix NPE 2023-07-15 21:16:10 +02:00
NEZNAMY 2228d5a35d Small cleanup 2023-07-15 21:08:18 +02:00
NEZNAMY c434d0eafd Remove component cache from Sponge 7 2023-07-15 21:01:27 +02:00
NEZNAMY 414a3a971f Optimize placeholder high_to_low and low_to_high to only take 3 characters instead of 10 and slightly increase range as well 2023-07-15 18:32:58 +02:00
NEZNAMY b8a22a2561 Fix layout fixed slot updating issue when switching between layouts 2023-07-15 15:06:25 +02:00
NEZNAMY bc1dc2f84f Fix layout skins not working on sponge 8 2023-07-15 14:24:01 +02:00
NEZNAMY 904c4f27aa Add check if a placeholder's refresh interval is configured same as default interval 2023-07-14 16:28:21 +02:00
NEZNAMY b19e5435ef Add check for redundant per-server / per-world overrides for header/footer 2023-07-14 15:45:18 +02:00
NEZNAMY 78fbcd829a Construct player object async 2023-07-14 14:22:56 +02:00
NEZNAMY a255844a92 Create separate methods for logging info/warn and use them 2023-07-14 14:19:22 +02:00
NEZNAMY c7a548c575 Fix placeholders in tablist formatting getting stuck when on bungeecord and joining into disabled server 2023-07-14 11:50:18 +02:00
NEZNAMY 90a4bab114 Fix global playerlist formatting tablist names for players meeting disable condition 2023-07-13 23:47:28 +02:00
NEZNAMY a1633d146a Fix NPE when using relational placeholders on bungeecord 2023-07-12 22:42:21 +02:00
NEZNAMY 87bf4b03df Add misconfiguration checks for bossbar properties 2023-07-12 17:58:17 +02:00
NEZNAMY 7731aea0d6 Create %player_ping% placeholder on bukkit to override the PAPI one and avoid issues like #972 when ping field changes 2023-07-10 21:37:33 +02:00
NEZNAMY e66350111e Make detectPlaceholders method 7x faster when text only consists of 1 placeholder 2023-07-09 22:27:13 +02:00
NEZNAMY 7e5df68626 Round animation change interval instead of decreasing it when invalid value is used 2023-07-09 21:25:10 +02:00
NEZNAMY 8e3e7de4ee Send warn if a placeholder took longer than 1000ms to return value 2023-07-09 18:35:03 +02:00
NEZNAMY 0ee535fbd5 Small code style improvements from PR 2023-07-09 10:45:05 +02:00
NEZNAMY 1b471d266c Merge pull request #970 from TFSMads/master
Added support for different default skins depending on the slot index in the layout feature
2023-07-09 10:38:44 +02:00
NEZNAMY 19a4f6ea1f Merge pull request #971 from misteeka/master
@NotNull marked function actually can return null
2023-07-08 19:10:36 +02:00
misteeka 7cc8d71224 Fix typo 2023-07-08 19:42:30 +03:00
_Mads b843475a0a Remove unnecessary imports and debug message 2023-07-08 15:18:05 +02:00
_Mads 027c0fa771 Added support for different default skins depending on the slot index 2023-07-08 15:09:50 +02:00
NEZNAMY c08e76f110 Fix startup error when refresh intervals are misconfigured (#969) 2023-07-08 12:46:39 +02:00
NEZNAMY b837fe7d15 4.0.4-SNAPSHOT 2023-07-08 12:34:21 +02:00
NEZNAMY d4d1bed408 4.0.3 2023-07-07 22:51:41 +02:00
NEZNAMY 8870295d54 Fix compatibility with bukkit/forge hybrids if they use different mappings 2023-07-07 22:50:32 +02:00
NEZNAMY 79cd6e4ae2 Add null check for messages form bridge (for future updates to avoid NPE / protocol break) 2023-07-07 17:36:44 +02:00
NEZNAMY 8e5ea29c40 Define decimal separator in one more way to make sure it works properly (#967) 2023-07-07 15:25:11 +02:00
NEZNAMY 287b51a0bf Send warn if using debug command on player who is not loaded (#959) 2023-07-06 20:55:16 +02:00
NEZNAMY 28cbf952fa Send warn if both per world playerlist and layout are enabled 2023-07-06 15:12:28 +02:00
NEZNAMY 598c35a3aa Simplify 2023-07-06 12:37:24 +02:00
NEZNAMY b8b67a64ab Simplify nms loading 2023-07-06 12:33:47 +02:00
NEZNAMY 7f6020ef34 Fix loading on catserver 2023-07-06 12:13:12 +02:00
NEZNAMY aeed453e15 Move named field loading to respective classes 2023-07-05 23:41:54 +02:00
NEZNAMY 71dddcca4c Drop support for mojang mapped server jar 2023-07-05 21:49:54 +02:00
NEZNAMY 46cdf79e20 Use all arg constructor for spawn entity packet on 1.17+ (using the wrong packet still spawns the entity) 2023-07-05 21:12:47 +02:00
NEZNAMY d7af96823b Merge pull request #962 from MachineBreaker/patch-1
Fix ViaVersion bug returning -1 version
2023-07-04 23:11:08 +02:00
MachineBreaker 5a228bd6b3 Fix ViaVersion bug returning -1 version
Explanation:

Both TAB and ViaVersion PlayerJoinEvent priority are LOWEST and seems that TAB registers earlier than ViaVersion the listeners, this causes TAB not being able to get the client protocol version due to ViaVersion not being able to register it firstly.

There might be another way to workaround this since i don't know changing the priority would cause any issue with TAB itself.
2023-07-04 16:19:07 -04:00
NEZNAMY fcd95655ce Send warn when trying to use parse command without any placeholders 2023-07-04 11:09:14 +02:00
NEZNAMY 7f5b57f8d2 Merge pull request #956 from Tanguygab/master
Update skins in Layout's FixedSlots
2023-07-03 17:07:01 +02:00
NEZNAMY f596877716 Fix forwarding unlimited nametag disable status to backend 2023-07-02 17:30:58 +02:00
NEZNAMY 1802c8fd90 Fix debug command not showing properties from unlimited nametag as disabled when they are 2023-07-02 17:30:35 +02:00
Tanguygab e05856facc only update name if skin didn't update 2023-07-02 13:51:37 +02:00
Tanguygab 45cfc3abb0 simplified FixedSlot skin updating 2023-07-02 13:40:08 +02:00
Tanguygab 2d39579861 added support for placeholders in layout's fixed-slots' skins 2023-07-02 13:27:37 +02:00
NEZNAMY 431f4bb4d5 Change Velocity policy 2023-07-02 00:39:10 +02:00
NEZNAMY 7bc9d38176 Slightly improve debug messages 2023-07-01 20:15:50 +02:00
NEZNAMY 7536447a8f Update README.md 2023-06-29 20:47:33 +02:00
NEZNAMY 059b796061 Update CONTRIBUTING.md 2023-06-29 20:35:44 +02:00
NEZNAMY a89942484f Layout - Fix duplicate internal names causing issues 2023-06-28 21:03:13 +02:00
NEZNAMY c28be3fd65 Add (another) warn if plugin's CPU usage is high 2023-06-26 21:04:26 +02:00
NEZNAMY 5f1116795c Add Folia support (#919) 2023-06-25 12:11:41 +02:00
NEZNAMY 4f54d2f6b2 4.0.3-SNAPSHOT 2023-06-25 10:59:05 +02:00
NEZNAMY 6b7bedbe2d Fix NPE when placeholder returns null 2023-06-24 14:30:44 +02:00
NEZNAMY 20ff225c31 Add missing set team color 2023-06-23 22:03:32 +02:00
NEZNAMY af2700798e 4.0.2 2023-06-22 13:07:44 +02:00
NEZNAMY f9c280bbdc Add back vault, so people stop asking 2023-06-22 12:50:36 +02:00
NEZNAMY 6ee31c2c3f Fix unlimited nametag property changes on world switch if per-world settings are used 2023-06-22 11:10:21 +02:00
NEZNAMY cbd944f6f2 Remove redundant field initialization 2023-06-19 21:19:48 +02:00
NEZNAMY cc20748f31 Move ping field initialization 2023-06-19 21:16:20 +02:00
NEZNAMY c9545165aa Rename a few classes 2023-06-19 20:38:19 +02:00
NEZNAMY 05c10bf2e7 Move all scoreboard packet fields & methods to PacketScoreboard class 2023-06-19 19:18:53 +02:00
NEZNAMY 67aeae2094 Create scoreboard implementation using paper api (not used yet) 2023-06-19 17:53:20 +02:00
NEZNAMY 1cc85a0647 Add API function for getting player's team name (#939) 2023-06-19 17:29:04 +02:00
NEZNAMY 988c0f8b4c Construct player object sync 2023-06-19 16:34:03 +02:00
NEZNAMY 62306fa5d6 More cleanup using @SneakyThrows annotation 2023-06-19 12:44:06 +02:00
NEZNAMY 641c5a49c3 Remove unused import 2023-06-19 11:12:16 +02:00
NEZNAMY 5637402435 Fix startup on some bukkit versions & improve compatibility error message 2023-06-18 20:54:25 +02:00
NEZNAMY 728d6c9a25 Make compatibility checks slightly more strict and remove package check. 2023-06-17 23:55:20 +02:00
NEZNAMY 22d3ba2877 Remove layout view on quit to avoid permission checks for offline players 2023-06-17 22:26:38 +02:00
NEZNAMY d1e032e010 Rename methods to reflect on recent change 2023-06-17 21:09:58 +02:00
NEZNAMY 80c4316826 Use Player#getPing() instead of NMS for 1.17+ where it was added 2023-06-17 21:01:02 +02:00
NEZNAMY 3addeba256 Bump bridge protocol 2023-06-17 17:43:21 +02:00
NEZNAMY c52db733b7 Fix potential issue with spectator fix not working if joining with spectator gamemode 2023-06-17 17:42:46 +02:00
NEZNAMY 07a492e353 Forward gamemode from bridge for spectator fix to work on online mode bungee (#934) 2023-06-17 17:42:03 +02:00
NEZNAMY 3c1ec478c2 Small code cleanup using lombok 2023-06-17 11:25:42 +02:00
NEZNAMY f3f614c3de Fix spacing 2023-06-16 17:27:50 +02:00
NEZNAMY a6627ac6c6 Merge pull request #933 from Golfing7/master
Fix loading on velocity
2023-06-16 17:26:14 +02:00
Golfing8 8d1040518f Fix loading on velocity 2023-06-16 11:11:20 -04:00
NEZNAMY ca5d17e6ec Merge pull request #930 from NEZNAMY/v4
Merge v4
2023-06-16 09:08:55 +02:00
NEZNAMY b9fab42510 Update supported version range in error message 2023-06-15 17:31:18 +02:00
NEZNAMY 22f1ef43c4 Add missing return to fix error (#927) 2023-06-15 09:44:14 +02:00
NEZNAMY ffbd4bf26b Move bungee component conversion to shared 2023-06-14 17:59:13 +02:00
NEZNAMY 4d6f93e975 Build adventure components with ProtocolVersion for manually defined legacy colors to actually work 2023-06-14 12:23:51 +02:00
NEZNAMY cb45cff77f 4.0.2-SNAPSHOT 2023-06-14 12:22:29 +02:00
NEZNAMY 4718040345 4.0.1 2023-06-13 00:11:20 +02:00
NEZNAMY afffefcce1 1.20.1 2023-06-13 00:11:10 +02:00
NEZNAMY 4580f47563 Fix bungee kick on 1.7 (#921) 2023-06-12 22:41:07 +02:00
NEZNAMY e51f4bb3a1 Remove unused methods 2023-06-12 13:34:30 +02:00
NEZNAMY e8c8d1dee7 Fix TabComplete on Sponge 7 2023-06-11 19:21:54 +02:00
NEZNAMY 3559afaef7 Fix TabComplete on Fabric 2023-06-11 18:26:39 +02:00
NEZNAMY fc29420eb3 Fix TabComplete on Sponge 8+ 2023-06-11 17:09:42 +02:00
NEZNAMY 023cd3fb75 4.0.0 2023-06-08 14:16:19 +02:00
NEZNAMY 2781d9f232 Fix errors when platform doesn't support unlimited nametags, but it's enabled in config 2023-06-08 14:15:34 +02:00
NEZNAMY d31e5de449 Fix NPE 2023-06-08 13:59:36 +02:00
NEZNAMY 245fcdf917 Update to 1.20 2023-06-08 13:50:47 +02:00
NEZNAMY e3ef3ffa68 Refactor scoreboard command and change /tab announce scoreboard to /tab scoreboard announce 2023-06-07 14:04:36 +02:00
NEZNAMY 95c3608c3f Fix typo 2023-06-07 14:03:34 +02:00
NEZNAMY 7c2236dc96 Not ready yet 2023-06-07 14:03:23 +02:00
NEZNAMY b83ef0e352 API - Add functions for force-updating placeholder values 2023-06-06 11:49:49 +02:00
NEZNAMY 2865871d04 Add check for ViaVersion bug returning -1 version 2023-06-06 11:30:42 +02:00
NEZNAMY 7f7b76a11e Fabric - Remove multi version class 2023-05-28 20:30:20 +02:00
NEZNAMY 52569e4c65 Create method in event listener class for replacing player instead of manually creating it in every class 2023-05-28 20:18:06 +02:00
NEZNAMY 0a0ae6c83b Fabric - Replace player object on respawn because it changes 2023-05-28 20:13:40 +02:00
NEZNAMY 6c8a41388e Unlimited NameTags - Fix disable-condition not working properly when set for the main feature 2023-05-28 16:43:52 +02:00
NEZNAMY fe94e33d85 Use PremiumVanish API code directly instead of reflection 2023-05-28 15:40:33 +02:00
NEZNAMY ca1be457cf Remove error message that was never actually sent 2023-05-25 21:20:22 +02:00
NEZNAMY 550b6350ea Change server version info into a method instead of field and provide more info 2023-05-25 13:38:42 +02:00
NEZNAMY 76470c880d Create player object on join async (broke it in a recode) 2023-05-25 12:23:48 +02:00
NEZNAMY a315bd88b3 Fix MySQL 2023-05-24 21:25:05 +02:00
NEZNAMY bb62930525 Simplify permission plugin data structure 2023-05-24 19:05:43 +02:00
NEZNAMY b7490f0002 Rename a few methods and fields 2023-05-24 13:21:56 +02:00
NEZNAMY 42f3851339 API - Small improvements/fixes and more documentation 2023-05-24 12:37:20 +02:00
NEZNAMY 34f39ac8d5 Fix compilation 2023-05-23 13:02:34 +02:00
NEZNAMY 8c16330119 API - Remove getApiVersion method, since everyone should be up-to-date and users are unlikely to check for it anyway 2023-05-23 12:56:33 +02:00
NEZNAMY 8323c21c3e Move ProtocolVersion enum out of API 2023-05-23 12:49:56 +02:00
NEZNAMY 2a3ca932a4 API - Add functions for unregistering placeholders 2023-05-23 12:41:48 +02:00
NEZNAMY e339928caa Conditions - Fix startsWith and endsWith types (how could they even have worked before?) 2023-05-23 12:40:22 +02:00
NEZNAMY 3eeb025f23 Add API for Layout and move team names to a new sorting class 2023-05-22 21:03:28 +02:00
NEZNAMY f19c1f1baa Another way of breaking the plugin was found by a customer 2023-05-20 18:56:17 +02:00
NEZNAMY 003e6a6adb Forward placeholder output replacements to bridge 2023-05-18 12:01:19 +02:00
NEZNAMY 95d435d61c Create field for viewer instead of passing as parameter 2023-05-17 14:25:19 +02:00
NEZNAMY bbc4e1d767 Unlimited nametags - Compensate for new 1.19.4 bug calling sneak event on vehicle leave with value "true", but never calling with "false" (#894) 2023-05-17 14:01:41 +02:00
NEZNAMY ae46adf8b9 Layout - Make layouts per-player, allowing hiding vanished players and opening new possibilities for the future 2023-05-17 13:31:02 +02:00
NEZNAMY 461ef988d3 Make Platform class an interface and make each implementation override sendConsoleMessage 2023-05-16 20:33:57 +02:00
NEZNAMY 307d21cc85 Remove nickname field, since plugin doesn't track name changes anymore 2023-05-16 19:01:51 +02:00
NEZNAMY e2beac6570 A couple of fixes for disable-conditions 2023-05-16 18:48:02 +02:00
NEZNAMY 75e455f078 Replace NonNull annotation with NotNull for internal methods 2023-05-16 12:52:04 +02:00
NEZNAMY 19c832d370 Rename a few classes 2023-05-16 11:34:02 +02:00
NEZNAMY 9cd4afe1d4 Rework disabled worlds/servers into disable conditions 2023-05-16 11:32:37 +02:00
NEZNAMY 3b00f07f78 Unlimited NameTags - Update visibility on vehicle enter/leave instantly (also maybe fix #894?) 2023-05-16 11:25:15 +02:00
NEZNAMY 0b5008c087 Remove unused fields 2023-05-14 21:59:44 +02:00
NEZNAMY 76f210db03 Print console warn if some placeholder's usage exceeds 50% 2023-05-14 17:43:29 +02:00
NEZNAMY 85c8d9f1b9 Bukkit - Add ViaRewind to softdepend to avoid console warn 2023-05-14 17:12:02 +02:00
NEZNAMY a795fc7f77 BossBar - Send console warn if progress could not be evaluated to a number between 0 and 100 2023-05-14 17:10:39 +02:00
NEZNAMY 7742ab8220 Add native support for multi-character symbols for aligned tabsuffix 2023-05-13 13:18:12 +02:00
NEZNAMY 3e235a8f18 Construct packet class with null title instead of empty string for unregister 2023-05-11 23:56:50 +02:00
NEZNAMY 30f9888f6a Split layout loading into multiple methods for better readability 2023-05-11 19:14:58 +02:00
NEZNAMY d1f8886e2b Layout - Add misconfiguration checks 2023-05-11 16:56:01 +02:00
NEZNAMY 3c037f9aa6 Create and use enums for team visibility and collision 2023-05-11 15:16:45 +02:00
NEZNAMY c70346c57c Fix NPE in injection when player is not loaded yet (#891) 2023-05-09 22:16:36 +02:00
NEZNAMY bd40dd85af Global PlayerList - Properly display players on other servers as spectators if enabled (#890) 2023-05-09 16:42:21 +02:00
NEZNAMY eacd929b0f Remove redundant fully qualified name of imported classes 2023-05-04 01:04:31 +02:00
NEZNAMY 0ff5f17b7d Create and use more constants instead of literal strings 2023-05-04 00:57:46 +02:00
NEZNAMY 57fa01151e Move command implementation for platforms into separate classes instead of being a nested class inside main class 2023-05-04 00:29:51 +02:00
NEZNAMY e7bc1fc74b Move sendPlayerListHeaderFooter method from TabPlayer to TabList 2023-04-29 21:32:40 +02:00
NEZNAMY 897f35ab53 Remove BulkUpdateTabList class and make all implementations update entries one by one 2023-04-29 16:32:51 +02:00
NEZNAMY 7f8b738a69 Add more NotNull / NonNull / Nullable annotations to methods 2023-04-29 15:10:30 +02:00
NEZNAMY 079302fb05 Small code cleanup 2023-04-28 23:31:31 +02:00
NEZNAMY 630fcc9e50 Remove underscore from class names 2023-04-27 16:08:20 +02:00
NEZNAMY c1601ca487 Remove nick compatibility hook, as it should no longer be needed 2023-04-27 14:03:47 +02:00
NEZNAMY d035b9cb80 Fix NPE 2023-04-27 13:20:49 +02:00
NEZNAMY 8db5e4d8ba Add NotNull / NonNull / Nullable annotations to fields and methods 2023-04-26 15:33:06 +02:00
NEZNAMY 4d51bb39d7 Print warn if both global playerlist and layout are enabled 2023-04-25 23:19:16 +02:00
NEZNAMY 7eb0231e08 Move floodgate hook into a separate class 2023-04-25 22:57:30 +02:00
NEZNAMY 6412461576 Simplify 2023-04-25 19:46:07 +02:00
NEZNAMY 5732eb366b Remove unused fields and methods 2023-04-25 19:36:03 +02:00
NEZNAMY ed3df8bb68 Recode redisbungee support and fix many bugs 2023-04-25 19:07:13 +02:00
NEZNAMY 787f531203 Remove unused fields 2023-04-25 19:00:57 +02:00
NEZNAMY cf5d888901 Remove unused parameter 2023-04-23 22:08:59 +02:00
NEZNAMY 1969e5f755 Global playerlist - Remove redundant entry removal on server switch (proxy removes it) 2023-04-23 15:57:43 +02:00
NEZNAMY 8d12ee0958 Remove unused import 2023-04-23 15:57:10 +02:00
NEZNAMY c3bbc89c5b Add final modifier where possible 2023-04-23 15:56:57 +02:00
NEZNAMY 625c7cf3c2 Fix issue with legacy color codes 2023-04-23 11:58:35 +02:00
NEZNAMY 14f0321992 Reduce static usage 2023-04-22 22:43:25 +02:00
NEZNAMY e5294c596a Change way of detection for presence of other plugins 2023-04-22 21:04:57 +02:00
NEZNAMY c83290a99a Fix error in RedisBungee 2023-04-22 20:56:48 +02:00
NEZNAMY 8c66310af0 Add RedisBungee support to Velocity as well 2023-04-22 19:17:05 +02:00
NEZNAMY 5ae91bf89e Update config comment (global playerlist will no longer duplicate players if this issue occurs) 2023-04-22 13:44:13 +02:00
NEZNAMY d5dc1b7bdb Simplify 2023-04-22 13:39:12 +02:00
NEZNAMY 864eee3349 Spectator Fix - Resend on server switch 2023-04-22 13:32:43 +02:00
NEZNAMY 88932e3a5e Move bungee component conversion out of shared to avoid class not found error 2023-04-20 21:59:04 +02:00
NEZNAMY 4ebc60ff00 Print original error instead of the current one 2023-04-20 21:43:05 +02:00
NEZNAMY b0cb63b05d Remove weird code in error printing 2023-04-20 21:19:08 +02:00
NEZNAMY 1db40ee2d5 Fix layout loading 2023-04-19 21:20:50 +02:00
NEZNAMY fd3f0ebd21 Move classes into packages 2023-04-19 11:46:22 +02:00
NEZNAMY ce73069345 Remove hover/click event from chat components 2023-04-18 13:54:06 +02:00
NEZNAMY 088f2748e3 Print amount of startup warns into the console 2023-04-17 20:39:35 +02:00
NEZNAMY d9a8c69f43 Refactor ViaVersion hook and drop ProtocolSupport hook as the plugin is abandoned 2023-04-17 19:23:54 +02:00
NEZNAMY 16ff503b6a Add back task queue while plugin is loading 2023-04-17 18:20:54 +02:00
NEZNAMY 68c78b9c73 Make field public 2023-04-17 18:04:17 +02:00
NEZNAMY 0c9d78a644 Fix method name 2023-04-15 18:59:54 +02:00
NEZNAMY 99d22642e2 Scoreboard - Fix placeholders not being updated in title on scoreboard switch 2023-04-15 15:22:02 +02:00
NEZNAMY dc762eacf8 Fill all fields in plugin.yml 2023-04-13 20:47:01 +02:00
NEZNAMY 5a44f30140 Reorder permission constants 2023-04-13 20:37:53 +02:00
NEZNAMY 82e86b0f8a Fix NPE on reload 2023-04-13 18:02:21 +02:00
NEZNAMY 9c4eb4359c Document empty methods 2023-04-12 21:53:32 +02:00
NEZNAMY 827c380463 Move more reflection related methods to ReflectionUtils 2023-04-12 21:50:01 +02:00
NEZNAMY 7c4fc324c8 Simplify 2023-04-12 21:18:29 +02:00
NEZNAMY 46215f3304 Fix indentation 2023-04-12 21:15:16 +02:00
NEZNAMY 1accc21365 Avoid possible NPE 2023-04-12 21:11:29 +02:00
NEZNAMY b74c2f62f7 Replace star import with explicit imports 2023-04-12 21:08:38 +02:00
NEZNAMY 9d14c0c923 Resend header/footer on server switch because velocity clears it 2023-04-12 16:17:12 +02:00
NEZNAMY 650fda5781 Reorder class loading to make it cleaner 2023-04-11 16:43:40 +02:00
NEZNAMY 286ccf5836 Reorder class loading to make it cleaner 2023-04-10 19:08:54 +02:00
NEZNAMY dfa310666c Move reflection related methods to ReflectionUtils 2023-04-10 19:02:35 +02:00
NEZNAMY 06595241ab Remove plugins from softdepend which are no longer being hooked into 2023-04-10 18:38:57 +02:00
NEZNAMY 0f80dd1cfd Simplify 2023-04-10 18:33:02 +02:00
NEZNAMY 7bc5be264c Define dependencies directly instead of using versions file 2023-04-10 16:23:44 +02:00
NEZNAMY e31784fb1e Include initial task delay to prevent errors while plugin is still loading 2023-04-10 13:15:14 +02:00
NEZNAMY ae156f424b Move layout to config.yml 2023-04-10 13:11:41 +02:00
NEZNAMY cfe0b3883e Remove deleted placeholder from default configuration 2023-04-10 12:49:54 +02:00
NEZNAMY fc6beaa0a5 Add removing of removed config option to converter 2023-04-10 12:45:11 +02:00
NEZNAMY 5f2140b147 Make placeholder output replacements no longer require splitting config section with each "." used in placeholder 2023-04-10 12:43:28 +02:00
NEZNAMY c497bdff39 Swap condition registration order for proper detection of type 2023-04-10 12:22:16 +02:00
NEZNAMY 7c84b6432e Simplify condition classes (or maybe just make them more complicated?) 2023-04-10 01:21:14 +02:00
NEZNAMY 3f787c3b0b Create separate Platform class for consistency 2023-04-10 00:00:32 +02:00
NEZNAMY 2046a39e99 Simplify 2023-04-09 23:42:32 +02:00
NEZNAMY 2ccf89e396 Further simplify Fabric code changes for older server versions 2023-04-08 22:45:43 +02:00
NEZNAMY efaebc3cb0 Simplify code changes for older versions on Fabric 2023-04-08 17:03:56 +02:00
NEZNAMY d7c952cc36 Fix placeholders from bridge not updating if player is not loaded yet when response is sent 2023-04-08 15:19:56 +02:00
NEZNAMY 1d3ea37129 Prevent bridge form re-registering placeholders that got deleted from config 2023-04-08 15:06:58 +02:00
NEZNAMY 1e9c47398d Add bridge connection status to debug command 2023-04-08 14:43:05 +02:00
NEZNAMY 5a1f48f1a5 Apparently it cannot be null 2023-04-08 14:25:03 +02:00
NEZNAMY ed65554d30 Remove duplication in player/playeruuid/group commands 2023-04-07 22:36:35 +02:00
NEZNAMY 0f19970401 Clean up ChatModifier serialization 2023-04-07 22:04:57 +02:00
NEZNAMY b419499710 Remove unnecessary fully qualified name 2023-04-07 21:58:38 +02:00
NEZNAMY 14a5f30b74 Split method for processing plugin messages into multiple 2023-04-07 21:48:38 +02:00
NEZNAMY 302cdf200f Small refactor of classes implemented by platforms 2023-04-07 21:32:15 +02:00
NEZNAMY bdde257cac Remove unused fields 2023-04-07 13:01:25 +02:00
NEZNAMY 73af013bca Change usage of methods which didn't exist before on fabric 2023-04-06 18:26:26 +02:00
Callum Seabrook e421427007 Disabled access widener validation task for Fabric 2023-04-06 16:17:51 +01:00
NEZNAMY 169a180d68 Remove unused imports 2023-04-06 16:28:28 +02:00
Callum Seabrook 16a19bf578 Switched to access wideners for player info entries on Fabric 2023-04-06 14:29:43 +01:00
Callum Seabrook 5b39204a8a Cleaned up Krypton code 2023-04-06 13:54:54 +01:00
Callum Seabrook b2bb125248 Switched Krypton tab list to API and removed server dependency 2023-04-06 13:35:08 +01:00
NEZNAMY 980354104b Remove fabric constant that is only used once 2023-04-06 00:18:15 +02:00
NEZNAMY b27571dcaa Catch UnsupportedOperationException 2023-04-05 22:27:44 +02:00
NEZNAMY b215bf9000 Fix dumb mistake 2023-04-05 21:10:04 +02:00
NEZNAMY ab75b13b3f Add option to choose target minecraft version on Fabric all the way down to 1.17 2023-04-05 20:42:51 +02:00
Callum Seabrook 31cd2aeb08 Made getFeature generic and removed casts 2023-04-05 19:18:39 +01:00
Callum Seabrook 8b6b33b314 Fixed compile 2023-04-05 18:40:10 +01:00
Callum Seabrook 1b66257ece Removed chat session from ProxyTabPlayer 2023-04-05 18:30:52 +01:00
Callum Seabrook bed9fcf212 Show new blank scoreboard to joining players on Krypton 2023-04-05 18:22:01 +01:00
Callum Seabrook 692f309750 Switched to API instead of packets for Krypton scoreboard 2023-04-05 16:30:19 +01:00
Callum Seabrook 2c9c9b502a Renamed Main to KryptonTAB for consistency 2023-04-05 15:48:50 +01:00
Callum Seabrook 3da8fd2a77 Fixed failed startup without Spark present on Krypton 2023-04-05 15:45:56 +01:00
Callum Seabrook ba6826f3d1 Added variable to change target Minecraft version for Fabric 2023-04-05 12:19:59 +01:00
Callum Seabrook 68b3629437 Added permissions API support for Fabric 2023-04-05 12:08:32 +01:00
Callum Seabrook 8bc5962d1c Use reflection for changing player info entries 2023-04-05 11:55:25 +01:00
NEZNAMY b8aa1982e4 Improve ViaVersion check on Fabric 2023-04-05 00:57:35 +02:00
NEZNAMY e0a427732e Update README.md 2023-04-04 23:25:38 +02:00
NEZNAMY 8d1f60f5fa Fix github actions 2023-04-04 23:16:15 +02:00
NEZNAMY 5cd7fdf385 Small improvements and fixes to fabric 2023-04-04 23:14:51 +02:00
Callum Seabrook e54d342ac5 Made relocations have a consistent pattern 2023-04-04 21:30:52 +01:00
Callum Seabrook 9019261c2a Fabric support 2023-04-04 21:27:55 +01:00
Callum Seabrook b1a5092053 Added Adventure-based boss bar handler for platforms using Adventure 2023-04-04 20:11:27 +01:00
NEZNAMY ef8bdbb40f Spectator fix - Add delay on join to avoid being too early 2023-04-04 21:08:19 +02:00
Callum Seabrook 0d67826b3d Removed message component cache on Bukkit 2023-04-04 19:34:24 +01:00
NEZNAMY 6c99e28d51 Improve code style in Scoreboard class 2023-04-04 17:21:38 +02:00
NEZNAMY 13931aafa5 Rethrow exceptions as IllegalStateException instead of RuntimeException 2023-04-04 17:01:17 +02:00
NEZNAMY 089e7ce8b5 Move class out of package 2023-04-04 15:52:01 +02:00
NEZNAMY 54287c8a29 Fix color getter 2023-04-04 15:41:18 +02:00
NEZNAMY 5ebc5dee21 Make gamemode change detection a periodic task instead of listening to packets 2023-04-04 15:29:34 +02:00
NEZNAMY bedbdbcca9 Remove redundant interface class 2023-04-04 14:44:37 +02:00
NEZNAMY a1c35adf4c Convert to adventure components directly instead of deserializing string 2023-04-04 14:34:56 +02:00
NEZNAMY 81b6a9be8b Remove redundant component deserialization attempts 2023-04-04 13:50:21 +02:00
Callum Seabrook 88eee58329 Moved chat system out of API 2023-04-02 22:49:15 +01:00
Callum Seabrook 2f02a5bda4 Removed unlimited nametags from Krypton 2023-04-02 21:11:05 +01:00
Callum Seabrook ba716df634 Removed unlimited nametags from Sponge 8 2023-04-02 20:12:30 +01:00
NEZNAMY 988292b82a XD 2023-04-02 16:30:06 +02:00
Callum Seabrook d52cfdd6e6 Start of Fabric 2023-04-02 13:55:38 +01:00
Callum Seabrook b13a49539f Updated to Gradle 8 2023-04-02 13:47:37 +01:00
NEZNAMY 32a39a361c Improve marking vanish placeholder as used for features listening to vanish status change 2023-04-02 13:46:20 +02:00
NEZNAMY 0e2e9b9ae6 Fix playerlist formatting not updating on unvanish 2023-04-02 13:41:14 +02:00
NEZNAMY 331faefde2 Fix compilation 2023-04-02 12:57:46 +02:00
NEZNAMY 1603ebe9fc Remove unused parameter 2023-04-02 12:43:15 +02:00
NEZNAMY bad4359469 Remove usage of separated initializers 2023-04-02 12:17:16 +02:00
NEZNAMY a860b463ed Merge ifs 2023-04-02 12:13:01 +02:00
NEZNAMY b3b0a96bb8 Remove commented out code that will never come back 2023-04-02 12:11:20 +02:00
NEZNAMY c211724d13 RedisBungee - Don't send yellow number / belowname if feature is disabled (#861) 2023-04-02 12:02:01 +02:00
NEZNAMY 0fb9a832ca Fix raw generic use 2023-04-02 00:34:42 +02:00
NEZNAMY 7ce1a97c93 Remove player from map on quit 2023-04-02 00:10:33 +02:00
NEZNAMY 1d461c1adf Make Scoreboard class generic to avoid massive casting 2023-04-02 00:02:31 +02:00
NEZNAMY 5ec09c37bc Remove things from API which shouldn't be there 2023-04-01 22:10:11 +02:00
NEZNAMY 95a20766c3 Simplify redisbungee belowname/yellow number forwarding 2023-04-01 17:41:25 +02:00
NEZNAMY 87feb1c2bf Improve misconfiguration checks and management 2023-04-01 17:33:34 +02:00
NEZNAMY 8c732ac7e9 Use defined constant instead of literal string 2023-04-01 13:01:26 +02:00
NEZNAMY 977c1f4d74 Remove unused field 2023-04-01 12:46:04 +02:00
NEZNAMY 65ac22aadb Perform scoreboard objective & team re-registration on server switch instead of listening to Login packet, which is assumed to be sent always 2023-04-01 12:37:02 +02:00
NEZNAMY 8e7a14bdb8 PlayerList - Format players after they un-vanish 2023-04-01 12:07:19 +02:00
NEZNAMY 4693a297b6 Remove unused field 2023-04-01 01:41:03 +02:00
NEZNAMY af69dcd9b5 Add this branch to github actions 2023-04-01 01:20:56 +02:00
NEZNAMY 71110fc722 Remove redundant conversion from older versions 2023-04-01 01:16:50 +02:00
NEZNAMY c2af5632a2 Remove hidden ghost player fix feature 2023-04-01 01:13:21 +02:00
NEZNAMY 399e813ca4 Remove placeholder refresh intervals categories to reduce confusion, split them in code based on identifier 2023-04-01 01:10:05 +02:00
NEZNAMY daac7c6171 Rename config conversion methods 2023-04-01 00:52:30 +02:00
NEZNAMY 940352ef2c Replace null checks with @NonNull 2023-04-01 00:47:21 +02:00
NEZNAMY c70b909c0f Remove disabled worlds/servers from bossbar and scoreboard, since conditions can achieve the same 2023-04-01 00:43:36 +02:00
NEZNAMY dcc2eb9217 Remove vault hook, vault placeholders and %afk% placeholder 2023-04-01 00:34:12 +02:00
NEZNAMY 5425b3b6ea Finish previous commit and remove pet fix feature 2023-04-01 00:17:48 +02:00
NEZNAMY a90ea3e3fa Remove ping spoof feature 2023-04-01 00:03:21 +02:00
NEZNAMY 6d52b7b615 4.0.0-SNAPSHOT 2023-04-01 00:01:43 +02:00
NEZNAMY 51635a7322 3.3.2 2023-03-31 20:44:09 +02:00
NEZNAMY 481b33ba55 Print error message if plugin message could not be sent 2023-03-31 15:30:30 +02:00
NEZNAMY 22c785fc99 Add error message when plugin message could not be processed 2023-03-31 15:21:53 +02:00
NEZNAMY 42d4159b69 Global PlayerList - Register placeholders in constructor to avoid it being registered as PAPI placeholder 2023-03-31 14:41:21 +02:00
NEZNAMY 6d95a2f06d Fix error on concurrent access 2023-03-31 13:40:50 +02:00
NEZNAMY 7f0b1d8a2e Fix rare error on /btab reload 2023-03-31 13:40:37 +02:00
NEZNAMY a3fecd0fea Unregister tab expansion on unload to fix compatibility with latest PAPI release 2023-03-30 21:17:09 +02:00
NEZNAMY f3d6a54a2a Merge remote-tracking branch 'origin/master' 2023-03-29 17:24:21 +02:00
NEZNAMY 1d434f2557 Add more documentation 2023-03-29 17:24:11 +02:00
NEZNAMY b3500086e4 Merge pull request #856 from zelvalea/master
optimizations in CPUManager
2023-03-28 23:23:48 +02:00
Amoeba bf9202af38 added time constants 2023-03-28 20:31:29 +03:00
Amoeba bd2c683568 fixed happens before and cpu usage statistics 2023-03-28 20:09:51 +03:00
Amoeba c03feaab23 fixed nanosToPercent 2023-03-28 19:44:11 +03:00
Amoeba 9cb9ccbd2a optimize 2023-03-28 19:21:48 +03:00
Callum Seabrook 4606ebd588 Use single thread for tasks and remove ThreadManager from API 2023-03-28 12:32:13 +01:00
Callum Seabrook 679be83c5a Merge pull request #854 from zelvalea/master 2023-03-28 11:51:03 +01:00
NEZNAMY afe868bb95 Fix group update through bridge after recoding group updating 2023-03-28 01:21:29 +02:00
ZelvaLea c9ce3aa23e reduced number of threads in the pool, fixed races 2023-03-28 01:15:04 +03:00
NEZNAMY 11a49c4874 Remove unnecessary full import 2023-03-27 21:21:27 +02:00
NEZNAMY 65d2a0fba9 Remove unused field 2023-03-27 21:19:15 +02:00
NEZNAMY c1219195c7 Remove workarounds for fixing compilation, which are no longer needed 2023-03-26 17:06:57 +02:00
NEZNAMY fca0dfc91b More constant extraction to plugin.yml & bungee.yml 2023-03-26 17:02:32 +02:00
NEZNAMY 7f1cc3063c Move text cutting to 16/32 characters to avoid duplication 2023-03-26 16:53:47 +02:00
NEZNAMY e444c25287 Convert krypton to java 2023-03-26 16:36:51 +02:00
NEZNAMY 476354e407 Change thrown exception 2023-03-25 23:25:07 +01:00
NEZNAMY 9f84d3f22f Remove cpu measurement of chosen methods 2023-03-25 23:22:28 +01:00
NEZNAMY 4608ff360c Simplify 2023-03-25 23:12:48 +01:00
NEZNAMY 68b12e199d Placeholders - Measure per-placeholder cpu usage on join as well 2023-03-25 20:54:53 +01:00
NEZNAMY 4b1151f769 Use defined value instead of duplicating it in build settings 2023-03-25 20:25:27 +01:00
NEZNAMY 6687214f2a Sponge - Add support all the way down to Sponge 5 2023-03-25 19:22:07 +01:00
NEZNAMY a57757258e Sponge - Use API for scoreboards instead of packets (also small bug fix) 2023-03-25 18:41:56 +01:00
NEZNAMY 3a6060c45d Fix display name change on 1.7 2023-03-25 14:29:27 +01:00
NEZNAMY ee375c1b60 Unify tablist management between proxies 2023-03-25 14:22:40 +01:00
NEZNAMY c9d870cca0 Use placeholder refreshing engine for refreshing player groups instead of creating another thread 2023-03-23 14:52:19 +01:00
NEZNAMY 3ad8015cc1 Placeholder manager - Initialize task with the lowest refresh interval immediately instead of re-initializing 2023-03-23 14:19:42 +01:00
NEZNAMY 33609c322f Remove duplicated Field#setAccessible 2023-03-22 00:59:02 +01:00
NEZNAMY 431b89c487 Create constant for "textures" String in skin data 2023-03-22 00:33:16 +01:00
NEZNAMY 35a146992c Remove redundant checks and simplify 2023-03-22 00:27:46 +01:00
NEZNAMY dedee114c4 BungeeCord - Split boss bar handler into 2 classes 2023-03-21 20:07:11 +01:00
NEZNAMY 9d42af0388 Bukkit - Split boss bar handler into 3 classes 2023-03-21 18:39:08 +01:00
NEZNAMY 8f5d81bc33 Remove redundant annotation 2023-03-21 18:38:41 +01:00
NEZNAMY b17f5563dd Remove unused method 2023-03-21 14:09:57 +01:00
NEZNAMY 0ab8bb363a Unlimited NameTags - Force marker tag for 1.8.x and remove config option 2023-03-20 22:33:56 +01:00
NEZNAMY ac5fca86bd Split TabFeature into multiple interfaces based on used methods 2023-03-20 19:36:40 +01:00
NEZNAMY 28c2c2e193 Fix armor stand metadata on spawn 2023-03-20 16:25:37 +01:00
NEZNAMY 904c0b6a17 Fix anti-override for tablist name formatting (and possibly ping spoof & spectator fix) 2023-03-20 16:21:23 +01:00
NEZNAMY f3c31bc92c Fix error 2023-03-20 12:42:42 +01:00
NEZNAMY 6016b84889 BungeeCord - Fix bossbar color & style update 2023-03-19 22:24:13 +01:00
NEZNAMY cfb5cb3a9f Create object for calling bossbar methods 2023-03-19 22:07:45 +01:00
NEZNAMY 1746f6b1d0 Bukkit - Send all tablist packets grouped, ViaVersion splits them properly for 1.7 players 2023-03-19 21:21:33 +01:00
NEZNAMY 9c3979a0d9 More tablist fixes on 1.7 2023-03-19 21:04:52 +01:00
NEZNAMY 810676c69f Bukkit - Fix NPE on <1.8 servers 2023-03-19 16:44:55 +01:00
NEZNAMY d0892a245a BungeeCord - Fix NPE kick on 1.7 2023-03-19 16:40:53 +01:00
NEZNAMY f539b1b93b BungeeCord - Split TabList class into 3 based on version & make 1.7 actually work (potential future use?) 2023-03-19 14:50:02 +01:00
NEZNAMY adfa64a29f Scoreboard - Fix unregistering teams of lines that aren't displayed due to placeholder output being empty 2023-03-19 14:07:44 +01:00
NEZNAMY d4f86ee79d Remove redundant declaration 2023-03-19 13:43:06 +01:00
NEZNAMY 6ebe31b737 Add @NonNull / @Nullable annotations to TeamManager (#843) 2023-03-19 13:38:58 +01:00
NEZNAMY dcbd5e62d4 Fix packet un-cancel breaking PetFix (#842) 2023-03-19 13:26:24 +01:00
NEZNAMY a7e6e49363 Fix #845 (1.7 requires non-null title in scoreboard unregister action) 2023-03-19 12:52:56 +01:00
NEZNAMY d115257ff7 Move DisplaySlot class inside Scoreboard 2023-03-18 23:53:49 +01:00
NEZNAMY d578be276c Remove duplicated caches 2023-03-18 22:35:16 +01:00
NEZNAMY e338f57441 Completely get rid of PacketBuilder class 2023-03-18 22:25:05 +01:00
Callum Seabrook c87321b5b0 Rewrote packet injection for Krypton 2023-03-18 19:37:34 +00:00
NEZNAMY 84bb9b449b Add methods for manipulating entities instead of using sendPacket 2023-03-18 19:22:29 +01:00
NEZNAMY 9d613fc24d Rework tablist packet management to make it more API-style 2023-03-18 17:44:43 +01:00
NEZNAMY 3f2ae3131e Add back a default animation, which is still used in default scoreboard 2023-03-18 00:14:56 +01:00
NEZNAMY 0e9f53bd58 Fix wrong action on objective unregister 2023-03-17 18:20:47 +01:00
NEZNAMY 3981815b63 Create Scoreboard interface for calling scoreboard-related methods 2023-03-17 15:04:55 +01:00
NEZNAMY 3ef99e4e7c Better abstraction of pipeline injection 2023-03-16 15:16:24 +01:00
NEZNAMY 76767a0958 Use API-style managing of scoreboard teams 2023-03-16 13:53:50 +01:00
NEZNAMY 1683943d58 Use API-style managing of scoreboard objectives 2023-03-16 01:07:21 +01:00
NEZNAMY cc0dbe8623 Move sendPluginMessage method to ProxyTabPlayer 2023-03-16 00:03:13 +01:00
NEZNAMY b056251e07 Remove calculating of packets sent by the plugin 2023-03-15 23:50:23 +01:00
NEZNAMY 3b352c66e2 Change way how scoreboard score and display objective packets are sent 2023-03-15 23:31:30 +01:00
NEZNAMY b4f0ea73c1 Supplier with Exception is Callable 2023-03-15 21:43:27 +01:00
NEZNAMY 1c683788b5 BungeeCord - Create cleaner methods for player info action conversion 2023-03-15 21:29:58 +01:00
NEZNAMY 250597df2d Sponge 8 - Fix class cast exception 2023-03-15 21:07:20 +01:00
NEZNAMY 0d2d1d5aaa Sponge 8 - Use direct code instead of reflection for entity teleport packet 2023-03-15 21:00:42 +01:00
NEZNAMY 061375d1e7 3.3.2-SNAPSHOT 2023-03-15 21:00:14 +01:00
NEZNAMY 5b05614305 Remove unused field 2023-03-15 20:13:29 +01:00
NEZNAMY 69db2e0e53 Use API style code for bossbars since packets were not used anymore 2023-03-15 20:03:10 +01:00
NEZNAMY 00ef364324 Use APIs for header/footer instead of packets where possible (rip bukkit) 2023-03-15 16:13:31 +01:00
NEZNAMY de68312b6b Use APIs for chat messages instead of packets 2023-03-15 14:36:25 +01:00
NEZNAMY ddac08bcbc Fix bungee compilation (did md5 break something?) 2023-03-15 14:31:59 +01:00
NEZNAMY 517e285bb3 Fix bossbar error on <1.9 from recode 2023-03-15 14:29:13 +01:00
NEZNAMY 81a08a6b51 3.3.1 2023-03-14 19:18:01 +01:00
NEZNAMY 4dfef1d032 1.19.4 support 2023-03-14 19:15:15 +01:00
NEZNAMY 5d38e28cae Extract more constants and add config option 2023-03-14 18:35:25 +01:00
NEZNAMY 66a12d10c4 Layout - Resend on server switch on Velocity 2023-03-14 17:27:27 +01:00
NEZNAMY 1a4027c8d5 Remove unused fields and methods 2023-03-14 16:15:55 +01:00
NEZNAMY 2a0243c51a RedisSupport - Add missing packet source 2023-03-14 15:24:13 +01:00
NEZNAMY 4851d09e70 More abstraction of unlimited nametag mode & packets it uses 2023-03-14 15:20:49 +01:00
NEZNAMY 3cdf3accfd Remove redundant checks 2023-03-14 10:46:27 +01:00
NEZNAMY fd1f094216 Layout - Small cleanup in code 2023-03-14 00:22:02 +01:00
NEZNAMY 25ece1ecb8 Move ViaVersion hook (and remove safety checks, hopefully it works correctly now) 2023-03-13 23:31:10 +01:00
NEZNAMY 802e65c280 Change constructor fields to getters in features 2023-03-13 23:04:22 +01:00
NEZNAMY 882688f056 Layout - Cache remove packet 2023-03-13 21:52:29 +01:00
NEZNAMY 97d60d0ab3 Bungee - Remove unused field (and simplify) 2023-03-13 21:33:48 +01:00
NEZNAMY 9ef99daed1 Revert feature loading as references were not null when they were supposed to be 2023-03-13 21:31:48 +01:00
NEZNAMY 71c85d9d39 NameTags - Speed up unloading 2023-03-13 19:36:05 +01:00
NEZNAMY b981ab46be Add debug messages about feature unloading times 2023-03-13 19:35:45 +01:00
NEZNAMY 016633e494 BelowName - Speed up reloads by caching values for players (tested with 100 bots, reloads were 2x faster) 2023-03-13 19:22:00 +01:00
NEZNAMY 1db862ab56 NameTags - Speed up reloading (tested with 100 bots, reload was 3x faster) 2023-03-13 19:15:45 +01:00
NEZNAMY 3563786310 PlayerList - Drastically speed up reloads (up to 10+ times with high player counts) 2023-03-13 19:01:26 +01:00
NEZNAMY ac8851dd6a Cache EnumSets for info packet for better performance 2023-03-13 18:22:16 +01:00
NEZNAMY 53ab2d9793 Commands - Do not load widths for widthcheck command unless command is used 2023-03-13 18:03:55 +01:00
NEZNAMY 2a58c86ffc Move code from initializer to load 2023-03-13 16:35:30 +01:00
NEZNAMY fa46d527ae Spectator Fix - Don't send useless packets on load/unload 2023-03-13 15:17:13 +01:00
NEZNAMY 667bd38305 Velocity - Fix Velocity being Velocity exception 2023-03-13 14:51:52 +01:00
NEZNAMY faba439466 Global PlayerList - Send all affected entries in a single packet, reducing amount of packets sent and improving performance 2023-03-13 14:47:29 +01:00
NEZNAMY c301157758 Global PlayerList - No longer delay task on quit as I was not able to reproduce the bug; Update placeholders periodically as they didn't update properly on vanish change anyway 2023-03-13 14:14:06 +01:00
NEZNAMY 8d09d425a4 Global PlayerList - No longer prevent other sources from removing players, better organize adding on server switch as well 2023-03-13 13:26:12 +01:00
NEZNAMY 11f03d5a10 Simplify 2023-03-13 12:58:19 +01:00
NEZNAMY 83625202a1 Merge pull request #841 from NEZNAMY/better-pipeline-injection
Better pipeline injection
2023-03-12 14:39:19 +01:00
Callum Seabrook eb4b715a88 Move pipeline injection to its own package 2023-03-12 13:35:47 +00:00
Callum Seabrook 89dce941cf Add RequiredArgsConstructor to NettyPipelineInjector 2023-03-12 13:31:58 +00:00
Callum Seabrook 6371b67e9e Removed player Channel and moved Netty injection to NettyPipelineInjector 2023-03-12 13:23:16 +00:00
NEZNAMY 966e27418a Bukkit - Attempt to fix error on ArrayList#remove 2023-03-11 15:35:31 +01:00
NEZNAMY 4adf7d1734 Bukkit - Fix NoSuchElementException that doesn't make sense 2023-03-11 15:25:06 +01:00
NEZNAMY 1a4c4a6fe8 Add more debug messages about feature loading times 2023-03-11 15:14:12 +01:00
NEZNAMY 7ff36592ae Do not re-initialize ErrorManager 2023-03-11 14:27:23 +01:00
NEZNAMY 1b513412d2 Remove redundant import 2023-03-10 13:20:14 +01:00
NEZNAMY f2bb43fbcd Make tabExpansion variable not null and add default empty implementation 2023-03-10 13:16:22 +01:00
NEZNAMY df5d07d885 Include forgotten code from previous commit 2023-03-10 13:13:21 +01:00
NEZNAMY 0ad2f33c4c Further clean up feature loading and mutual hooks 2023-03-10 12:45:28 +01:00
NEZNAMY dee48fccb3 Partially de-spaghetti feature loading and mutual hooking 2023-03-09 00:13:15 +01:00
NEZNAMY 9ca372ab13 Velocity is XD 2023-03-07 23:07:26 +01:00
NEZNAMY 58a849a902 Update README.md 2023-03-06 18:15:04 +01:00
NEZNAMY 895072ec35 Update README.md 2023-03-06 18:07:14 +01:00
NEZNAMY 2b2c4d2c39 Update README.md 2023-03-06 12:03:45 +01:00
NEZNAMY d534b6e2e8 Layout - Make fixed slots take priority over player groups if a slot is used in both 2023-03-05 15:31:14 +01:00
NEZNAMY 783e9d9931 Fix wrong values in widths.txt 2023-03-02 21:06:15 +01:00
NEZNAMY a109d9b55b Enable widthcheck command and fix error in it 2023-03-02 20:54:28 +01:00
NEZNAMY 5ce59ce44f Fix compilation 2023-03-02 20:33:37 +01:00
NEZNAMY 1e9195a9bc Velocity - Add try/catch for calling new methods on old versions 2023-03-01 18:49:41 +01:00
NEZNAMY 3099d16468 Create SECURITY.md 2023-03-01 12:23:39 +01:00
NEZNAMY c9c6af346c Add new issue templates 2023-03-01 12:18:23 +01:00
NEZNAMY af80a43628 Catch Throwable in pipeline instead of Exception to reduce chance of plugin causing player disconnect in future unsupported versions 2023-02-28 23:33:31 +01:00
NEZNAMY dc3c98ec91 Bukkit - Change how ViaVersion is detected (to avoid wrong detection due to a plugin order load bug in 1.17.1) 2023-02-25 18:41:25 +01:00
NEZNAMY dffa2c83f6 Typo 2023-02-24 23:45:40 +01:00
NEZNAMY dc66f253a7 Config converter - Remove placeholder that got deleted 2023-02-24 23:42:35 +01:00
NEZNAMY 17498cbfc2 BungeeCord - Fix bytebuf deserialization from previous commits 2023-02-24 23:15:01 +01:00
NEZNAMY 96e4d30168 Include 1.5 in supported version range 2023-02-24 16:48:56 +01:00
NEZNAMY c9d1934bb9 Use lombok for empty private constructor 2023-02-24 16:33:39 +01:00
NEZNAMY 8bec083194 Remove redundant duplicated checks 2023-02-24 16:30:34 +01:00
NEZNAMY abf047be23 Fix global playerlist class being instantiated twice 2023-02-24 16:22:02 +01:00
NEZNAMY 4a95399d56 3.3.1-SNAPSHOT 2023-02-24 16:19:01 +01:00
NEZNAMY 885bf5154e NameTags - Fix updating nametag visibility of players in disabled world/server 2023-02-24 15:43:58 +01:00
NEZNAMY 3e146fc496 Simplify 2023-02-24 15:41:30 +01:00
NEZNAMY 95710bcaf5 Sorting - Fix team name change registering teams for players in disabled worlds/servers 2023-02-24 15:37:40 +01:00
NEZNAMY edd620f17d BelowName - Fix text refreshing packets being sent to players in disabled worlds/servers 2023-02-24 15:25:09 +01:00
NEZNAMY 9c466d8b1e Update comment 2023-02-24 14:57:56 +01:00
NEZNAMY b385ab70fb Simplify 2023-02-24 14:52:22 +01:00
NEZNAMY 140224840b Remove double registration of sorting feature 2023-02-23 21:24:10 +01:00
NEZNAMY 2078cd5a5f Downgrade sponge API dependencies to confirm support 2023-02-21 18:46:43 +01:00
NEZNAMY 27bae7fdbd 3.3.0 2023-02-21 17:03:39 +01:00
NEZNAMY 174b4214de Bukkit - Fix error from previous commits 2023-02-21 13:52:21 +01:00
NEZNAMY 4ac524840c Remove non-existing branch from build.yml 2023-02-20 20:42:48 +01:00
NEZNAMY dd4ce4e2c3 Remove redundant library (gson) 2023-02-20 17:34:38 +01:00
NEZNAMY 2a03d69628 Add @SuppressWarnings annotation to a method 2023-02-20 17:33:44 +01:00
NEZNAMY c28252e6ce RGB - Fix redundant color codes in conversion to legacy, causing issues with limits (#827) 2023-02-20 17:02:20 +01:00
NEZNAMY da1768d833 Further code cleanup 2023-02-20 16:43:49 +01:00
NEZNAMY b58c3e6b47 Fix typos 2023-02-20 12:11:58 +01:00
NEZNAMY c7575bdc8f Remove redundant throws and empty method 2023-02-20 12:00:01 +01:00
NEZNAMY e275d22061 Bukkit - Fix startup on <1.8 2023-02-20 00:35:50 +01:00
NEZNAMY b5600ef22a Fix merge conflict 2023-02-20 00:29:05 +01:00
NEZNAMY b8e14c9256 Merge branch 'master' of https://github.com/NEZNAMY/TAB
 Conflicts:
	krypton/src/main/kotlin/me/neznamy/tab/platforms/krypton/KryptonPlaceholderRegistry.kt
2023-02-19 23:58:11 +01:00
NEZNAMY 1af5b9a308 Bukkit - Further split NMS loading and packet creation 2023-02-19 23:58:01 +01:00
Callum Seabrook 2dd719c3a5 Added Spark placeholder support back to Krypton 2023-02-19 21:08:04 +00:00
NEZNAMY 45d94694cd Bukkit - Split NMS fields and initialization into multiple classes 2023-02-19 21:15:10 +01:00
NEZNAMY 3ce8c6a192 Code style - Extract duplicated code 2023-02-19 17:41:06 +01:00
NEZNAMY 7deb86f105 Fix typo 2023-02-19 17:28:25 +01:00
NEZNAMY d9b4f6a43f Bukkit - Move data watcher serializer initialization 2023-02-19 17:15:58 +01:00
NEZNAMY 54a7fb8721 Code style - Extract duplicated code for permission plugin detection 2023-02-19 16:08:02 +01:00
NEZNAMY d1dcb2f437 Merge remote-tracking branch 'origin/master' 2023-02-18 17:35:03 +01:00
NEZNAMY a92bab617a Unlimited nametags - Fix error caused by wrong method being called 2023-02-18 17:34:52 +01:00
Callum Seabrook 57e34bc595 Updated to latest Krypton and added TAB prefix to console 2023-02-15 23:58:25 +00:00
Callum Seabrook 953baa273c Build Krypton targeting Java 8 2023-02-15 20:23:39 +00:00
NEZNAMY c403cb3187 Fix startup after recode 2023-02-15 21:03:19 +01:00
NEZNAMY ffd6dc5bb9 Disable krypton compilation to fix Sponge 7 startup (due to a Sponge 7 bug) 2023-02-15 20:53:06 +01:00
NEZNAMY 0f6504a3dc Remove unused local variable 2023-02-15 20:28:55 +01:00
NEZNAMY f376fea598 Formatting 2023-02-15 20:26:50 +01:00
NEZNAMY 4896cdff91 Further extend lombok usage 2023-02-15 19:17:29 +01:00
NEZNAMY b4c87431bc Scoreboard - Fix placeholders rarely not refreshing in title 2023-02-15 19:16:29 +01:00
NEZNAMY 1cffbab97d Remove plugin message text from /tab cpu due to being impossible to read 2023-02-15 15:14:30 +01:00
NEZNAMY 9b0711075a Code style - Use constants in more places 2023-02-15 15:06:15 +01:00
NEZNAMY 17a8e902eb Velocity - Use data folder given by the platform instead of manually defining it 2023-02-15 15:01:16 +01:00
Callum Seabrook 4bdb5ba245 Use more constants for plugin info 2023-02-15 12:16:48 +00:00
NEZNAMY fd7748d280 Merge remote-tracking branch 'origin/master' 2023-02-15 12:51:36 +01:00
NEZNAMY 317b0292ff Fix error on sponge (and redundancy on bukkit) 2023-02-15 12:51:19 +01:00
Callum Seabrook b87be649bc Made ARMOR_STAND_ID constant and removed packet builder singleton for Sponge 2023-02-15 11:51:09 +00:00
Callum Seabrook e2dda54011 Added sneaking support for unlimited nametags Sponge 2023-02-15 11:43:05 +00:00
NEZNAMY d17518c39b Sponge7 - Fix broken scoreboards 2023-02-14 23:19:33 +01:00
NEZNAMY 48541dd0bd Placeholders - Fix NPE 2023-02-14 22:29:28 +01:00
NEZNAMY b83b023fee Sponge8 - Add ViaVersion hook 2023-02-14 22:29:13 +01:00
NEZNAMY 60e0c78033 Add Sponge7 module 2023-02-14 22:28:32 +01:00
NEZNAMY a395f7f01f Unlimited NameTags - Fix entity type detection from previous commits 2023-02-14 18:46:17 +01:00
NEZNAMY 09c36ef69e Relocate yamlassist as well 2023-02-14 18:28:45 +01:00
NEZNAMY 9d5fa685a0 Rename folder as well 2023-02-14 18:25:08 +01:00
NEZNAMY 6794f593fa Rename sponge module to sponge8 2023-02-14 17:39:41 +01:00
NEZNAMY 5e9965276f Code style - Change how packet builder is initialized in Platform class 2023-02-14 16:54:58 +01:00
NEZNAMY 1473038955 Code style - Fix redundant imports, throws, extract constants, grammar 2023-02-14 16:39:21 +01:00
Callum Seabrook 2041ca57f3 Finished Sponge support 2023-02-14 14:38:09 +00:00
NEZNAMY 098782d820 3.3.0-SNAPSHOT 2023-02-14 12:53:10 +01:00
NEZNAMY 87e9a6defd 3.2.5 2023-02-13 16:40:36 +01:00
NEZNAMY 3d896ff828 More abstraction of unlimited nametag mode (also a small fix from previous commit) 2023-02-13 14:25:38 +01:00
NEZNAMY bf93240888 Unlimited NameTags - Make armor stands visible for spectators (#785) 2023-02-13 13:57:49 +01:00
NEZNAMY 0b0e30c3c9 Disable sponge module until compilation is fixed 2023-02-13 12:54:05 +01:00
NEZNAMY 84d3816bd1 Fix NPE 2023-02-12 12:51:37 +01:00
NEZNAMY 04ff606947 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2023-02-12 12:29:06 +01:00
NEZNAMY 1cb8e6e238 Almost finish Sponge 8 2023-02-12 12:29:01 +01:00
NEZNAMY 4a2226170d Merge pull request #819 from Tanguygab/master
added support for *server or *world (just like server* and world*)
2023-02-12 12:27:03 +01:00
Tanguygab 1469124cfc added support for *server or *world (just like server* and world*) 2023-02-12 11:41:11 +01:00
NEZNAMY 0826095e73 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2023-02-11 15:34:05 +01:00
NEZNAMY 1bd698adde Code style - Create custom cache object to avoid code duplication 2023-02-11 14:33:31 +00:00
Callum Seabrook 1058d269ff Start of rewrite of Sponge for v8 over v7 2023-02-11 14:13:05 +00:00
NEZNAMY bacd87b78e Code style - Create custom cache object to avoid code duplication 2023-02-11 13:08:20 +01:00
Callum Seabrook 5befbe8780 Updated Sponge platform 2023-02-10 23:11:38 +00:00
Callum Seabrook e159b2adae Finished Sponge implementation (mostly) 2023-02-10 23:08:21 +00:00
Callum Seabrook 52f7418853 The start of Sponge 2023-02-10 23:08:21 +00:00
Callum Seabrook 76061f7ccd Set up Sponge skeleton project 2023-02-10 23:08:21 +00:00
NEZNAMY 7d39e21eae Fix mistake after recode 2023-02-11 00:07:10 +01:00
NEZNAMY fee3723474 Code style - More abstraction of features and feature loading 2023-02-10 23:06:52 +01:00
NEZNAMY 2f0e4c6888 Merge remote-tracking branch 'origin/master' 2023-02-10 12:58:49 +01:00
NEZNAMY b04cb6d3c5 Config converter - Reverse old config detection (#762) 2023-02-10 12:58:07 +01:00
Callum Seabrook a9228a698a Fixed NPE being thrown when player info data name is null for Krypton 2023-02-09 16:18:13 +00:00
NEZNAMY 27102495d8 Code style - Use lombok in more locations 2023-02-08 00:49:48 +01:00
NEZNAMY d1bf96ab33 Code style - Use lombok for getters/setters 2023-02-07 23:14:02 +01:00
NEZNAMY 3360dbb468 Update README.md 2023-02-07 20:21:14 +01:00
NEZNAMY d224e7584e Create CONTRIBUTING.md 2023-02-07 20:20:31 +01:00
Callum Seabrook 6373540076 Updated Krypton platform and removed Indra 2023-02-07 16:56:58 +00:00
NEZNAMY 5f78aca997 Hide proxy-only methods into proxy player object 2023-02-07 15:33:24 +01:00
NEZNAMY 145d944d33 Bukkit - Remove unused (and incorrectly implemented) code 2023-02-07 15:26:01 +01:00
NEZNAMY ac91940e3b Velocity - Send packet using wanted player uuid instead of random if entry does not exist (apparently it goes through even if not registered) 2023-01-31 21:36:26 +01:00
NEZNAMY ef8452be73 Code style - Simplify BungeeCord info packet read/write based on client version 2023-01-31 18:23:24 +01:00
NEZNAMY 47e0810697 Placeholders - Remove %essentialsnick% placeholder due to being pretty redundant (keeping empty implementation so users don't get it suddenly broken) 2023-01-31 17:11:45 +01:00
NEZNAMY 1f30f225c7 Code style - Use reflection for PremiumVanish API instead of direct code with dependencies 2023-01-31 16:17:33 +01:00
NEZNAMY fcbfd60ddb Code style - Call method directly when command sender is console 2023-01-31 15:55:28 +01:00
NEZNAMY 46f9033713 Code style - Add final modifier to variable 2023-01-31 15:54:48 +01:00
NEZNAMY 7e65018a0e Velocity - Switch to non-deprecated method 2023-01-31 15:51:28 +01:00
NEZNAMY 9ef3485968 Code style - Create a method for convenience to avoid duplicated code 2023-01-27 15:52:38 +01:00
NEZNAMY 257f8f1145 Commands - Remove properly length from debug output 2023-01-27 15:24:26 +01:00
NEZNAMY 8d65cbb0e5 3.2.5-SNAPSHOT 2023-01-26 10:35:08 +01:00
NEZNAMY 20d221e3ca Merge pull request #806 from NotAlexNoyle/proposal
Propose adopting open source license
2023-01-25 12:17:56 +01:00
NEZNAMY 94072acf10 3.2.4 2023-01-25 10:39:44 +01:00
NEZNAMY 0422cc2597 Update issue templates 2023-01-24 14:00:13 +01:00
NEZNAMY 58f61e645b 3.2.4-SNAPSHOT 2023-01-24 10:45:11 +01:00
NEZNAMY 5f416bd9e5 Unlimited NameTags - Avoid Bukkit API NPE (#800, #804) 2023-01-24 10:44:35 +01:00
NotAlexNoyle 64528c913c Propose adopting open source license 2023-01-23 22:33:21 -05:00
NEZNAMY 357e7c6a49 Revert "Remove redundant try/catch"
This reverts commit 1777d8bf01.
2023-01-23 14:39:54 +01:00
NEZNAMY 81dd202053 3.2.3 2023-01-21 14:58:34 +01:00
NEZNAMY 189797aa73 Bukkit - Use the other constructor for player info packet which is cleaner (also #797) 2023-01-21 00:18:19 +01:00
NEZNAMY 1ae2ef7cad Fix compilation 2023-01-21 00:17:02 +01:00
NEZNAMY de14e14d33 Revert "Placeholders - Add timeout for placeholders to return value, causing TAB to freeze"
This reverts commit 37caed3123.
2023-01-19 22:30:09 +01:00
NEZNAMY 19892c14c1 Revert "Placeholders - Increase placeholder retrieve timeout to 500ms (#792)"
This reverts commit 6d85875fde.
2023-01-19 22:29:59 +01:00
NEZNAMY 2e00c4c002 Revert "NameTags - Fix error that doesn't make sense (#794)"
This reverts commit 12f5dd52b4.
2023-01-19 22:29:52 +01:00
NEZNAMY 97aa367629 Revert "Fix thread pool not being single threaded (maybe #781?)"
This reverts commit 87cc9a030b.
2023-01-19 22:28:22 +01:00
NEZNAMY 87cc9a030b Fix thread pool not being single threaded (maybe #781?) 2023-01-19 22:01:11 +01:00
NEZNAMY 8ba83d1cb1 BossBar - Fix errors when progress is below 0 or above 100 2023-01-19 16:59:05 +01:00
NEZNAMY 9f0e60bd47 Per world PlayerList - Remove redundant task schedule to main thread 2023-01-19 16:58:45 +01:00
NEZNAMY 93b2a25b2e Merge pull request #795 from Chris6ix/update-actions
Update Actions
2023-01-17 22:01:37 +01:00
Chris 4242d7a839 Remove Test Build Trigger 2023-01-17 21:33:33 +01:00
Chris 0ddb99bd46 Trigger Test Build 2023-01-17 21:29:34 +01:00
Chris 969672e3ad Update Actions 2023-01-17 21:26:40 +01:00
NEZNAMY 2423fbb150 Update README.md 2023-01-17 20:28:47 +01:00
NEZNAMY a99928dbf5 Placeholders - Colorize output of relational placeholders as well 2023-01-17 20:25:06 +01:00
NEZNAMY 12f5dd52b4 NameTags - Fix error that doesn't make sense (#794) 2023-01-17 15:38:20 +01:00
NEZNAMY 2bd105446e Bukkit - Properly send RGB messages into console (only for Paper+, Spigot doesn't support RGB at all) 2023-01-17 14:11:51 +01:00
NEZNAMY 6d85875fde Placeholders - Increase placeholder retrieve timeout to 500ms (#792) 2023-01-16 18:49:23 +01:00
NEZNAMY 42810aa490 3.2.3-SNAPSHOT 2023-01-16 18:47:42 +01:00
NEZNAMY bd31521bd3 3.2.2 2023-01-15 12:02:05 +01:00
NEZNAMY ec0deb8378 Yellow Number - Disable the feature for Bedrock players 2023-01-12 21:57:56 +01:00
NEZNAMY ae66b6e9a1 RGB - Fix MiniMessage->Bukkit conversion being after Bukkit->TAB, making it not translate anymore 2023-01-12 14:55:41 +01:00
NEZNAMY 3b86becebd Header/Footer - Do not add &r after \n anymore 2023-01-12 14:53:38 +01:00
NEZNAMY 4a8d86825b Configuration - Small changes to default configuration files 2023-01-10 18:56:34 +01:00
NEZNAMY 54a3bd81b4 Global PlayerList - Increase player removal prevention delay to 2000ms from 500ms 2023-01-10 16:49:16 +01:00
NEZNAMY b6768cca99 CPU manager - Add try/catch for task scheduling (#781) 2023-01-10 16:48:28 +01:00
NEZNAMY 60c414deed Bukkit - Remove redundant methods in nms loader 2023-01-05 16:48:16 +01:00
NEZNAMY 009283a83f Update README.md 2023-01-05 16:04:38 +01:00
Callum Seabrook 4a430b97df Merge pull request #779 from JT122406/master
Updates
2023-01-04 22:50:08 +00:00
NEZNAMY 722333da2e Fix RejectedExecutionException (#769) 2023-01-04 19:15:56 +01:00
NEZNAMY 1215af5262 Bukkit - Add 1.5.0 support 2023-01-04 18:28:24 +01:00
NEZNAMY 254088c37b Bukkit - Repackage nms loaders (also fix startup on Thermos) 2023-01-04 18:14:39 +01:00
J.T. McQuiggandCallum Seabrook cb2ce4acca Update gradle/libs.versions.toml
Co-authored-by: Callum Seabrook <callum.seabrook@prevarinite.com>
2023-01-04 11:20:55 -05:00
J.T.McQuigg 872adca8fd Updates 2023-01-03 23:09:35 -05:00
J.T.McQuigg f37cc0bdd8 Gradle 7.6 2023-01-03 23:02:04 -05:00
NEZNAMY 4063578ac3 Code style - Remove duplicate .toLowerCase() 2023-01-02 20:45:23 +01:00
NEZNAMY 2528de7eeb Code style - Use built-in String#trim function 2023-01-02 20:25:11 +01:00
NEZNAMY 37caed3123 Placeholders - Add timeout for placeholders to return value, causing TAB to freeze 2022-12-23 14:49:31 +01:00
NEZNAMY 27fe9dfb12 Commands - Make player command update player instantly if uuid is used instead of player name 2022-12-21 22:00:58 +01:00
NEZNAMY 0169cbd4d2 Bukkit - Fix wrong method taken sometimes, causing player kick with unlimited nametag mode 2022-12-20 15:08:18 +01:00
NEZNAMY ed760aec08 Aligned PlayerList - Fix rare error that makes no sense 2022-12-20 15:04:48 +01:00
NEZNAMY 858cbcefeb Code style - Add toString override 2022-12-20 15:04:21 +01:00
NEZNAMY 8a87d581a9 Scoreboard - Do not send packets to players who cannot see that specific scoreboard 2022-12-18 17:16:26 +01:00
NEZNAMY 6d642f4e51 NameTags - Fix players getting unregistered with wrong team name 2022-12-17 19:12:00 +01:00
NEZNAMY aca63640bc Attempt to fix rare NPE 2022-12-17 19:08:32 +01:00
NEZNAMY 1845a35315 3.2.2-SNAPSHOT 2022-12-16 22:25:39 +01:00
NEZNAMY cab5f89a95 Remove redundant check 2022-12-16 22:03:21 +01:00
NEZNAMY 1777d8bf01 Remove redundant try/catch 2022-12-16 21:14:47 +01:00
NEZNAMY 91b0bfaa9d Configuration - Simplify file copying, since default files already are encoded with UTF-8 encoding 2022-12-16 15:04:52 +01:00
NEZNAMY f1cf994d10 3.2.1 2022-12-16 11:27:48 +01:00
NEZNAMY 69957be933 Pet name fix - Fix excessive CPU usage caused by heavy operation and not caching the result 2022-12-15 00:22:23 +01:00
NEZNAMY b78eb5efb6 Bukkit - Remove forgotten debug stack trace printing 2022-12-15 00:21:28 +01:00
NEZNAMY 3577ad1964 Bukkit - Fix NPE in packet creation on <1.13 (#763) 2022-12-14 18:02:04 +01:00
520 changed files with 30145 additions and 88915 deletions

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
* text=auto
*.sh text eol=lf
gradlew text eol=lf
*.bat text eol=crlf
*.jar binary
+13 -3
View File
@@ -1,6 +1,6 @@
name: Bug Report
description: Unwanted behavior that does not match configuration.
labels: ["Bug"]
description: Plugin does not work as described.
labels: ["Type: Bug"]
body:
- type: input
attributes:
@@ -18,6 +18,14 @@ body:
validations:
required: true
- type: textarea
attributes:
label: Plugin list
description: Full list of plugins (output of /plugins)
placeholder: ...
validations:
required: true
- type: textarea
attributes:
label: Describe the bug
@@ -45,7 +53,7 @@ body:
- type: textarea
attributes:
label: Additional info
description: Any other info you want to provide, such as screenshots.
description: Anything else you want to provide that will help in resolving the issue faster, such as screenshots or videos.
validations:
required: false
- type: checkboxes
@@ -60,4 +68,6 @@ body:
- 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
- label: I am willing to cooperate to get this issue resolved faster, including providing any additional information requested or testing new builds
required: true
@@ -0,0 +1,38 @@
name: Compatibility issue
description: A compatibility issue with another plugin/mod or a specific version of a platform marked as supported
labels: ["Type: Compatibility issue"]
body:
- type: input
attributes:
label: TAB version
description: Full version in numbers, "latest" is not a version.
placeholder: 4.0.0
validations:
required: true
- type: textarea
attributes:
label: Software causing compatibility issue
description: plugin/mod/platform version causing the issue
validations:
required: true
- type: textarea
attributes:
label: Additional info
description: Anything else you want to provide that will help in resolving the issue faster.
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 ticked all of the boxes without actually reading them
required: false
- label: The compatibility wiki page does not mention this incompatibility
required: true
- label: I am willing to cooperate to get this issue resolved faster, including providing any additional information requested or testing new builds
required: true
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Support
url: https://discord.gg/YPqXt63YQj
about: Fresh new community discord providing support with all plugins, including TAB
+1 -1
View File
@@ -1,6 +1,6 @@
name: Error
description: Error message with a stack trace in console or error file.
labels: ["Error"]
labels: ["Type: Error"]
body:
- type: input
attributes:
+56
View File
@@ -0,0 +1,56 @@
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
@@ -0,0 +1,55 @@
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
+1 -1
View File
@@ -1,6 +1,6 @@
name: Wiki change
description: Change to text on a wiki page or adding a new section.
labels: ["Wiki"]
labels: ["Type: Wiki change"]
body:
- type: input
attributes:
+8 -11
View File
@@ -3,30 +3,27 @@ on:
push:
branches:
- master
- v3
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4 #https://github.com/actions/checkout/releases
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 11
uses: actions/setup-java@v3
- name: Set up JDK 21
uses: actions/setup-java@v4 #https://github.com/actions/setup-java/releases
with:
distribution: 'temurin'
java-version: 11
java-version: 21
- name: Cache SonarCloud packages
uses: actions/cache@v2
uses: actions/cache@v3.2.3 #https://github.com/actions/cache/releases
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Gradle
uses: actions/cache@v2
uses: actions/cache@v3.2.3 #https://github.com/actions/cache/releases
with:
path: |
~/.gradle/caches
@@ -39,7 +36,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
run: ./gradlew clean build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2 #https://github.com/actions/upload-artifact/releases
with:
name: Jar
name: Click here to download
path: jar/build/libs/TAB-**.jar
-1
View File
@@ -33,7 +33,6 @@ hs_err_pid*
.idea/
# other
.gitattributes
*.iml
run/
+9
View File
@@ -0,0 +1,9 @@
If this plugin has helped you in any way, and you would like to return something back
to make the plugin even better, there is a lot of ways to contribute:
* Open a **pull request** containing a new feature or a bug fix, which you believe many users will benefit from.
* Make detailed high-quality **bug reports**. The difference between a bug getting fixed in 1 week vs 1 hour
is in quality of the report (typically providing correct steps to reproduce that actually work).
* Test **experimental builds**, which can be found in [GitHub Actions](https://github.com/NEZNAMY/TAB/actions) 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.
+174
View File
@@ -0,0 +1,174 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
+46 -35
View File
@@ -1,47 +1,58 @@
# 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, compatibility and support.
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.
# Distribution
As of TAB 3.0.0, released with MC 1.18 (November 30th, 2021), there is no free / premium
version anymore. Only one version of the plugin exists now, which contains all implemented features
and is available to everyone for free. However, official support on discord is only provided to
those who have purchased the plugin.
# Download
### Releases
You can download full releases from [GitHub releases](https://github.com/NEZNAMY/TAB/releases),
[SpigotMC](https://www.spigotmc.org/resources/57806/), [Modrinth](https://modrinth.com/plugin/tab-was-taken) or [BuiltByBit](https://builtbybit.com/resources/20631).
### Dev builds
Plugin's development is continuous and contains lots of small changes/fixes not worthy of being called a full
update, therefore development builds are being created and eventually a full update is released when enough changes are made.
If you want to benefit from a recent change in the code before official release, you can download development builds
from [GitHub actions](https://github.com/NEZNAMY/TAB/actions) (you must be logged into GitHub to be able to download it).
# Compiling
Compilation requires JDK 8 and up.
Compilation requires JDK 21 and up.
To compile the plugin, run `./gradlew build` from the terminal.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
The universal jar contains all modules for all supported platforms.
# Links
The precompiled JAR can be downloaded for free from:
* GitHub releases: https://github.com/NEZNAMY/TAB/releases
* BuiltByBit: https://builtbybit.com/resources/20631/
Support my work & receive official assistance with configuration:
* BuiltByBit: https://builtbybit.com/resources/14009/
* Polymart: https://polymart.org/resource/484
Other links:
* Discord support for buyers: https://discord.gg/EaSvdk6
* Wiki: https://github.com/NEZNAMY/TAB/wiki
# 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.
# Issues
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is **only** available for cases included in templates.
In other words, they **are not available for**:
* **Support** - support is provided on discord for buyers, where roles are given to verified users
allowing us to know who bought it and who didn't. It also serves as a much faster way of communication.
* **Feature requests** - closed indefinitely.
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
If this plugin has helped you in any way, and you would like to return something back
to make the plugin even better, there is a lot of ways to contribute:
* Open a **pull request** containing a new feature or a bug fix, which you believe many users will benefit from.
* Make detailed high-quality **bug reports**. The difference between a bug getting fixed in 1 week vs 1 hour
is in quality of the report (typically providing correct steps to reproduce that actually work).
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) or in a form of pre-releases on discord
for problems of any kind including bugs. This way you will help make the actual releases more stable / help get stable releases sooner.
* Help improve the wiki by opening a [Wiki change issue](https://github.com/NEZNAMY/TAB/issues/new?assignees=&labels=Wiki&template=wiki_change.yml),
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples of usage of functions.
* [Buy a copy](https://builtbybit.com/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
See [CONTRIBUTING.md](https://github.com/NEZNAMY/TAB/blob/master/CONTRIBUTING.md)
# Provided services
The plugin is being used by a large amount of servers, which also means the demand for my attention is high. Unfortunately,
I am no longer able to keep up with the demand since I don't want to dedicate my whole existence to a free plugin.
In order to provide the best quality of all provided services, the amount of services provided may vary over time.
Here is the full list of potential services and their status:
| Service type | Availability |
|-------------------------------|--------------|
| Updates for new MC versions | ✔ |
| Bug fixes | ✔ |
| Wiki updates<sup>1</sup> | ✔ |
| Feature requests | ❌ |
| Customer support<sup>2</sup> | ✔ |
| Free user support<sup>3</sup> | ❌* |
| Optimization<sup>4</sup> | ✔ |
<sup>1</sup> - Adding new examples, guides and other kind of useful information people ask about.
<sup>2</sup> - Unrestricted assistance with the plugin for those, who bought a copy of the plugin when it was purchasable.
<sup>3</sup> - Unlimited access to my free time for everyone in the world.
<sup>4</sup> - Improving plugin's performance while not losing out on any features. This was done
proactively a lot in the past, so there isn't much to do anymore, only if someone actually finds and reports something.
*Instead, you can use [this fresh new community discord](https://discord.gg/YPqXt63YQj) made by a nice person who will provide this service.
+2
View File
@@ -0,0 +1,2 @@
# Security Policy
If you found a security issue, send me a private message anywhere.
+1 -19
View File
@@ -1,23 +1,5 @@
plugins {
id("net.kyori.blossom") version "1.2.0"
}
dependencies {
compileOnlyApi(libs.netty)
api(libs.jsonSimple) {
exclude("junit", "junit")
}
compileOnlyApi(libs.snakeyaml)
api(libs.yamlAssist)
compileOnlyApi(libs.gson)
compileOnlyApi(libs.adventureMiniMessage)
compileOnlyApi(libs.adventureApi)
compileOnlyApi(libs.adventureLegacy)
}
blossom {
replaceToken("@plugin_version@", project.version)
replaceTokenIn("src/main/java/me/neznamy/tab/api/TabConstants.java")
compileOnlyApi("org.jetbrains:annotations:24.1.0")
}
tasks.javadoc {
@@ -1,108 +0,0 @@
package me.neznamy.tab.api;
/**
* Interface representing an armor stand attached to player
* for displaying text in game.
*/
public interface ArmorStand {
/**
* Returns true if offset is static, false if not
*
* @return true if static, false if dynamic
*/
boolean hasStaticOffset();
/**
* Changes offset of the armor stand
*
* @param offset
* new offset
*/
void setOffset(double offset);
/**
* Returns offset of the armor stand
*
* @return offset from base
*/
double getOffset();
/**
* Returns property for armor stand's name
*
* @return property for armor stand's name
*/
Property getProperty();
/**
* Teleports armor stand to owner's current location for everyone in range
*/
void teleport();
/**
* Teleports armor stand to owner's current location for specified player
*
* @param viewer
* player to send packet to
*/
void teleport(TabPlayer viewer);
/**
* Changes sneaking flag and sends packets to everyone in range
*
* @param sneaking
* new sneaking status
*/
void sneak(boolean sneaking);
/**
* DeSpawns armor stand for everyone
*/
void destroy();
/**
* DeSpawns armor stand for specified player
*
* @param viewer
* player to send packet to
*/
void destroy(TabPlayer viewer);
/**
* Updates armor stand's name if needed
*/
void refresh();
/**
* Updates visibility if needed
*
* @param force
* if refresh should be forced
*/
void updateVisibility(boolean force);
/**
* Returns entity ID of this armor stand
*
* @return entity ID of this armor stand
*/
int getEntityId();
/**
* Spawns armor stand for specified player
*
* @param viewer
* player to spawn for
*/
void spawn(TabPlayer viewer);
/**
* Sends respawn packets to specified player to make position
* change instant instead of teleport animation
*
* @param viewer
* Player to send armor stand respawn packets to
*/
void respawn(TabPlayer viewer);
}
@@ -1,116 +0,0 @@
package me.neznamy.tab.api;
import java.util.UUID;
/**
* Feature manager allows registration and work with features.
*/
public interface FeatureManager {
/**
* Registers a feature, which will start receiving events
*
* @param name
* name of feature
* @param feature
* the handler
*/
void registerFeature(String name, TabFeature feature);
/**
* Unregisters feature making it no longer receive events. This does not run unload method nor cancel
* tasks created by the feature
*
* @param name
* feature name defined in registerFeature
*/
void unregisterFeature(String name);
/**
* Returns whether a feature with said name is registered or not
*
* @param name
* name of feature defined in registerFeature method
* @return true if feature exists, false if not
*/
boolean isFeatureEnabled(String name);
/**
* Returns feature handler by its name
*
* @param name
* name of feature defined in registerFeature method
* @return the feature or null if feature does not exist
*/
TabFeature getFeature(String name);
/**
* Calls onQuit(TabPlayer) to all enabled features
*
* @param disconnectedPlayer
* player who disconnected
*/
void onQuit(TabPlayer disconnectedPlayer);
/**
* Calls onJoin(TabPlayer) to all enabled features
*
* @param connectedPlayer
* player who connected
*/
void onJoin(TabPlayer connectedPlayer);
/**
* Calls onServerChange(TabPlayer, String, String) to all enabled features
*
* @param playerUUID
* player who switched server
* @param to
* name of the new server
*/
void onServerChange(UUID playerUUID, String to);
/**
* Calls onWorldChange(TabPlayer, String, String) to all enabled features
*
* @param playerUUID
* player who switched world
* @param to
* name of the new world
*/
void onWorldChange(UUID playerUUID, String to);
/**
* Calls onCommand(TabPlayer, String) to all enabled features
*
* @param sender
* command sender
* @param command
* command line including /
* @return {@code true} if some feature marked the command for cancel, {@code false} if not
*/
boolean onCommand(TabPlayer sender, String command);
/**
* Internal function that marks objective packet deserialization
* when at least 1 feature uses it.
* <p>
* Internal use only.
*/
void markObjective();
/**
* Internal function that marks display objective packet deserialization
* when at least 1 feature uses it.
* <p>
* Internal use only.
*/
void markDisplayObjective();
/**
* Returns array of all currently enabled features
*
* @return array of all currently enabled features
*/
TabFeature[] getValues();
}
@@ -1,68 +0,0 @@
package me.neznamy.tab.api;
/**
* An interface representing a property assigned to a player,
* which may contain placeholders and can be changed using the API.
*/
public interface Property {
/**
* Returns original raw value ignoring API calls
*
* @return original raw value
*/
String getOriginalRawValue();
/**
* Temporarily overrides current raw value with an API call
*
* @param temporaryValue
* temporary value to be assigned
*/
void setTemporaryValue(String temporaryValue);
/**
* Returns current temporary value
*
* @return temporary value or null if not set
*/
String getTemporaryValue();
/**
* Returns temporary value (via API) if present, raw value otherwise
*
* @return current raw value
*/
String getCurrentRawValue();
/**
* Returns last known value
*
* @return last known value
*/
String get();
/**
* Replaces all placeholders in current raw value, colorizes it, removes remove-strings and returns whether value changed or not
*
* @return if updating changed value or not
*/
boolean update();
/**
* Replaces all placeholders in current raw value, colorizes it, removes remove-strings and returns it.
* Equal to calling update() and then get().
*
* @return updated value
*/
String updateAndGet();
/**
* Returns value for defined viewer by applying relational placeholders to last known value
*
* @param viewer
* the viewer
* @return format for the viewer
*/
String getFormat(TabPlayer viewer);
}
+40 -118
View File
@@ -2,13 +2,19 @@ package me.neznamy.tab.api;
import java.util.UUID;
import lombok.NonNull;
import lombok.Setter;
import me.neznamy.tab.api.bossbar.BossBarManager;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.event.EventBus;
import me.neznamy.tab.api.placeholder.PlaceholderManager;
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
import me.neznamy.tab.api.task.ThreadManager;
import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.api.tablist.SortingManager;
import me.neznamy.tab.api.tablist.HeaderFooterManager;
import me.neznamy.tab.api.tablist.TabListFormatManager;
import me.neznamy.tab.api.nametag.NameTagManager;
import me.neznamy.tab.api.tablist.layout.LayoutManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The primary API class to get instances of other API classes
@@ -16,14 +22,7 @@ import me.neznamy.tab.api.team.TeamManager;
public abstract class TabAPI {
/** Instance of the API */
private static TabAPI instance;
/**
* Constructs new instance and sets it as new API instance
*/
public TabAPI() {
instance = this;
}
@Setter private static TabAPI instance;
/**
* Returns API instance. If instance was not set by the plugin, throws
@@ -35,9 +34,13 @@ public abstract class TabAPI {
* @throws IllegalStateException
* If instance is {@code null}
*/
public static TabAPI getInstance() {
if (instance == null) throw new IllegalStateException("API instance is null. This likely means you shaded TAB's API into your project" +
" instead of only using it, which is not allowed.");
public static @NotNull TabAPI getInstance() {
if (instance == null) throw new IllegalStateException("The API instance is null. This can have 2 possible causes: \n" +
"#1 - API was called before TAB was loaded. This means your plugin was loaded before TAB was. To make sure your " +
"plugin loads after TAB, add it as a depend or soft depend of your plugin.\n" +
"#2 - You shaded TAB's classes into your plugin, instead of only using them. This is not allowed. To verify this " +
"is your case, unzip your plugin and check for TAB's classes. If they are there, you will need to fix your compiler " +
"to not include them, such as scope provided for maven compilation.");
return instance;
}
@@ -48,7 +51,7 @@ public abstract class TabAPI {
* Player UUID
* @return player object from given UUID
*/
public abstract TabPlayer getPlayer(UUID id);
public abstract @Nullable TabPlayer getPlayer(@NonNull UUID id);
/**
* Returns player object from given name
@@ -57,157 +60,76 @@ public abstract class TabAPI {
* Player name
* @return player object from given name
*/
public abstract TabPlayer getPlayer(String name);
public abstract @Nullable TabPlayer getPlayer(@NonNull String name);
/**
* Returns collection of all online players. Will return empty list if plugin is disabled (due to a broken configuration file for example).
*
* @return collection of online players
*/
public abstract TabPlayer[] getOnlinePlayers();
public abstract @NotNull TabPlayer[] getOnlinePlayers();
/**
* Return BossBar manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return BossBar manager
*/
public abstract BossBarManager getBossBarManager();
public abstract @Nullable BossBarManager getBossBarManager();
/**
* Returns scoreboard manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return scoreboard manager
*/
public abstract ScoreboardManager getScoreboardManager();
public abstract @Nullable ScoreboardManager getScoreboardManager();
/**
* Returns team manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return team manager
*/
public abstract TeamManager getTeamManager();
public abstract @Nullable NameTagManager getNameTagManager();
/**
* Returns header/footer manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return Header/footer manager
*/
public abstract HeaderFooterManager getHeaderFooterManager();
public abstract @Nullable HeaderFooterManager getHeaderFooterManager();
/**
* Returns PlaceholderManager instance
*
* @return PlaceholderManager instance
*/
public abstract PlaceholderManager getPlaceholderManager();
public abstract @NotNull PlaceholderManager getPlaceholderManager();
/**
* Returns feature manager instance
* Returns TabList name format manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return feature manager instance
* @return TabList name format manager
*/
public abstract FeatureManager getFeatureManager();
public abstract @Nullable TabListFormatManager getTabListFormatManager();
/**
* Returns Tablist name format manager instance if the feature is enabled. If not, returns {@code null}.
* Returns Layout manager instance if the feature is enabled. If not, returns {@code null}.
*
* @return Tablist name format manager
* @return Layout manager
*/
public abstract TablistFormatManager getTablistFormatManager();
public abstract @Nullable LayoutManager getLayoutManager();
/**
* Returns Sorting manager instance if at least one feature capable of sorting players is enabled.
* If not, returns {@code null}.
*
* @return Sorting manager
*/
public abstract @Nullable SortingManager getSortingManager();
/**
* Gets the event bus for registering listeners for TAB events.
*
* @return the event bus
*/
public abstract EventBus getEventBus();
/**
* Returns server version. On proxy installation returns PROXY.
*
* @return server version
*/
public abstract ProtocolVersion getServerVersion();
/**
* Prints message into console
*
* @param message
* message to print
* @param translateColors
* {@code true} if colors should be translated, {@code false} if not
*/
public abstract void sendConsoleMessage(String message, boolean translateColors);
/**
* Returns TAB's Thread manager, which allows task submitting
*
* @return ThreadManager instance
*/
public abstract ThreadManager getThreadManager();
/**
* Returns TAB's cache file used to store player toggle data
*
* @return TAB's player cache file
*/
public abstract ConfigurationFile getPlayerCache();
/**
* Returns TAB's config.yml file
*
* @return config.yml file
*/
public abstract ConfigurationFile getConfig();
/**
* Returns TAB's group configuration
*
* @return TAB's group configuration
*/
public abstract PropertyConfiguration getGroups();
/**
* Returns TAB's user configuration
*
* @return TAB's user configuration
*/
public abstract PropertyConfiguration getUsers();
/**
* Sends a debug message into console if the option
* is enabled in config.
*
* @param message
* Message to send
*/
public abstract void debug(String message);
/**
* Logs an error into errors.log file
*
* @param message
* Error message
* @param t
* Thrown error
*/
public abstract void logError(String message, Throwable t);
/**
* Sets name of file with syntax error, which prevented
* the plugin from enabling. Internal use only.
*
* @param file
* Name of file with syntax error
*/
public abstract void setBrokenFile(String file);
/**
* Returns {@code true} if the plugin is currently disabled due to an error,
* either a yaml syntax error or an internal startup error. Returns {@code false}
* if the plugin is properly running.
*
* @return {@code true} if disabled, {@code false} if running correctly
*/
public abstract boolean isPluginDisabled();
public abstract @Nullable EventBus getEventBus();
}
@@ -1,382 +0,0 @@
package me.neznamy.tab.api;
/**
* A class containing various constants used everywhere in the plugin
* to allow easier overview and modification to prevent inconsistencies.
*/
public class TabConstants {
public static final String PLUGIN_VERSION = "@plugin_version@";
public static final String NO_GROUP = "NONE";
public static final String DEFAULT_GROUP = "_DEFAULT_";
public static final String PLUGIN_MESSAGE_CHANNEL_NAME = "tab:bridge-2";
public static final String REDIS_CHANNEL_NAME = "TAB";
/**
* Private constructor to prevent the class from being instantiated
*/
private TabConstants() {}
/**
* Feature sub-category explaining why / when a certain feature
* needed CPU time to process tasks.
*/
public static class CpuUsageCategory {
public static final String PLAYER_JOIN = "Player Join";
public static final String PLAYER_QUIT = "Player Quit";
public static final String WORLD_SWITCH = "World Switch";
public static final String SERVER_SWITCH = "Server Switch";
public static final String COMMAND_PREPROCESS = "Command Preprocess";
public static final String PLAYER_SNEAK = "Player Sneak";
public static final String PLAYER_RESPAWN = "Player Respawn";
public static final String PLUGIN_MESSAGE = "PluginMessageEvent";
public static final String REDIS_BUNGEE_MESSAGE = "PubSubMessageEvent";
public static final String VANISH_CHANGE = "Vanish status change";
public static final String ANTI_OVERRIDE = "Anti override";
public static final String BYTE_BUF = "ByteBuf";
public static final String PACKET_PLAYER_INFO = "PacketPlayOutPlayerInfo";
public static final String PACKET_DISPLAY_OBJECTIVE = "PacketPlayOutScoreboardDisplayObjective";
public static final String PACKET_OBJECTIVE = "PacketPlayOutScoreboardObjective";
public static final String PACKET_JOIN_GAME = "Login Packet";
public static final String PACKET_ENTITY_MOVE = "PacketPlayOutEntity";
public static final String PACKET_ENTITY_MOVE_PASSENGER = "PacketPlayOutEntity (passenger)";
public static final String PACKET_ENTITY_SPAWN = "PacketPlayOutNamedEntitySpawn";
public static final String PACKET_ENTITY_DESTROY = "PacketPlayOutEntityDestroy";
public static final String RAW_PACKET_IN = "Packet reading (in)";
public static final String RAW_PACKET_OUT = "Packet reading (out)";
public static final String PLACEHOLDER_REFRESHING = "Refreshing placeholders";
public static final String GROUP_REFRESHING = "Refreshing groups";
public static final String REFRESHING_NAME_TAG_VISIBILITY = "Refreshing NameTag visibility";
public static final String SCOREBOARD_PACKET_CHECK = "Checking for other plugins";
public static final String PROCESSING_PLAYER_MOVEMENT = "Processing player movement";
public static final String TELEPORTING_WITHER = "Teleporting wither";
/**
* Private constructor to prevent the class from being instantiated
*/
private CpuUsageCategory() {}
}
/**
* Information why a specific feature sent a packet
* and what the packet sent is supposed to do.
*/
public static class PacketCategory {
public static final String BOSSBAR_COLOR_STYLE = "BossBar (Color and style)";
public static final String BOSSBAR_PROGRESS = "BossBar (Progress)";
public static final String BOSSBAR_TEXT = "BossBar (Text)";
public static final String BOSSBAR_WITHER_TELEPORT = "BossBar (Teleporting Wither)";
public static final String GLOBAL_PLAYERLIST_LATENCY = "Global PlayerList (Updating latency)";
public static final String GLOBAL_PLAYERLIST_VANISH = "Global PlayerList (Vanishing)";
public static final String LAYOUT_FIXED_SLOTS = "Layout (Fixed slots)";
public static final String LAYOUT_PLAYER_SLOTS = "Layout (Player slots)";
public static final String LAYOUT_LATENCY = "Layout (Updating latency)";
public static final String NAMETAGS_TEAM_REGISTER = "NameTags (Team register)";
public static final String NAMETAGS_TEAM_UNREGISTER = "NameTags (Team unregister)";
public static final String NAMETAGS_TEAM_UPDATE = "NameTags (Team update)";
public static final String SORTING_CHANGING_TEAM_NAME = "Sorting (Changing team name)";
public static final String SCOREBOARD_TITLE = "Scoreboard (Title)";
public static final String SCOREBOARD_LINES = "Scoreboard (Lines)";
public static final String UNLIMITED_NAMETAGS_OFFSET_CHANGE = "Unlimited NameTags (Changing offset)";
public static final String UNLIMITED_NAMETAGS_SPAWN = "Unlimited NameTags (Spawning)";
public static final String UNLIMITED_NAMETAGS_DESPAWN = "Unlimited NameTags (Despawning)";
public static final String UNLIMITED_NAMETAGS_TELEPORT = "Unlimited NameTags (Teleporting)";
public static final String UNLIMITED_NAMETAGS_SNEAK = "Unlimited NameTags (Sneaking)";
public static final String UNLIMITED_NAMETAGS_METADATA = "Unlimited NameTags (Metadata)";
/**
* Private constructor to prevent the class from being instantiated
*/
private PacketCategory() {}
}
/**
* Permission nodes used by the plugin
*/
public static class Permission {
public static final String COMMAND_ALL = "tab.admin";
public static final String COMMAND_RELOAD = "tab.reload";
public static final String COMMAND_BOSSBAR_ANNOUNCE = "tab.announce.bar";
public static final String COMMAND_BOSSBAR_TOGGLE = "tab.bossbar.toggle";
public static final String COMMAND_BOSSBAR_SEND = "tab.send.bar";
public static final String COMMAND_CPU = "tab.cpu";
public static final String COMMAND_DEBUG = "tab.debug";
public static final String COMMAND_GROUP_INFO = "tab.groupinfo";
public static final String COMMAND_GROUP_LIST = "tab.grouplist";
public static final String COMMAND_MYSQL_DOWNLOAD = "tab.mysql.download";
public static final String COMMAND_MYSQL_UPLOAD = "tab.mysql.upload";
public static final String COMMAND_NAMETAG_TOGGLE = "tab.nametag.toggle";
public static final String COMMAND_NAMETAG_TOGGLE_OTHER = "tab.nametag.toggle.other";
public static final String COMMAND_NAMETAG_PREVIEW = "tab.nametag.preview";
public static final String COMMAND_NAMETAG_PREVIEW_OTHER = "tab.nametag.preview.other";
public static final String COMMAND_PARSE = "tab.parse";
public static final String COMMAND_SCOREBOARD_ANNOUNCE = "tab.announce.scoreboard";
public static final String COMMAND_SCOREBOARD_TOGGLE = "tab.scoreboard.toggle";
public static final String COMMAND_SCOREBOARD_TOGGLE_OTHER = "tab.scoreboard.toggle.other";
public static final String COMMAND_SCOREBOARD_SHOW = "tab.scoreboard.show";
public static final String COMMAND_SCOREBOARD_SHOW_OTHER = "tab.scoreboard.show.other";
public static final String COMMAND_SETCOLLISION = "tab.setcollision";
public static final String COMMAND_AUTOCOMPLETE = "tab.tabcomplete";
public static final String COMMAND_DATA_REMOVE = "tab.remove";
public static final String COMMAND_PROPERTY_CHANGE_PREFIX = "tab.change.";
public static final String COMMAND_WIDTH = "tab.width";
public static final String STAFF = "tab.staff";
public static final String PER_WORLD_PLAYERLIST_BYPASS = "tab.bypass";
public static final String SEE_VANISHED = "tab.seevanished";
public static final String SPECTATOR_BYPASS = "tab.spectatorbypass";
public static final String TEST_PERMISSION = "tab.testpermission";
public static final String GROUP_PREFIX = "tab.group.";
/**
* Private constructor to prevent the class from being instantiated
*/
private Permission(){}
}
/**
* Internal property names used to store text
* under a specific key
*/
public static class Property {
public static final String HEADER = "header";
public static final String FOOTER = "footer";
public static final String TABPREFIX = "tabprefix";
public static final String CUSTOMTABNAME = "customtabname";
public static final String TABSUFFIX = "tabsuffix";
public static final String TAGPREFIX = "tagprefix";
public static final String CUSTOMTAGNAME = "customtagname";
public static final String TAGSUFFIX = "tagsuffix";
public static final String ABOVENAME = "abovename";
public static final String NAMETAG = "nametag";
public static final String BELOWNAME = "belowname";
public static final String SCOREBOARD_TITLE = "scoreboard-title";
public static final String BELOWNAME_NUMBER = "belowname-number";
public static final String BELOWNAME_TEXT = "belowname-text";
public static final String YELLOW_NUMBER = "yellow-number";
/**
* Private constructor to prevent the class from being instantiated
*/
private Property() {}
public static String bossbarTitle(String name) {
return "bossbar-title-" + name;
}
public static String bossbarProgress(String name) {
return "bossbar-progress-" + name;
}
public static String bossbarColor(String name) {
return "bossbar-color-" + name;
}
public static String bossbarStyle(String name) {
return "bossbar-style-" + name;
}
public static String scoreboardPrefix(String scoreboard, int lineNumber) {
return scoreboard + "-" + lineNumber + "-prefix";
}
public static String scoreboardName(String scoreboard, int lineNumber) {
return scoreboard + "-" + lineNumber + "-name";
}
public static String scoreboardSuffix(String scoreboard, int lineNumber) {
return scoreboard + "-" + lineNumber + "-suffix";
}
}
/**
* Feature names used to register features under in the
* feature manager which they can be retrieved by.
*/
public static class Feature {
//universal features
public static final String HEADER_FOOTER = "HeaderFooter";
public static final String GHOST_PLAYER_FIX = "GhostPlayerFix";
public static final String PLAYER_LIST = "PlayerList";
public static final String SPECTATOR_FIX = "SpectatorFix";
public static final String YELLOW_NUMBER = "YellowNumber";
public static final String BELOW_NAME = "BelowName";
public static final String BELOW_NAME_TEXT = "BelowNameText";
public static final String SCOREBOARD = "ScoreBoard";
public static final String PING_SPOOF = "PingSpoof";
public static final String SORTING = "sorting";
public static final String LAYOUT = "layout";
public static final String LAYOUT_LATENCY = "layout-latency";
public static final String NICK_COMPATIBILITY = "nick";
public static final String PIPELINE_INJECTION = "injection";
public static final String BOSS_BAR = "BossBar";
public static final String NAME_TAGS = "NameTag16";
public static final String NAME_TAGS_COLLISION = "NameTagCollision";
public static final String NAME_TAGS_VISIBILITY = "NameTagVisibility";
public static final String GROUP_MANAGER = "GroupManager";
public static final String PLACEHOLDER_MANAGER = "PlaceholderManager";
public static final String PET_FIX = "PetFix";
public static final String UNLIMITED_NAME_TAGS = "NameTagX";
public static final String UNLIMITED_NAME_TAGS_PACKET_LISTENER = "nametagx-packet";
public static final String UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER = "nametagx-vehicle";
//Bukkit only
public static final String PER_WORLD_PLAYER_LIST = "PerWorldPlayerList";
//BungeeCord only
public static final String REDIS_BUNGEE = "RedisBungee";
public static final String GLOBAL_PLAYER_LIST = "GlobalPlayerList";
public static final String GLOBAL_PLAYER_LIST_LATENCY = "GlobalPlayerList-Latency";
//additional info displayed in cpu command
public static final String PACKET_SERIALIZING = "Packet serializing";
public static final String PACKET_DESERIALIZING = "Packet deserializing";
/**
* Private constructor to prevent the class from being instantiated
*/
private Feature(){}
public static String scoreboardLine(String line) {
return "scoreboard-" + line;
}
public static String scoreboardLine(String scoreboard, int index) {
return "scoreboard-score-" + scoreboard + "-" + index;
}
public static String layout(String name) {
return "layout-" + name;
}
public static String layoutSlot(String layout, int slot) {
return "layout-" + layout + "-slot-" + slot;
}
public static String bossBarTitle(String name) {
return "BossBar-title-" + name;
}
public static String bossBarProgress(String name) {
return "BossBar-progress-" + name;
}
public static String bossBarColorStyle(String name) {
return "BossBar-color-style-" + name;
}
}
/**
* All internal placeholders offered by the plugin
*/
public static class Placeholder {
// Universal
public static final String DISPLAY_NAME = "%displayname%";
public static final String GROUP = "%group%";
public static final String VANISHED = "%vanished%";
public static final String WORLD = "%world%";
public static final String WORLD_ONLINE = "%worldonline%";
public static final String SERVER = "%server%";
public static final String SERVER_ONLINE = "%serveronline%";
public static final String PLAYER = "%player%";
public static final String TIME = "%time%";
public static final String DATE = "%date%";
public static final String PING = "%ping%";
public static final String PLAYER_VERSION = "%player-version%";
public static final String PLAYER_VERSION_ID = "%player-version-id%";
public static final String MEMORY_USED = "%memory-used%";
public static final String MEMORY_MAX = "%memory-max%";
public static final String MEMORY_USED_GB = "%memory-used-gb%";
public static final String MEMORY_MAX_GB = "%memory-max-gb%";
public static final String ONLINE = "%online%";
public static final String STAFF_ONLINE = "%staffonline%";
public static final String NON_STAFF_ONLINE = "%nonstaffonline%";
public static final String LUCKPERMS_PREFIX = "%luckperms-prefix%";
public static final String LUCKPERMS_SUFFIX = "%luckperms-suffix%";
public static final String COUNTDOWN = "%countdown%";
// Placeholders for internal use
public static final String COLLISION = "%collision%";
public static final String INVISIBLE = "%invisible%";
public static final String VEHICLE = "%vehicle%";
// Bukkit only
public static final String TPS = "%tps%";
public static final String MSPT = "%mspt%";
public static final String AFK = "%afk%";
public static final String ESSENTIALS_NICK = "%essentialsnick%";
public static final String VAULT_PREFIX = "%vault-prefix%";
public static final String VAULT_SUFFIX = "%vault-suffix%";
public static final String HEALTH = "%health%";
public static final int MINIMUM_REFRESH_INTERVAL = 50;
public static String condition(String name) {
return "%condition:" + name + "%";
}
public static String animation(String name) {
return "%animation:" + name + "%";
}
public static String globalPlayerListGroup(String group) {
return "%playerlist-group_" + group + "%";
}
}
/**
* All plugins TAB hooks into
*/
public static class Plugin {
public static final String LUCKPERMS = "LuckPerms";
public static final String VAULT = "Vault";
public static final String PLACEHOLDER_API = "PlaceholderAPI";
public static final String LIBS_DISGUISES = "LibsDisguises";
public static final String VIAVERSION = "ViaVersion";
public static final String PROTOCOL_SUPPORT = "ProtocolSupport";
public static final String ESSENTIALS = "Essentials";
public static final String REDIS_BUNGEE = "RedisBungee";
public static final String PREMIUM_VANISH = "PremiumVanish";
}
/**
* TAB's custom metrics charts
*/
public static class MetricsChart {
public static final String PERMISSION_SYSTEM = "permission_system";
public static final String GLOBAL_PLAYER_LIST_ENABLED = "global_playerlist_enabled";
public static final String PLACEHOLDER_API = "placeholderapi";
public static final String SERVER_VERSION = "server_version";
public static final String UNLIMITED_NAME_TAG_MODE_ENABLED = "unlimited_nametag_mode_enabled";
}
}
@@ -1,408 +0,0 @@
package me.neznamy.tab.api;
import java.util.*;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
/**
* Abstract class representing a core feature of the plugin.
* <p>
* It receives all kinds of events and can react to them.
*/
public abstract class TabFeature {
/** Feature's name displayed in /tab cpu */
private final String featureName;
/** Feature's function name displayed in place of refreshing in /tab cpu */
private final String refreshDisplayName;
/** Servers where the feature is disabled (or enabled if using whitelist mode) */
protected String[] disabledServers = new String[0];
/** Flag tracking whether disabled server list is a whitelist or blacklist */
private boolean serverWhitelistMode = false;
/** Worlds where the feature is disabled (or enabled if using whitelist mode) */
protected String[] disabledWorlds = new String[0];
/** Flag tracking whether disabled world list is a whitelist or blacklist */
private boolean worldWhitelistMode = false;
/** Players located in currently disabled worlds / servers */
private final Set<TabPlayer> disabledPlayers = Collections.newSetFromMap(new WeakHashMap<>());
/**
* List of methods overridden (implemented) by each feature.
* If a method is not overridden, it is not called at all.
* This avoids a massive spam for every single method
* in every single feature in /tab cpu output
*/
private final List<String> methodOverrides = new ArrayList<>();
/**
* Constructs new instance with given parameters and loads method overrides
*
* @param featureName
* Feature's name in /tab cpu
* @param refreshDisplayName
* "refreshing" cpu display type name of the feature
*/
protected TabFeature(String featureName, String refreshDisplayName) {
this.featureName = featureName;
this.refreshDisplayName = refreshDisplayName;
try {
if (getClass().getMethod("onCommand", TabPlayer.class, String.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onCommand");
if (getClass().getMethod("onJoin", TabPlayer.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onJoin");
if (getClass().getMethod("onQuit", TabPlayer.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onQuit");
if (getClass().getMethod("onWorldChange", TabPlayer.class, String.class, String.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onWorldChange");
if (getClass().getMethod("onServerChange", TabPlayer.class, String.class, String.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onServerChange");
if (getClass().getMethod("onDisplayObjective", TabPlayer.class, PacketPlayOutScoreboardDisplayObjective.class).getDeclaringClass() != TabFeature.class) {
methodOverrides.add("onDisplayObjective");
TabAPI.getInstance().getFeatureManager().markDisplayObjective();
}
if (getClass().getMethod("onLoginPacket", TabPlayer.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onLoginPacket");
if (getClass().getMethod("onObjective", TabPlayer.class, PacketPlayOutScoreboardObjective.class).getDeclaringClass() != TabFeature.class) {
methodOverrides.add("onObjective");
TabAPI.getInstance().getFeatureManager().markObjective();
}
if (getClass().getMethod("onPlayerInfo", TabPlayer.class, PacketPlayOutPlayerInfo.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onPlayerInfo");
if (getClass().getMethod("onPacketReceive", TabPlayer.class, Object.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onPacketReceive");
if (getClass().getMethod("onPacketSend", TabPlayer.class, Object.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onPacketSend");
if (getClass().getMethod("refresh", TabPlayer.class, boolean.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("refresh");
if (getClass().getMethod("onVanishStatusChange", TabPlayer.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onVanishStatusChange");
} catch (NoSuchMethodException e) {
//this will never happen
}
}
/**
* Constructs new instance with given parameters and loads method overrides.
* Also loads lists of disabled worlds and servers of this feature with config
* section path specified with {@code configSection} parameter.
*
* @param featureName
* Feature's name in /tab cpu
* @param refreshDisplayName
* "refreshing" cpu display type name of the feature
* @param configSection
* Configuration section of the feature to load disabled
* servers / worlds from
*/
protected TabFeature(String featureName, String refreshDisplayName, String configSection) {
this(featureName, refreshDisplayName);
List<String> disabledServers = TabAPI.getInstance().getConfig().getStringList(configSection + ".disable-in-servers");
List<String> disabledWorlds = TabAPI.getInstance().getConfig().getStringList(configSection + ".disable-in-worlds");
if (disabledServers != null) {
this.disabledServers = disabledServers.toArray(new String[0]);
serverWhitelistMode = disabledServers.contains("WHITELIST");
}
if (disabledWorlds != null) {
this.disabledWorlds = disabledWorlds.toArray(new String[0]);
worldWhitelistMode = disabledWorlds.contains("WHITELIST");
}
}
/**
* Loads all online players and sends packets
*/
public void load() {
//empty by default
}
/**
* Unloads all players and sends clear packets
*/
public void unload() {
//empty by default
}
/**
* Processes command from player. This is typically a toggle command.
*
* @param sender
* command sender
* @param message
* command line
* @return {@code true} if event should be cancelled, {@code true} if not
*/
public boolean onCommand(TabPlayer sender, String message) {
return false;
}
/**
* Processes join event
*
* @param connectedPlayer
* player who connected
*/
public void onJoin(TabPlayer connectedPlayer) {
//empty by default
}
/**
* Processes quit event
*
* @param disconnectedPlayer
* player who disconnected
*/
public void onQuit(TabPlayer disconnectedPlayer) {
//empty by default
}
/**
* Processes world switch
*
* @param changed
* player who switched world
* @param from
* world player changed from
* @param to
* world player changed to
*/
public void onWorldChange(TabPlayer changed, String from, String to) {
//empty by default
}
/**
* Processes server switch
*
* @param changed
* player who switched server
* @param from
* server player changed from
* @param to
* server player changed to
*/
public void onServerChange(TabPlayer changed, String from, String to) {
//empty by default
}
/**
* Processes the packet send and returns true if packet should be cancelled
*
* @param receiver
* player receiving packet
* @param packet
* received packet
*/
public void onDisplayObjective(TabPlayer receiver, PacketPlayOutScoreboardDisplayObjective packet) {
//empty by default
}
/**
* Processes login packet, only available on BungeeCord
*
* @param packetReceiver
* player receiving client reset packet
*/
public void onLoginPacket(TabPlayer packetReceiver) {
//empty by default
}
/**
* Processes the packet send
*
* @param receiver
* player receiving packet
* @param packet
* received packet
*/
public void onObjective(TabPlayer receiver, PacketPlayOutScoreboardObjective packet) {
//empty by default
}
/**
* Processes the packet send and possibly modifies it
*
* @param receiver
* player receiving packet
* @param info
* received packet
*/
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
//empty by default
}
/**
* Processes raw packet sent by client
*
* @param sender
* packet sender
* @param packet
* packet received
* @return true if false should be cancelled, false if not
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
return false;
}
/**
* Processes raw packet sent to client
*
* @param receiver
* packet receiver
* @param packet
* the packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
//empty by default
}
/**
* Performs refresh of specified player
*
* @param refreshed
* player to refresh
* @param force
* if refresh should be forced despite refresh seemingly not needed
*/
public void refresh(TabPlayer refreshed, boolean force) {
//empty by default
}
/**
* Processes vanish status change of player
*
* @param player
* Player who changed vanish status
*/
public void onVanishStatusChange(TabPlayer player){
//empty by default
}
/**
* Registers this feature as one using specified placeholders
*
* @param placeholders
* placeholders to add as used in this feature
*/
public void addUsedPlaceholders(Collection<String> placeholders) {
if (placeholders.isEmpty()) return;
placeholders.forEach(p -> TabAPI.getInstance().getPlaceholderManager().addUsedPlaceholder(p, this));
}
/**
* Returns {@code true} if world or server is disabled, {@code false} if not
*
* @param server
* server to check
* @param world
* world to check
* @return {@code true} if feature should be disabled, {@code false} if not
*/
public boolean isDisabled(String server, String world) {
boolean contains = contains(disabledWorlds, world);
if (worldWhitelistMode) contains = !contains;
if (contains) return true;
contains = contains(disabledServers, server);
if (serverWhitelistMode) contains = !contains;
return contains;
}
/**
* Returns {@code true} if list contains the specified element or element
* ends with {@code "*"} and element meeting that requirement is present,
* {@code false} otherwise.
*
* @param list
* List to check
* @param element
* Element to find
* @return {@code true} if element was found, {@code false} if not
*/
protected boolean contains(String[] list, String element) {
if (element == null) return false;
for (String s : list) {
if (s.endsWith("*")) {
if (element.toLowerCase().startsWith(s.substring(0, s.length()-1).toLowerCase())) return true;
} else {
if (element.equalsIgnoreCase(s)) return true;
}
}
return false;
}
/**
* Returns name of the feature displayed in /tab cpu.
*
* @return name of the feature displayed in /tab cpu
*/
public String getFeatureName() {
return featureName;
}
/**
* Returns text to display in /tab cpu of this feature refreshing visuals
*
* @return Text to display as refreshing of this feature
*/
public String getRefreshDisplayName() {
return refreshDisplayName;
}
/**
* Returns {@code true} if method with specified name is overridden
* in the implementation, {@code false} if not
*
* @param method
* Method to check
* @return {@code true} if overridden, {@code false} if not
*/
public boolean overridesMethod(String method) {
return methodOverrides.contains(method);
}
/**
* Returns {@code true} if player is currently located in a server
* or world, which is marked as disabled, {@code false} if not
*
* @param p
* Player to check
* @return {@code true} if player is in disabled server / world,
* {@code false} if not
*/
public boolean isDisabledPlayer(TabPlayer p) {
return disabledPlayers.contains(p);
}
/**
* Adds specified player into list of players in disabled
* servers / worlds.
*
* @param p
* Player to add
*/
public void addDisabledPlayer(TabPlayer p) {
if (disabledPlayers.contains(p)) return;
disabledPlayers.add(p);
}
/**
* Removes specified player from list of players in disabled
* servers / worlds
*
* @param p
* Player to check
*/
public void removeDisabledPlayer(TabPlayer p) {
disabledPlayers.remove(p);
}
}
@@ -1,166 +1,37 @@
package me.neznamy.tab.api;
import java.util.UUID;
import lombok.NonNull;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import io.netty.channel.Channel;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.protocol.Skin;
import me.neznamy.tab.api.protocol.TabPacket;
import java.util.UUID;
/**
* An interface representing a player
*/
@SuppressWarnings("unused") // API class
public interface TabPlayer {
/**
* Refreshes all visuals on the player
*/
void forceRefresh();
/**
* Returns player's name
*
* @return Player's name
*/
String getName();
@NotNull String getName();
/**
* Returns player's UUID
*
* @return Player's UUID
*/
UUID getUniqueId();
/**
* Returns player's uuid used in TabList. This may only be different from real uuid if
* TAB is installed on velocity with some specific velocity setup
*
* @return player's uuid in TabList
*/
UUID getTablistUUID();
/**
* Returns player's protocol version
*
* @return player's protocol version
*/
ProtocolVersion getVersion();
@NotNull UUID getUniqueId();
/**
* Returns platform-specific entity
*
* @return an instance of bukkit/bungee player depending on platform
* @return platform's player object
*/
Object getPlayer();
/**
* Returns player's current world name (on BungeeCord this requires bridge installed)
*
* @return name of world where player is currently in, "N/A" on BungeeCord if bridge is not installed
*/
String getWorld();
/**
* Returns player's current server name on BungeeCord (on bukkit this returns "N/A")
*
* @return name of server where player is currently in, "N/A" on bukkit
*/
String getServer();
/**
* Performs platform-specific API call to check for permission and returns the result
*
* @param permission
* the permission to check for
* @return true if player has permission, false if not
*/
boolean hasPermission(String permission);
/**
* Sends the player a custom universal packet
*
* @param packet
* packet to send
*/
void sendCustomPacket(TabPacket packet);
/**
* Sends the player a custom universal packet and adds that packet into counter that
* is displayed in /tab cpu
*
* @param packet
* packet to send
* @param feature
* feature to increment sent packet counter of
*/
void sendCustomPacket(TabPacket packet, TabFeature feature);
/**
* Sends the player a custom universal packet and adds that packet into counter that
* is displayed in /tab cpu
*
* @param packet
* packet to send
* @param feature
* feature to increment sent packet counter of
*/
void sendCustomPacket(TabPacket packet, String feature);
/**
* Sends the player a platform-specific packet
*
* @param packet
* an instance of packet depending on platform
*/
void sendPacket(Object packet);
/**
* Returns player's property by name
*
* @param name
* name of property
* @return the property or {@code null} if not found
*/
Property getProperty(String name);
/**
* Sends a message to the player
*
* @param message
* message to be sent
* @param translateColors
* whether colors should be translated or not
*/
void sendMessage(String message, boolean translateColors);
/**
* Sends specified component as a chat message
*
* @param message
* message to send
*/
void sendMessage(IChatBaseComponent message);
/**
* Returns player's netty channel. On servers running version 1.7 or older returns {@code null}.
*
* @return player's channel
*/
Channel getChannel();
/**
* Returns player's ping calculated by server
*
* @return player's ping
*/
int getPing();
/**
* Returns player's skin data
*
* @return player's skin
*/
Skin getSkin();
@NotNull Object getPlayer();
/**
* Returns true once the player is successfully loaded (onJoin method ran through all methods)
@@ -169,92 +40,6 @@ public interface TabPlayer {
*/
boolean isLoaded();
/**
* Sets property with specified name to new value. If property did not exist before, it is
* created and {@code true} is returned. If it existed, it is overridden and {@code true} is returned.
* {@code false} is returned otherwise.
*
* @param feature
* feature using this property to get placeholders registered
* @param identifier
* property name
* @param rawValue
* new raw value
* @return {@code true} if value changed / did not exist, {@code false} if value did not change
*/
boolean setProperty(TabFeature feature, String identifier, String rawValue);
/**
* Loads property from config using standard property loading algorithm
*
* @param property
* property name to load
* @return {@code true} if value did not exist or changed, {@code false} otherwise
*/
boolean loadPropertyFromConfig(TabFeature feature, String property);
/**
* Loads property from config using standard property loading algorithm. If the property is
* not set in config, {@code ifNotSet} value is used.
*
* @param property
* property name to load
* @param ifNotSet
* value to use if property is not defined in config
* @return {@code true} if value did not exist or changed, {@code false} otherwise
*/
boolean loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
/**
* Returns name of player's scoreboard team or {@code null} if NameTag feature is disabled
*
* @return name of player's team
*/
String getTeamName();
/**
* Returns {@code true} if player is disguised using LibsDisguises, {@code false} if not
*
* @return {@code true} if player is disguised, {@code false} if not
*/
boolean isDisguised();
/**
* Returns {@code true} if player has invisibility potion, {@code false} if not.
* For bukkit, API is used, for BungeeCord, bridge is used.
*
* @return {@code true} if player has invisibility potion, {@code false} if not
*/
boolean hasInvisibilityPotion();
/**
* Checks if player is vanished using any supported vanish plugin and returns the result.
*
* @return {@code true} if player is vanished, {@code false} if not
*/
boolean isVanished();
/**
* Calls platform-specific method and returns the result
*
* @return {@code true} if player is online, {@code false} if not
*/
boolean isOnline();
/**
* Returns GameMode of the player (0 for survival, 1 creative, 2 adventure, 3 spectator)
*
* @return GameMode of the player
*/
int getGamemode();
/**
* Returns {@code true} if this is a bedrock player, {@code false} if not
*
* @return {@code true} if player is a bedrock player, {@code false} if not
*/
boolean isBedrockPlayer();
/**
* Returns player's primary permission group. If group has been changed using
* {@link #setTemporaryGroup(String)}, returns that value. Otherwise, returns group
@@ -262,59 +47,48 @@ public interface TabPlayer {
*
* @return player's primary permission group
*/
String getGroup();
@NotNull String getGroup();
/**
* Temporarily overrides player's group and applies all changes coming from new group.
* This includes all properties and sorting, if used.
* Set to {@code null} to reset back.
*
* @param group
* New group to use
* @see #hasTemporaryGroup()
* @see #resetTemporaryGroup()
*/
void setTemporaryGroup(String group);
void setTemporaryGroup(@Nullable String group);
/**
* Returns temporary group applied to the player using {@link #setTemporaryGroup(String)}.
* If no group was set, returns {@code null}
* Returns {@code true} if a temporary group was applied to the player using {@link #setTemporaryGroup(String)}.
* If no group was set, returns {@code false}
*
* @return Temporary group assigned to player or {@code null} if not set
* @return {@code true} if group is set, {@code false} if not
* @see #setTemporaryGroup(String)
* @see #resetTemporaryGroup()
*/
boolean hasTemporaryGroup();
/**
* Resets temporary group assigned using {@link #setTemporaryGroup(String)}.
* If no temporary group is set, doesn't do anything.
* 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 Sponge and 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.
*
* @see #setTemporaryGroup(String)
* @see #hasTemporaryGroup()
* @param profileName
* New expected profile name
*/
void resetTemporaryGroup();
void setExpectedProfileName(@NonNull String profileName);
/**
* Returns player's game profile name as seen by other players. By default,
* this returns player's name. If using a nickname / disguise plugin that changes
* game profile name for other players and change was successfully detected, returns
* that value.
* 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 game profile name as seen by other players
* @return Player's expected profile name
*/
String getNickname();
/**
* Returns player's chat message signing key. Returned object is a direct
* object from the server, which is different per platform.
*
* @return Player's direct chat message signing key
*/
Object getProfilePublicKey();
/**
* Return player's chat session ID
* @return player's chat session ID
*/
UUID getChatSessionId();
@NotNull
String getExpectedProfileName();
}
@@ -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
}
@@ -3,34 +3,22 @@ package me.neznamy.tab.api.bossbar;
/**
* An enum representing all available BossBar styles.
* Calling ordinal() will return style's network ID.
* Style names are equal to NMS names.
*/
@SuppressWarnings("unused") // API class
public enum BarStyle {
PROGRESS("SOLID"),
NOTCHED_6("SEGMENTED_6"),
NOTCHED_10("SEGMENTED_10"),
NOTCHED_12("SEGMENTED_12"),
NOTCHED_20("SEGMENTED_20");
/** Fully solid line */
PROGRESS,
/** Style's name used in Bukkit API */
private final String bukkitName;
/** Line segmented into 6 parts */
NOTCHED_6,
/**
* Initializes enum constant with given bukkit name
*
* @param bukkitName
* Name of color to be returned in {@code getBukkitName()}
*/
BarStyle(String bukkitName){
this.bukkitName = bukkitName;
}
/** Line segmented into 10 parts */
NOTCHED_10,
/**
* Returns name of this style in Bukkit API
* @return name of this style in Bukkit API
*/
public String getBukkitName() {
return bukkitName;
}
/** Line segmented into 12 parts */
NOTCHED_12,
/** Line segmented into 20 parts */
NOTCHED_20
}
@@ -3,7 +3,9 @@ package me.neznamy.tab.api.bossbar;
import java.util.List;
import java.util.UUID;
import lombok.NonNull;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
/**
* An interface representing a BossBar line.
@@ -26,7 +28,7 @@ public interface BossBar {
*
* @return name of BossBar
*/
String getName();
@NotNull String getName();
/**
* Returns randomly generated uuid of this BossBar used internally to match
@@ -34,7 +36,7 @@ public interface BossBar {
*
* @return uuid of this BossBar
*/
UUID getUniqueId();
@NotNull UUID getUniqueId();
/**
* Changes BossBar title to specified string, supporting placeholders.
@@ -48,7 +50,7 @@ public interface BossBar {
* @param title
* New title to display in this BossBar
*/
void setTitle(String title);
void setTitle(@NonNull String title);
/**
* Changes BossBar progress to specified string, supporting placeholders.
@@ -64,7 +66,7 @@ public interface BossBar {
* @param progress
* New progress to use in this BossBar
*/
void setProgress(String progress);
void setProgress(@NonNull String progress);
/**
* Changes BossBar progress to specified value ranging from 0 to 100.
@@ -90,7 +92,7 @@ public interface BossBar {
* @param color
* New color to use in this BossBar
*/
void setColor(String color);
void setColor(@NonNull String color);
/**
* Changes BossBar color to specified enum constant.
@@ -100,7 +102,7 @@ public interface BossBar {
* @param color
* New color to use in this BossBar
*/
void setColor(BarColor color);
void setColor(@NonNull BarColor color);
/**
* Changes BossBar style to specified string, supporting placeholders.
@@ -116,7 +118,7 @@ public interface BossBar {
* @param style
* New style to use in this BossBar
*/
void setStyle(String style);
void setStyle(@NonNull String style);
/**
* Changes BossBar style to specified enum constant.
@@ -126,7 +128,7 @@ public interface BossBar {
* @param style
* New style to use in this BossBar
*/
void setStyle(BarStyle style);
void setStyle(@NonNull BarStyle style);
/**
* Returns current title of the BossBar in raw format. If it contains placeholders,
@@ -134,7 +136,7 @@ public interface BossBar {
*
* @return title of the BossBar
*/
String getTitle();
@NotNull String getTitle();
/**
* Returns progress of the BossBar as a string, which is either entered string
@@ -142,7 +144,7 @@ public interface BossBar {
*
* @return entered progress as a string
*/
String getProgress();
@NotNull String getProgress();
/**
* Returns color of the BossBar as a string, which is either entered string
@@ -150,7 +152,7 @@ public interface BossBar {
*
* @return entered color as a string
*/
String getColor();
@NotNull String getColor();
/**
* Returns style of the BossBar as a string, which is either entered string
@@ -158,7 +160,7 @@ public interface BossBar {
*
* @return entered style as a string
*/
String getStyle();
@NotNull String getStyle();
/**
* Registers this BossBar to specified player.
@@ -168,7 +170,7 @@ public interface BossBar {
* @param player
* Player to register this BossBar to
*/
void addPlayer(TabPlayer player);
void addPlayer(@NonNull TabPlayer player);
/**
* Unregisters this BossBar from specified player.
@@ -178,7 +180,7 @@ public interface BossBar {
* @param player
* Player to unregister this BossBar from
*/
void removePlayer(TabPlayer player);
void removePlayer(@NonNull TabPlayer player);
/**
* Returns list of players who can see this BossBar.
@@ -189,19 +191,24 @@ public interface BossBar {
*
* @return List of players seeing this BossBar
*/
List<TabPlayer> getPlayers();
@NotNull List<TabPlayer> getPlayers();
/**
* Returns {@code true} if specified player can see this BossBar, {@code false} if not
* Returns {@code true} if this bossbar is visible to specified player based on
* display condition or adding via {@link #addPlayer(TabPlayer)}, {@code false} if not.
* <p>
* This may return {@code true} even if player cannot physically see the bossbar due to
* having it disabled via toggle command or {@link BossBarManager#toggleBossBar(TabPlayer, boolean)}
* (or {@link BossBarManager#setBossBarVisible(TabPlayer, boolean, boolean)}).
*
* @param player
* Player to check for
* @return {@code true} if contains, {@code false} if not
* 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 bossbar in configured as announcement-only bar,
* Returns {@code true} if this boss bar is configured as announcement-only bar,
* {@code false} if not.
*
* @return {@code true} if bar is announcement only, {@code false} if not
@@ -2,10 +2,12 @@ package me.neznamy.tab.api.bossbar;
import java.util.List;
import java.util.Map;
import java.util.UUID;
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;
/**
* An interface allowing work with BossBars such as creating, sending
@@ -15,6 +17,7 @@ import me.neznamy.tab.api.TabPlayer;
* This requires the BossBar feature to be enabled in config, otherwise the method will
* return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface BossBarManager {
/**
@@ -31,7 +34,7 @@ public interface BossBarManager {
* Style to use
* @return The newly created BossBar
*/
BossBar createBossBar(String title, float progress, BarColor color, BarStyle style);
@NotNull BossBar createBossBar(@NonNull String title, float progress, @NonNull BarColor color, @NonNull BarStyle style);
/**
* Creates BossBar with specified parameters and registers it to BossBar manager,
@@ -50,30 +53,19 @@ public interface BossBarManager {
* a placeholder that returns one of them
* @return The newly created BossBar
*/
BossBar createBossBar(String title, String progress, String color, String style);
@NotNull BossBar createBossBar(@NonNull String title, @NonNull String progress, @NonNull String color, @NonNull String style);
/**
* Returns registered BossBar with given name. For BossBars from config it is the name
* they were created with, for API BossBars their randomly generated name, which can be
* retrieved using {@link me.neznamy.tab.api.bossbar.BossBar#getName()}.
* Returns null if no such BossBar exists.
* Returns {@code null} if no such BossBar exists.
*
* @param name
* Name of registered BossBar
* @return BossBar with specified name
*/
BossBar getBossBar(String name);
/**
* Returns registered BossBar with specified UUID. This UUID is randomly generated when
* BossBar is created and used in packets / to match which the one from Bukkit API.
* Returns null if no such BossBar exists.
*
* @param id
* UUID to return BossBar by
* @return BossBar with specified uuid
*/
BossBar getBossBar(UUID id);
@Nullable BossBar getBossBar(@NonNull String name);
/**
* Returns a map of all registered BossBars. This includes both BossBars from config
@@ -84,7 +76,7 @@ public interface BossBarManager {
*
* @return all registered BossBars
*/
Map<String, BossBar> getRegisteredBossBars();
@NotNull Map<String, BossBar> getRegisteredBossBars();
/**
* Toggles BossBar for this player and sends toggle message if {@code sendToggleMessage} is true.
@@ -96,7 +88,7 @@ public interface BossBarManager {
* @see #hasBossBarVisible(TabPlayer)
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
*/
void toggleBossBar(TabPlayer player, boolean sendToggleMessage);
void toggleBossBar(@NonNull TabPlayer player, boolean sendToggleMessage);
/**
* Returns true if player can see BossBars, false if toggled via command or API.
@@ -107,7 +99,7 @@ public interface BossBarManager {
* @see #toggleBossBar(TabPlayer, boolean)
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
*/
boolean hasBossBarVisible(TabPlayer player);
boolean hasBossBarVisible(@NonNull TabPlayer player);
/**
* Sets BossBar visibility of player to set value. If value did not change, nothing happens.
@@ -122,7 +114,7 @@ public interface BossBarManager {
* @see #toggleBossBar(TabPlayer, boolean)
* @see #hasBossBarVisible(TabPlayer)
*/
void setBossBarVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
void setBossBarVisible(@NonNull TabPlayer player, boolean visible, boolean sendToggleMessage);
/**
* Temporarily displays registered BossBar to player for specified amount of time in milliseconds.
@@ -138,7 +130,7 @@ public interface BossBarManager {
* @see #announceBossBar(String, int)
* @see #getAnnouncedBossBars()
*/
void sendBossBarTemporarily(TabPlayer player, String bossBar, int duration);
void sendBossBarTemporarily(@NonNull TabPlayer player, @NonNull String bossBar, int duration);
/**
* Temporarily displays registered BossBar to all players for specified amount of time in milliseconds.
@@ -152,7 +144,7 @@ public interface BossBarManager {
* @see #sendBossBarTemporarily(TabPlayer, String, int)
* @see #getAnnouncedBossBars()
*/
void announceBossBar(String bossBar, int duration);
void announceBossBar(@NonNull String bossBar, int duration);
/**
* Returns list of BossBars which are currently being announced.
@@ -161,5 +153,5 @@ public interface BossBarManager {
* @see #sendBossBarTemporarily(TabPlayer, String, int)
* @see #announceBossBar(String, int)
*/
List<BossBar> getAnnouncedBossBars();
@NotNull List<BossBar> getAnnouncedBossBars();
}
@@ -1,62 +0,0 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.util.Preconditions;
/**
* Class for click event action in chat component
*/
public class ChatClickable {
/** Click action */
private final EnumClickAction action;
/** Click value */
private final String value;
/**
* Constructs new instance with given action and value.
*
* @param action
* click event action
* @param value
* click event value
* @throws IllegalArgumentException
* if {@code action} is null or {@code value} is null
*/
public ChatClickable(EnumClickAction action, String value) {
Preconditions.checkNotNull(action, "hover action");
Preconditions.checkNotNull(value, "hover value");
this.action = action;
this.value = value;
}
/**
* Returns click action defined in constructor
*
* @return click action
*/
public EnumClickAction getAction() {
return action;
}
/**
* Returns click action defined in constructor
*
* @return click action
*/
public String getValue() {
return value;
}
/**
* Enum for all possible click actions
*/
public enum EnumClickAction {
OPEN_URL,
RUN_COMMAND,
CHANGE_PAGE, //since 1.8
SUGGEST_COMMAND,
COPY_TO_CLIPBOARD //since 1.15
}
}
@@ -1,37 +0,0 @@
package me.neznamy.tab.api.chat;
import java.util.UUID;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
public class ChatComponentEntity extends IChatBaseComponent {
private final String CONTENTS;
private final String VALUE_1_13;
private final String VALUE_1_12;
private final String VALUE_1_8;
public ChatComponentEntity(String type, UUID id, String name) {
Preconditions.checkNotNull(type, "type");
Preconditions.checkNotNull(id, "id");
this.CONTENTS = String.format("{\"type\":\"%s\",\"id\":\"%s\",\"name\":{\"text\":\"%s\"}}", type, id, name);
this.VALUE_1_13 = String.format("{type:\"%s\",id:\"%s\",name:\"{\\\"text\\\":\\\"%s\\\"}\"}", type, id, name);
this.VALUE_1_12 = String.format("{type:\"%s\",id:\"%s\",name:\"%s\"}", type, id, name);
this.VALUE_1_8 = String.format("{type:%s,id:%s,name:%s}", type, id, name);
}
@Override
public String toRawText() {
return toString();
}
@Override
public String toString() {
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) return CONTENTS;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 13) return VALUE_1_13;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() == 12) return VALUE_1_12;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 8) return VALUE_1_8;
throw new IllegalStateException("show_entity hover action is not supported on <1.8");
}
}
@@ -1,70 +0,0 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.util.Preconditions;
/**
* Class for hover event action in chat component
*/
public class ChatHoverable {
/** Hover action */
private final EnumHoverAction action;
/** Hover value */
private final IChatBaseComponent value;
/**
* Constructs new instance with given action and value.
*
* @param action
* hover event action
* @param value
* hover event value
* @throws IllegalArgumentException
* if {@code action} is null or {@code value} is null
*/
public ChatHoverable(EnumHoverAction action, IChatBaseComponent value) {
Preconditions.checkNotNull(action, "hover action");
Preconditions.checkNotNull(value, "hover value");
this.action = action;
this.value = value;
}
/**
* Returns hover action defined in constructor
*
* @return hover action
*/
public EnumHoverAction getAction() {
return action;
}
/**
* Returns hover value defined in constructor
*
* @return hover value
*/
public IChatBaseComponent getValue() {
return value;
}
/**
* Enum for all possible hover actions
*/
public enum EnumHoverAction {
SHOW_TEXT("contents"),
SHOW_ITEM("value"),
SHOW_ENTITY("contents");
private final String preferredKey;
EnumHoverAction(String preferredKey) {
this.preferredKey = preferredKey;
}
public String getPreferredKey() {
return preferredKey;
}
}
}
@@ -1,316 +0,0 @@
package me.neznamy.tab.api.chat;
import java.util.UUID;
import me.neznamy.tab.api.util.Preconditions;
import org.json.simple.JSONObject;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.chat.ChatClickable.EnumClickAction;
import me.neznamy.tab.api.chat.ChatHoverable.EnumHoverAction;
public class ChatModifier {
private TextColor color;
private Boolean bold;
private Boolean italic;
private Boolean underlined;
private Boolean strikethrough;
private Boolean obfuscated;
private ChatClickable clickEvent;
private ChatHoverable hoverEvent;
private String font;
private ProtocolVersion targetVersion;
public ChatModifier() {
}
public ChatModifier(ChatModifier modifier) {
Preconditions.checkNotNull(modifier, "modifier");
this.color = modifier.color == null ? null : new TextColor(modifier.color);
this.bold = modifier.bold;
this.italic = modifier.italic;
this.underlined = modifier.underlined;
this.strikethrough = modifier.strikethrough;
this.obfuscated = modifier.obfuscated;
this.clickEvent = modifier.clickEvent == null ? null : new ChatClickable(modifier.clickEvent.getAction(), modifier.clickEvent.getValue());
this.hoverEvent = modifier.hoverEvent == null ? null : new ChatHoverable(modifier.hoverEvent.getAction(), modifier.hoverEvent.getValue());
this.font = modifier.font;
this.targetVersion = modifier.targetVersion;
}
public TextColor getColor() {
return color;
}
public void setColor(TextColor color) {
this.color = color;
}
public Boolean getBold() {
return bold;
}
public void setBold(Boolean bold) {
this.bold = bold;
}
public Boolean getItalic() {
return italic;
}
public void setItalic(Boolean italic) {
this.italic = italic;
}
public Boolean getUnderlined() {
return underlined;
}
public void setUnderlined(Boolean underlined) {
this.underlined = underlined;
}
public Boolean getStrikethrough() {
return strikethrough;
}
public void setStrikethrough(Boolean strikethrough) {
this.strikethrough = strikethrough;
}
public Boolean getObfuscated() {
return obfuscated;
}
public void setObfuscated(Boolean obfuscated) {
this.obfuscated = obfuscated;
}
/**
* Returns true if bold is defined and set to true, false otherwise
*
* @return true if bold is defined and set to true, false otherwise
*/
public boolean isBold(){
return Boolean.TRUE.equals(bold);
}
/**
* Returns true if italic is defined and set to true, false otherwise
*
* @return true if italic is defined and set to true, false otherwise
*/
public boolean isItalic(){
return Boolean.TRUE.equals(italic);
}
/**
* Returns true if underlined is defined and set to true, false otherwise
*
* @return true if underlined is defined and set to true, false otherwise
*/
public boolean isUnderlined(){
return Boolean.TRUE.equals(underlined);
}
/**
* Returns true if strikethrough is defined and set to true, false otherwise
*
* @return true if strikethrough is defined and set to true, false otherwise
*/
public boolean isStrikethrough(){
return Boolean.TRUE.equals(strikethrough);
}
/**
* Returns true if obfuscation is defined and set to true, false otherwise
*
* @return true if obfuscation is defined and set to true, false otherwise
*/
public boolean isObfuscated(){
return Boolean.TRUE.equals(obfuscated);
}
public ChatClickable getClickEvent() {
return clickEvent;
}
public void setClickEvent(ChatClickable clickEvent) {
this.clickEvent = clickEvent;
}
public ChatHoverable getHoverEvent() {
return hoverEvent;
}
public void setHoverEvent(ChatHoverable hoverEvent) {
this.hoverEvent = hoverEvent;
}
/**
* Sets click action to OPEN_URL and url to given value
*
* @param url
* url to open
*/
public void onClickOpenUrl(String url) {
Preconditions.checkNotNull(url, "url");
clickEvent = new ChatClickable(EnumClickAction.OPEN_URL, url);
}
/**
* Sets click action to RUN_COMMAND and command to given value
*
* @param command
* command to perform, might be without / to send a chat message
*/
public void onClickRunCommand(String command) {
Preconditions.checkNotNull(command, "command");
clickEvent = new ChatClickable(EnumClickAction.RUN_COMMAND, command);
}
/**
* Sets click action to SUGGEST_COMMAND and command to given value
*
* @param command
* command to suggest
*/
public void onClickSuggestCommand(String command) {
Preconditions.checkNotNull(command, "command");
clickEvent = new ChatClickable(EnumClickAction.SUGGEST_COMMAND, command);
}
/**
* Sets click action to CHANGE_PAGE and page id to given value
*
* @param newPage
* id of new page
*/
public void onClickChangePage(int newPage) {
if (TabAPI.getInstance().getServerVersion().getMinorVersion() < 8) throw new UnsupportedOperationException("change_page click action is not supported on <1.8");
clickEvent = new ChatClickable(EnumClickAction.CHANGE_PAGE, String.valueOf(newPage));
}
/**
* Sets click action to COPY_TO_CLIPBOARD and text to provided value
*
* @param text
* text to copy to clipboard on click
*/
public void onClickCopyToClipBoard(String text) {
Preconditions.checkNotNull(text, "text");
clickEvent = new ChatClickable(EnumClickAction.COPY_TO_CLIPBOARD, text);
}
public void onClick(EnumClickAction action, String value) {
Preconditions.checkNotNull(action, "action");
Preconditions.checkNotNull(value, "value");
clickEvent = new ChatClickable(action, value);
}
/**
* Sets hover action to SHOW_TEXT and text to given value
*
* @param text
* text to show
*/
public void onHoverShowText(IChatBaseComponent text) {
Preconditions.checkNotNull(text, "text");
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_TEXT, text);
}
/**
* Sets hover action to SHOW_ITEM and item to given value
*
* @param serializedItem
* item to show
*/
public void onHoverShowItem(String serializedItem) {
Preconditions.checkNotNull(serializedItem, "item");
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ITEM, new IChatBaseComponent(serializedItem));
}
/**
* Sets hover action to SHOW_ENTITY and entity data to given values
*
* @param type
* entity type
* @param id
* entity uuid
* @param name
* entity custom name
*/
public void onHoverShowEntity(String type, UUID id, String name) {
Preconditions.checkNotNull(type, "type");
Preconditions.checkNotNull(id, "id");
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new ChatComponentEntity(type, id, name));
}
public void onHover(EnumHoverAction action, IChatBaseComponent value) {
Preconditions.checkNotNull(action, "action");
Preconditions.checkNotNull(value, "value");
hoverEvent = new ChatHoverable(action, value);
}
public String getFont() {
return font;
}
public void setFont(String font) {
this.font = font;
}
@SuppressWarnings("unchecked")
public JSONObject serialize() {
JSONObject json = new JSONObject();
if (color != null) json.put("color", targetVersion.getMinorVersion() >= 16 ? color.toString() : color.getLegacyColor().toString().toLowerCase());
if (bold != null) json.put("bold", bold);
if (italic != null) json.put("italic", italic);
if (underlined != null) json.put("underlined", underlined);
if (strikethrough != null) json.put("strikethrough", strikethrough);
if (obfuscated != null) json.put("obfuscated", obfuscated);
if (clickEvent != null) {
JSONObject click = new JSONObject();
click.put("action", clickEvent.getAction().toString().toLowerCase());
click.put("value", clickEvent.getValue());
json.put("clickEvent", click);
}
if (hoverEvent != null) {
JSONObject hover = new JSONObject();
hover.put("action", hoverEvent.getAction().toString().toLowerCase());
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) {
hover.put(hoverEvent.getAction().getPreferredKey(), hoverEvent.getValue());
} else {
hover.put("value", TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 ?
hoverEvent.getValue() : hoverEvent.getValue().toRawText());
}
json.put("hoverEvent", hover);
}
if (font != null) json.put("font", font);
return json;
}
public String getMagicCodes() {
StringBuilder builder = new StringBuilder();
if (isBold()) builder.append(EnumChatFormat.BOLD.getFormat());
if (isItalic()) builder.append(EnumChatFormat.ITALIC.getFormat());
if (isUnderlined()) builder.append(EnumChatFormat.UNDERLINE.getFormat());
if (isStrikethrough()) builder.append(EnumChatFormat.STRIKETHROUGH.getFormat());
if (isObfuscated()) builder.append(EnumChatFormat.OBFUSCATED.getFormat());
return builder.toString();
}
public void setTargetVersion(ProtocolVersion targetVersion) {
this.targetVersion = targetVersion;
}
public ProtocolVersion getTargetVersion() {
return targetVersion;
}
}
@@ -1,171 +0,0 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.List;
/**
* A class representing a chat component that was serialized before,
* but deserialize was requested in PacketPlayOutPlayerInfo packet.
* This is used to read and modify the packet and then rewrite it.
* However, display name components must be deserialized to properly
* forward them in the new packet, but its value is not actually
* used anywhere.
* <p>
* To avoid this heavy and completely unnecessary operation, the original
* string is saved and returned during serialization. If any read/write
* operation is performed, the string is fully deserialized and this object
* will act like a normal chat component.
*/
@SuppressWarnings("unchecked")
public class DeserializedChatComponent extends IChatBaseComponent {
/** The original serialized component string */
private final String json;
/**
* Flag tracking whether this component was fully deserialized
* in case of a read operation to avoid repeating the deserialization
* process on every request.
*/
private boolean deserialized;
/**
* Flag tracking whether this component was modified, which means
* a new string has to be created instead of returning the input one.
*/
private boolean modified;
/**
* Constructs new instance with given parameter
*
* @param json
* serialized component as string
*/
public DeserializedChatComponent(String json) {
Preconditions.checkNotNull(json, "json");
this.json = json;
}
@Override
public String toString() {
if (modified) return super.toString();
return json;
}
@Override
public String toString(ProtocolVersion clientVersion) {
if (modified) return super.toString(clientVersion);
return json;
}
@Override
public List<IChatBaseComponent> getExtra(){
if (!deserialized) deserialize();
return super.getExtra();
}
@Override
public String getText() {
if (!deserialized) deserialize();
return super.getText();
}
@Override
public ChatModifier getModifier() {
if (!deserialized) deserialize();
return super.getModifier();
}
@Override
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
if (!deserialized) deserialize();
modified = true;
return super.setExtra(components);
}
@Override
public void addExtra(IChatBaseComponent child) {
if (!deserialized) deserialize();
modified = true;
super.addExtra(child);
}
@Override
public void setModifier(ChatModifier modifier) {
if (!deserialized) deserialize();
modified = true;
super.setModifier(modifier);
}
/**
* Performs a full deserialization process on this component.
*/
void deserialize() {
deserialized = true;
if (json.startsWith("\"") && json.endsWith("\"") && json.length() > 1) {
//simple component with only text used, minecraft serializer outputs the text in quotes instead of full json
setText(json.substring(1, json.length()-1));
return;
}
JSONObject jsonObject;
try {
jsonObject = (JSONObject) new JSONParser().parse(json);
} catch (ParseException e) {
TabAPI.getInstance().logError("Failed to deserialize json component " + json, e);
return;
}
/* if (jsonObject.containsKey("type")) {
return new ChatComponentEntity((String) jsonObject.get("type"), UUID.fromString((String) jsonObject.get("id")), IChatBaseComponent.deserialize(jsonObject.get("name").toString()).toFlatText());
}*/
setText((String) jsonObject.get("text"));
getModifier().setBold(getBoolean(jsonObject, "bold"));
getModifier().setItalic(getBoolean(jsonObject, "italic"));
getModifier().setUnderlined(getBoolean(jsonObject, "underlined"));
getModifier().setStrikethrough(getBoolean(jsonObject, "strikethrough"));
getModifier().setObfuscated(getBoolean(jsonObject, "obfuscated"));
getModifier().setColor(TextColor.fromString(((String) jsonObject.get("color"))));
if (jsonObject.containsKey("clickEvent")) {
JSONObject clickEvent = (JSONObject) jsonObject.get("clickEvent");
String action = (String) clickEvent.get("action");
String value = clickEvent.get("value").toString();
getModifier().onClick(ChatClickable.EnumClickAction.valueOf(action.toUpperCase()), value);
}
if (jsonObject.containsKey("hoverEvent")) {
JSONObject hoverEvent = (JSONObject) jsonObject.get("hoverEvent");
String action = (String) hoverEvent.get("action");
String value = (String) hoverEvent.get("value");
getModifier().onHover(ChatHoverable.EnumHoverAction.valueOf(action.toUpperCase()), deserialize(value));
}
if (jsonObject.containsKey("extra")) {
List<Object> list = (List<Object>) jsonObject.get("extra");
for (Object extra : list) {
String string = extra.toString();
//reverting .toString() removing "" for simple text
if (!string.startsWith("{")) string = "\"" + string + "\"";
addExtra(IChatBaseComponent.deserialize(string));
}
}
}
/**
* Returns boolean value of requested key from map
*
* @param jsonObject
* map to get value from
* @param key
* name of key
* @return value from json object or null if not present
*/
private static Boolean getBoolean(JSONObject jsonObject, String key) {
Preconditions.checkNotNull(jsonObject, "json object");
Preconditions.checkNotNull(key, "key");
String value = String.valueOf(jsonObject.getOrDefault(key, null));
return "null".equals(value) ? null : Boolean.parseBoolean(value);
}
}
@@ -1,438 +0,0 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.api.util.Preconditions;
import org.json.simple.JSONObject;
import java.util.*;
/**
* A class representing the n.m.s.IChatBaseComponent class to make work with it much easier
*/
@SuppressWarnings("unchecked")
public class IChatBaseComponent {
/**
* Component cache maps to avoid large memory allocations as well as
* higher CPU usage when using animations which send the same text on repeat.
*/
private static final Map<String, IChatBaseComponent> componentCache = new HashMap<>();
private static final Map<IChatBaseComponent, String> serializeCacheModern = new HashMap<>();
private static final Map<IChatBaseComponent, String> serializeCacheLegacy = new HashMap<>();
/** Text of the component */
private String text;
/** Chat modifier containing color, magic codes, hover and click event */
private ChatModifier modifier = new ChatModifier();
/** Extra components used in "extra" field */
private List<IChatBaseComponent> extra;
/**
* Constructs a new empty component
*/
public IChatBaseComponent() {
}
/**
* Constructs a new component which is a clone of provided component
*
* @param component
* component to clone
*/
public IChatBaseComponent(IChatBaseComponent component) {
Preconditions.checkNotNull(component, "component");
this.text = component.text;
this.modifier = new ChatModifier(component.modifier);
for (IChatBaseComponent child : component.getExtra()) {
addExtra(new IChatBaseComponent(child));
}
}
/**
* Constructs new instance with given text
*
* @param text
* text to display
*/
public IChatBaseComponent(String text) {
this.text = text;
}
/**
* Returns list of extra components. If no extra components are defined, returns empty list.
*
* @return list of extra components
*/
public List<IChatBaseComponent> getExtra(){
if (extra == null) return Collections.emptyList();
return extra;
}
/**
* Sets full list of extra components to given list. Does not allow empty list.
*
* @param components
* components to use as extra
* @return self
* @throws IllegalArgumentException
* if {@code components} is an empty list
*/
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
if (components.isEmpty()) throw new IllegalArgumentException("Unexpected empty array of components"); //exception taken from minecraft
this.extra = components;
return this;
}
/**
* Appends provided component as extra component
*
* @param child
* component to append
*/
public void addExtra(IChatBaseComponent child) {
Preconditions.checkNotNull(child, "extra");
if (extra == null) extra = new ArrayList<>();
extra.add(child);
}
/**
* Returns text of this component
*
* @return text of this component
*/
public String getText() {
return text;
}
/**
* Returns chat modifier of this component
*
* @return chat modifier of this component
*/
public ChatModifier getModifier() {
return modifier;
}
/**
* Sets modifier to provided value
*
* @param modifier
* modifier to set value to
* @throws IllegalArgumentException
* if {@code modifier} is null
*/
public void setModifier(ChatModifier modifier) {
Preconditions.checkNotNull(modifier, "modifier");
this.modifier = modifier;
}
/**
* Changes text of this component
*
* @param text
* text to show
*/
public void setText(String text) {
this.text = text;
}
/**
* Deserializes string and returns created component. If provided string is null, returns null.
*
* @param json
* serialized string
* @return Deserialized component or null if input is null
*/
public static IChatBaseComponent deserialize(String json) {
if (json == null) return null;
return new DeserializedChatComponent(json);
}
/**
* Converts the component to a string representing the serialized component.
* This method is only used internally by json library since it's missing
* protocol version field used by the method.
*
* @return serialized component in string form
* @see #toString(ProtocolVersion)
*/
@Override
public String toString() {
JSONObject json = new JSONObject();
if (text != null) json.put("text", text);
if (modifier.getTargetVersion() == null) modifier.setTargetVersion(TabAPI.getInstance().getServerVersion()); //packet.toString() was called as a part of a debug message
json.putAll(modifier.serialize());
if (extra != null) json.put("extra", extra);
return json.toString();
}
/**
* Serializes this component with colors based on client version.
* If client version is &lt;1.16, HEX colors will be converted to legacy colors.
*
* @param clientVersion
* client version to adapt component for
* @return serialized string
*/
public String toString(ProtocolVersion clientVersion) {
return toString(clientVersion, false);
}
/**
* Serializes this component with colors based on client version.
* If client version is &lt;1.16, HEX colors will be converted to legacy colors.
*
* @param clientVersion
* client version to adapt component for
* @param sendTranslatableIfEmpty
* whether empty translatable should be sent if text is empty or not
* @return serialized string
*/
public String toString(ProtocolVersion clientVersion, boolean sendTranslatableIfEmpty) {
if (extra == null) {
if (text == null) return null;
if (text.length() == 0) {
if (sendTranslatableIfEmpty) {
return "{\"translate\":\"\"}";
} else {
return "{\"text\":\"\"}";
}
}
}
modifier.setTargetVersion(clientVersion);
for (IChatBaseComponent child : getExtra()) {
child.modifier.setTargetVersion(clientVersion);
}
String string;
if (clientVersion.getMinorVersion() >= 16) {
if (serializeCacheModern.containsKey(this)) return serializeCacheModern.get(this);
string = toString();
if (serializeCacheModern.size() > 10000) serializeCacheModern.clear();
serializeCacheModern.put(this, string);
} else {
if (serializeCacheLegacy.containsKey(this)) return serializeCacheLegacy.get(this);
string = toString();
if (serializeCacheLegacy.size() > 10000) serializeCacheLegacy.clear();
serializeCacheLegacy.put(this, string);
}
return string;
}
/**
* Returns organized component from colored text
*
* @param originalText
* text to convert
* @return organized component from colored text
*/
public static IChatBaseComponent fromColoredText(String originalText){
Preconditions.checkNotNull(originalText, "text");
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText));
List<IChatBaseComponent> components = new ArrayList<>();
StringBuilder builder = new StringBuilder();
IChatBaseComponent component = new IChatBaseComponent();
for (int i = 0; i < text.length(); i++){
char c = text.charAt(i);
if (c == EnumChatFormat.COLOR_CHAR){
i++;
if (i >= text.length()) {
break;
}
c = text.charAt(i);
if ((c >= 'A') && (c <= 'Z')) {
c = (char)(c + ' ');
}
EnumChatFormat format = EnumChatFormat.getByChar(c);
if (format != null){
if (builder.length() > 0) {
component.setText(builder.toString());
components.add(component);
component = new IChatBaseComponent(component);
component.text = null;
builder = new StringBuilder();
}
switch (format){
case BOLD:
component.modifier.setBold(true);
break;
case ITALIC:
component.modifier.setItalic(true);
break;
case UNDERLINE:
component.modifier.setUnderlined(true);
break;
case STRIKETHROUGH:
component.modifier.setStrikethrough(true);
break;
case OBFUSCATED:
component.modifier.setObfuscated(true);
break;
case RESET:
component = new IChatBaseComponent();
component.modifier.setColor(new TextColor(EnumChatFormat.WHITE));
break;
default:
component = new IChatBaseComponent();
component.modifier.setColor(new TextColor(format));
break;
}
}
} else if (c == '#' && text.length() > i+6){
String hex = text.substring(i+1, i+7);
if (RGBUtils.getInstance().isHexCode(hex)) {
TextColor color;
if (containsLegacyCode(text, i)) {
color = new TextColor(hex, EnumChatFormat.getByChar(text.charAt(i+8)));
i += 8;
} else {
color = new TextColor(hex);
i += 6;
}
if (builder.length() > 0){
component.setText(builder.toString());
components.add(component);
builder = new StringBuilder();
}
component = new IChatBaseComponent();
component.modifier.setColor(color);
} else {
builder.append(c);
}
} else {
builder.append(c);
}
}
component.setText(builder.toString());
components.add(component);
return new IChatBaseComponent("").setExtra(components);
}
/**
* Returns true if text contains legacy color request at defined RGB index start
*
* @param text
* text to check
* @param i
* current index start
* @return true if legacy color is defined, false if not
*/
private static boolean containsLegacyCode(String text, int i) {
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
return EnumChatFormat.getByChar(text.charAt(i+8)) != null;
}
/**
* Converts this component into a simple text with legacy colors (the closest match if color is set to RGB)
*
* @return The simple text format
*/
public String toLegacyText() {
StringBuilder builder = new StringBuilder();
append(builder, "");
return builder.toString();
}
/**
* Appends text to string builder, might also add color and magic codes if they are different
* from previous component in chain.
*
* @param builder
* builder to append text to
* @param previousFormatting
* colors and magic codes in previous component
* @return new formatting, might be identical to previous one
*/
private String append(StringBuilder builder, String previousFormatting) {
String formatting = previousFormatting;
if (text != null) {
formatting = getFormatting();
if (!formatting.equals(previousFormatting)) {
builder.append(formatting);
}
builder.append(text);
}
for (IChatBaseComponent component : getExtra()) {
formatting = component.append(builder, formatting);
}
return formatting;
}
/**
* Returns colors and magic codes of this component
*
* @return used colors and magic codes
*/
private String getFormatting() {
StringBuilder builder = new StringBuilder();
if (modifier.getColor() != null) {
if (modifier.getColor().getLegacyColor() == EnumChatFormat.WHITE) {
//preventing unwanted &r -> &f conversion and stopping the <1.13 client bug fix from working
builder.append(EnumChatFormat.RESET.getFormat());
} else {
builder.append(modifier.getColor().getLegacyColor().getFormat());
}
}
builder.append(modifier.getMagicCodes());
return builder.toString();
}
/**
* Returns raw text without colors, only works correctly when component is organized
*
* @return raw text in this component and all child components
*/
public String toRawText() {
StringBuilder builder = new StringBuilder();
if (text != null) builder.append(text);
for (IChatBaseComponent child : getExtra()) {
if (child.text != null) builder.append(child.text);
}
return builder.toString();
}
/**
* Converts the component into flat text with used colors (including rgb) and magic codes
*
* @return converted text
*/
public String toFlatText() {
StringBuilder builder = new StringBuilder();
if (modifier.getColor() != null) builder.append("#").append(modifier.getColor().getHexCode());
builder.append(modifier.getMagicCodes());
if (text != null) builder.append(text);
for (IChatBaseComponent child : getExtra()) {
builder.append(child.toFlatText());
}
return builder.toString();
}
/**
* Returns the most optimized component based on text. Returns null if text is null,
* organized component if RGB colors are used or simple component with only text field
* containing the whole text when no RGB colors are used
*
* @param text
* text to create component from
* @return The most performance-optimized component based on text
*/
public static IChatBaseComponent optimizedComponent(String text){
if (text == null) return null;
if (componentCache.containsKey(text)) return componentCache.get(text);
IChatBaseComponent component;
if (text.contains("#") || text.contains("&x") || text.contains(EnumChatFormat.COLOR_CHAR + "x") || text.contains("<")){
//contains RGB colors
component = IChatBaseComponent.fromColoredText(text);
} else {
//no RGB
component = new IChatBaseComponent(text);
}
if (componentCache.size() > 10000) componentCache.clear();
componentCache.put(text, component);
return component;
}
}
@@ -1,75 +0,0 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.ProtocolVersion;
import java.util.List;
/**
* A class extending IChatBaseComponent that holds the original value
* to avoid deserializing it, which is an unnecessary operation that
* slows down the performance, as well as makes code more complicated
* and open to problems.
*/
public class WrappedChatComponent extends IChatBaseComponent {
/** Original NMS component */
private final Object originalComponent;
/**
* Constructs new instance with given parameter
*
* @param originalComponent
* Original NMS component
*/
public WrappedChatComponent(Object originalComponent) {
this.originalComponent = originalComponent;
}
/**
* Returns the original NMS component
* @return the original NMS component
*/
public Object get() {
return originalComponent;
}
@Override
public String toString() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public String toString(ProtocolVersion clientVersion) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public List<IChatBaseComponent> getExtra(){
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public String getText() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public ChatModifier getModifier() {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public void addExtra(IChatBaseComponent child) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
@Override
public void setModifier(ChatModifier modifier) {
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
}
}
@@ -1,209 +0,0 @@
package me.neznamy.tab.api.chat.rgb;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.TextColor;
import me.neznamy.tab.api.chat.rgb.format.*;
import me.neznamy.tab.api.chat.rgb.gradient.CMIGradient;
import me.neznamy.tab.api.chat.rgb.gradient.CommonGradient;
import me.neznamy.tab.api.chat.rgb.gradient.GradientPattern;
import me.neznamy.tab.api.chat.rgb.gradient.KyoriGradient;
import me.neznamy.tab.api.util.Preconditions;
/**
* A helper class to reformat all RGB formats into the default #RRGGBB and apply gradients
*/
public class RGBUtils {
/** Instance of the class */
private static final RGBUtils instance = new RGBUtils();
/** Registered RGB formatters */
private final RGBFormatter[] formats;
/** Registered gradient patterns */
private final GradientPattern[] gradients;
/** TAB's RGB pattern, used to convert text to bukkit format for boss bar */
private final Pattern tabPattern = Pattern.compile("#[0-9a-fA-F]{6}");
/** RGB pattern for legacy codes */
private final Pattern tabPatternLegacy = Pattern.compile("#[0-9a-fA-F]{6}\\|.");
/**
* Constructs new instance and loads all RGB patterns and gradients
*/
public RGBUtils() {
List<RGBFormatter> list = new ArrayList<>();
list.add(new BukkitFormat());
list.add(new CMIFormat());
list.add(new UnnamedFormat1());
list.add(new HtmlFormat());
list.add(new KyoriFormat());
try {
Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
list.add(new MiniMessageFormat());
} catch (ClassNotFoundException ignored) {}
formats = list.toArray(new RGBFormatter[0]);
gradients = new GradientPattern[] {
//{#RRGGBB>}text{#RRGGBB<}
new CMIGradient(),
//<#RRGGBB>Text</#RRGGBB>
new CommonGradient(Pattern.compile("<#[0-9a-fA-F]{6}>[^<]*</#[0-9a-fA-F]{6}>"),
Pattern.compile("<#[0-9a-fA-F]{6}\\|.>[^<]*</#[0-9a-fA-F]{6}>"),
"<#", 9, 2, 9, 7),
//<$#RRGGBB>Text<$#RRGGBB>
new CommonGradient(Pattern.compile("<\\$#[0-9a-fA-F]{6}>[^<]*<\\$#[0-9a-fA-F]{6}>"),
Pattern.compile("<\\$#[0-9a-fA-F]{6}\\|.>[^<]*<\\$#[0-9a-fA-F]{6}>"),
"<$", 10, 3, 10, 7),
new KyoriGradient()
};
}
/**
* Returns instance of this class
*
* @return instance
*/
public static RGBUtils getInstance() {
return instance;
}
/**
* Applies all RGB formats and gradients to text and returns it.
*
* @param text
* original text
* @return text where everything is converted to #RRGGBB
*/
public String applyFormats(String text) {
Preconditions.checkNotNull(text, "text");
String replaced = text;
for (GradientPattern pattern : gradients) {
replaced = pattern.applyPattern(replaced, false);
}
for (RGBFormatter formatter : formats) {
replaced = formatter.reformat(replaced);
}
return replaced;
}
/**
* Applies all gradient formats to text and returns it. This only affects
* usage where no placeholder is used inside.
*
* @param text
* original text
* @return text where all gradients with static text are converted to #RRGGBB
*/
public String applyCleanGradients(String text) {
Preconditions.checkNotNull(text, "text");
String replaced = text;
for (GradientPattern pattern : gradients) {
replaced = pattern.applyPattern(replaced, true);
}
return replaced;
}
/**
* Converts TAB's RGB format (#RRGGBB) into bukkit one
* (&amp;x&amp;r&amp;r&amp;g&amp;g&amp;b&amp;b) for modern
* clients (1.16+), for legacy clients it will use the closest color.
*
* @param text
* text to convert
* @param rgbClient
* whether client accepts RGB or not
* @return converted text
*/
public String convertToBukkitFormat(String text, boolean rgbClient) {
if (text == null) return null;
if (!text.contains("#")) return text; //no rgb codes
if (rgbClient) {
//converting random formats to TAB one
String replaced = applyFormats(text);
for (Pattern p : new Pattern[]{tabPatternLegacy, tabPattern}) {
Matcher m = p.matcher(replaced);
while (m.find()) {
String hexCode = m.group();
String fixed = "&x&" + hexCode.charAt(1) + "&" + hexCode.charAt(2) + "&" + hexCode.charAt(3) + "&" + hexCode.charAt(4) + "&" + hexCode.charAt(5) + "&" + hexCode.charAt(6);
replaced = replaced.replace(hexCode, EnumChatFormat.color(fixed));
}
}
return replaced;
} else {
return convertRGBtoLegacy(text);
}
}
/**
* Converts all hex codes in given string to legacy codes
*
* @param text
* text to convert
* @return translated text
*/
public String convertRGBtoLegacy(String text) {
if (text == null) return null;
if (!text.contains("#")) return EnumChatFormat.color(text);
String applied = applyFormats(text);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < applied.length(); i++){
char c = applied.charAt(i);
if (c == '#' && applied.length() > i+6) {
String hexCode = applied.substring(i+1, i+7);
if (isHexCode(hexCode)) {
if (containsLegacyCode(applied, i)) {
sb.append(new TextColor(hexCode, EnumChatFormat.getByChar(applied.charAt(i+8))).getLegacyColor().getFormat());
i += 8;
} else {
sb.append(new TextColor(hexCode).getLegacyColor().getFormat());
i += 6;
}
} else {
sb.append(c);
}
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* Returns true if entered string is a valid 6-digit combination of
* hexadecimal numbers, false if not
*
* @param string
* string to check
* @return {@code true} if valid, {@code false} if not
*/
public boolean isHexCode(String string) {
Preconditions.checkNotNull(string, "string");
if (string.length() != 6) return false;
for (int i=0; i<6; i++) {
char c = string.charAt(i);
if (c < 48 || (c > 57 && c < 65) || (c > 70 && c < 97) || c > 102) return false;
}
return true;
}
/**
* Returns true if text contains legacy color request at defined RGB index start
*
* @param text
* text to check
* @param i
* current index start
* @return {@code true} if legacy color is defined and valid, {@code false} otherwise
*/
private static boolean containsLegacyCode(String text, int i) {
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
return EnumChatFormat.getByChar(text.charAt(i+8)) != null;
}
}
@@ -1,22 +0,0 @@
package me.neznamy.tab.api.chat.rgb.format;
import me.neznamy.tab.api.chat.EnumChatFormat;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
/**
* Formatter for &lt;#RRGGBB&gt;
*/
public class MiniMessageFormat implements RGBFormatter {
@Override
public String reformat(String text) {
if (!text.contains("<")) return text;
String format = text.replace(EnumChatFormat.RESET.getFormat(), ""); //remove &r from header/footer newline inserts
try {
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(format));
} catch (Throwable ignored) {
return text;
}
}
}
@@ -1,12 +0,0 @@
package me.neznamy.tab.api.chat.rgb.format;
/**
* Formatter for &amp;#RRGGBB
*/
public class UnnamedFormat1 implements RGBFormatter {
@Override
public String reformat(String text) {
return text.contains("&#") ? text.replace("&#", "#") : text;
}
}
@@ -1,46 +0,0 @@
package me.neznamy.tab.api.chat.rgb.gradient;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.TextColor;
/**
* Gradient applier for &lt;gradient:#RRGGBB:#RRGGBB&gt;Text&lt;/gradient&gt;
* and &lt;gradient:#RRGGBB|L:#RRGGBB&gt;Text&lt;/gradient&gt;
*/
public class KyoriGradient implements GradientPattern {
private final Pattern pattern = Pattern.compile("<gradient:#[0-9a-fA-F]{6}:#[0-9a-fA-F]{6}>[^<]*</gradient>");
private final Pattern patternLegacy = Pattern.compile("<gradient:#[0-9a-fA-F]{6}\\|.:#[0-9a-fA-F]{6}>[^<]*</gradient>");
@Override
public String applyPattern(String text, boolean ignorePlaceholders) {
if (!text.contains("<grad")) return text;
String replaced = text;
Matcher m = patternLegacy.matcher(replaced);
while (m.find()) {
String format = m.group();
EnumChatFormat legacyColor = EnumChatFormat.getByChar(format.charAt(18));
if ((ignorePlaceholders && format.contains("%")) || legacyColor == null) continue;
TextColor start = new TextColor(format.substring(11, 17), legacyColor);
String message = format.substring(28, format.length()-11);
TextColor end = new TextColor(format.substring(21, 27));
String applied = asGradient(start, message, end);
replaced = replaced.replace(format, applied);
}
m = pattern.matcher(replaced);
while (m.find()) {
String format = m.group();
if (ignorePlaceholders && format.contains("%")) continue;
TextColor start = new TextColor(format.substring(11, 17));
String message = format.substring(26, format.length()-11);
TextColor end = new TextColor(format.substring(19, 25));
String applied = asGradient(start, message, end);
replaced = replaced.replace(format, applied);
}
return replaced;
}
}
@@ -1,16 +1,19 @@
package me.neznamy.tab.api.event;
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 {
/**
* Scans the given listener for any method that is annotated with
* {@link Subscribe} and registers it as a listener to this event bus.
*
* All methods in this class that are annotated with {@link Subscribe}
* <p>All methods in this class that are annotated with {@link Subscribe}
* must meet the following criteria:
* <ul>
* <li>It must be <strong>public</strong>.</li>
@@ -24,7 +27,7 @@ public interface EventBus {
* @param listener the listener to register
* @throws RuntimeException if a method does not meet the criteria
*/
void register(Object listener);
void register(@NonNull Object listener);
/**
* Registers the given handler to this event bus.
@@ -33,7 +36,7 @@ public interface EventBus {
* @param handler the handler to register
* @param <E> the type of the event
*/
<E extends TabEvent> void register(Class<E> type, EventHandler<E> handler);
<E extends TabEvent> void register(@NonNull Class<E> type, @NonNull EventHandler<E> handler);
/**
* Scans the given listener for any method that is annotated with
@@ -41,7 +44,7 @@ public interface EventBus {
*
* @param listener the listener to unregister
*/
void unregister(Object listener);
void unregister(@NonNull Object listener);
/**
* Unregisters the given handler from this event bus.
@@ -49,5 +52,5 @@ public interface EventBus {
* @param handler the handler to unregister
* @param <E> the type of the event
*/
<E extends TabEvent> void unregister(EventHandler<E> handler);
<E extends TabEvent> void unregister(@NonNull EventHandler<E> handler);
}
@@ -3,7 +3,7 @@ package me.neznamy.tab.api.event;
/**
* An event that may be fired by TAB when something occurs.
*
* This is a marker interface. All events <strong>must</strong> implement this
* <p>This is a marker interface. All events <strong>must</strong> implement this
* interface.
*/
public interface TabEvent {
@@ -2,6 +2,7 @@ package me.neznamy.tab.api.event.player;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.event.TabEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when the player has been fully loaded by TAB.
@@ -13,13 +14,13 @@ public interface PlayerLoadEvent extends TabEvent {
*
* @return the player that was loaded
*/
TabPlayer getPlayer();
@NotNull TabPlayer getPlayer();
/**
* Returns true if player was loaded because they joined,
* false if the player was loaded because tab reload command was executed
* Returns {@code true} if player was loaded because they joined,
* {@code false} if the player was loaded because tab reload command was executed
*
* @return true if player joined, false if plugin was reloaded
* @return {@code true} if player joined, {@code false} if plugin was reloaded
*/
boolean isJoin();
}
@@ -1,11 +1,48 @@
package me.neznamy.tab.api.event.plugin;
import lombok.NonNull;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.event.TabEvent;
import me.neznamy.tab.api.placeholder.Placeholder;
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 {
String getIdentifier();
/**
* Returns placeholder identifier starting and ending with {@code %}
*
* @return placeholder identifier starting and ending with {@code %}
*/
@NotNull String getIdentifier();
void setPlaceholder(Placeholder placeholder);
/**
* 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,222 @@
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.Nullable;
/**
* Interface for manipulating player name tags.
* <p>
* Instance can be obtained using {@link TabAPI#getNameTagManager()}.
* This requires the Team feature to be enabled in config, otherwise the method will
* return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface NameTagManager {
/**
* Makes player's NameTag globally invisible
*
* @param player
* player to hide name tag of
* @see #showNameTag(TabPlayer)
* @see #hasHiddenNameTag(TabPlayer)
*/
void hideNameTag(@NonNull TabPlayer player);
/**
* Hides player's NameTag for specified player until it's shown again
*
* @param player
* player to hide name tag of
* @param viewer
* player to hide name tag for
*/
void hideNameTag(@NonNull TabPlayer player, @NonNull TabPlayer viewer);
/**
* Makes player's NameTag visible again
*
* @param player
* player to show name tag of
* @see #hideNameTag(TabPlayer)
* @see #hasHiddenNameTag(TabPlayer)
*/
void showNameTag(@NonNull TabPlayer player);
/**
* Shows player's NameTag for specified viewer if it was hidden before
*
* @param player
* player to show name tag of
* @param viewer
* player to show NameTag back for
*/
void showNameTag(@NonNull TabPlayer player, @NonNull TabPlayer viewer);
/**
* Return whether player has hidden NameTag or not
*
* @param player
* player to check name tag visibility status of
* @return Whether player has hidden NameTag or not
* @see #hideNameTag(TabPlayer)
* @see #showNameTag(TabPlayer)
*/
boolean hasHiddenNameTag(@NonNull TabPlayer player);
/**
* Returns true if NameTag is hidden for specified viewer, false if not
*
* @param player
* player to check visibility status of
* @param viewer
* player to check visibility status for
* @return true if hidden, false if not
*/
boolean hasHiddenNameTag(@NonNull TabPlayer player, @NonNull TabPlayer viewer);
/**
* Unregisters player's team and no longer handles it, as well as disables anti-override for teams.
* This can be resumed using resumeTeamHandling(). If team handling was paused already, nothing happens.
*
* @param player
* player to pause team handling of
*/
void pauseTeamHandling(@NonNull TabPlayer player);
/**
* Resumes team handling if it was before paused using pauseTeamHandling(), if not, nothing happens
*
* @param player
* player to resume team handling of
*/
void resumeTeamHandling(@NonNull TabPlayer player);
/**
* Returns true if team handling is paused for this player using pauseTeamHandling(), false if not, or
* it was resumed already using resumeTeamHandling
*
* @param player
* player to check handling status of
* @return true if paused, false if not
*/
boolean hasTeamHandlingPaused(@NonNull TabPlayer player);
/**
* Forces collision rule for the player. Setting it to null will remove forced value
*
* @param player
* player to set collision rule of
* @param collision
* forced collision rule
*/
void setCollisionRule(@NonNull TabPlayer player, @Nullable Boolean collision);
/**
* Returns forced collision rule or null if collision is not forced using setCollisionRule
*
* @param player
* player to get forced collision of
* @return forced value or null if not forced
*/
@Nullable Boolean getCollisionRule(@NonNull TabPlayer player);
/**
* Changes player's prefix to provided value. Supports placeholders,
* as well as any supported RGB formats. Use {@code null} to reset
* value back to original.
*
* @param player
* player to change prefix of
* @param prefix
* new prefix value
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
void setPrefix(@NonNull TabPlayer player, @Nullable String prefix);
/**
* Changes player's suffix to provided value. Supports placeholders,
* as well as any supported RGB formats. Use {@code null} to reset
* value back to original.
*
* @param player
* player to change prefix of
* @param suffix
* new suffix value
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
void setSuffix(@NonNull TabPlayer player, @Nullable String suffix);
/**
* 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 #getOriginalPrefix(TabPlayer)
*/
@Nullable
String getCustomPrefix(@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 #getOriginalSuffix(TabPlayer)
*/
@Nullable
String getCustomSuffix(@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)
*/
@NonNull
String getOriginalPrefix(@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)
*/
@NonNull
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.
*
* @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
*/
void toggleNameTagVisibilityView(@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,
* {@code false} if not.
*
* @param player
* player to check
* @return {@code true} if hidden, {@code false} if not
*/
boolean hasHiddenNameTagVisibilityView(@NonNull TabPlayer player);
}
@@ -0,0 +1,129 @@
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.
*/
@SuppressWarnings("unused") // API 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();
}
@@ -1,14 +1,23 @@
package me.neznamy.tab.api.placeholder;
import org.jetbrains.annotations.NotNull;
/**
* An interface representing some kind of placeholder
*/
public interface Placeholder {
/**
* Returns refresh interval of this placeholder
*
* @return refresh interval of this placeholder
*/
int getRefresh();
String getIdentifier();
void enableTriggerMode(Runnable onActivation, Runnable onDisable);
void unload();
boolean isUsed();
/**
* Returns placeholder's identifier
*
* @return placeholder's identifier
*/
@NotNull String getIdentifier();
}
@@ -1,53 +1,93 @@
package me.neznamy.tab.api.placeholder;
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 java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
/**
* An interface allowing placeholder registration. Instance can be
* obtained using {@link TabAPI#getPlaceholderManager()}
*/
@SuppressWarnings("unused") // API class
public interface PlaceholderManager {
/**
* Registers a server placeholder (placeholder with same output for all players)
*/
ServerPlaceholder registerServerPlaceholder(String identifier, int refresh, Supplier<Object> supplier);
/**
* Registers a player placeholder (placeholder with player-specific output)
*/
PlayerPlaceholder registerPlayerPlaceholder(String identifier, int refresh, Function<TabPlayer, Object> function);
/**
* Registers a relational placeholder (different output for each player pair)
*/
RelationalPlaceholder registerRelationalPlaceholder(String identifier, int refresh, BiFunction<TabPlayer, TabPlayer, Object> function);
/**
* Detects placeholders in text using %% pattern and returns list of all detected identifiers
*
* @param text
* text to detect placeholders in
* @return list of detected identifiers
*/
List<String> detectPlaceholders(String text);
/**
* Adds placeholder to list of used placeholders and assigns this feature as using it,
* which will then receive refresh() if values changes
*
* @param identifier
* placeholder identifier
* @param feature
* feature using the placeholder
* Placeholder identifier
* @param refresh
* Refresh interval
* @param supplier
* Supplier for placeholder output
* @return Registered placeholder for further use
* @throws IllegalArgumentException
* If {@code identifier} does not start and end with {@code %} or
* {@code refresh} is not divisible by 50
*/
void addUsedPlaceholder(String identifier, TabFeature feature);
@NotNull ServerPlaceholder registerServerPlaceholder(@NonNull String identifier, int refresh, @NonNull Supplier<Object> supplier);
String findReplacement(String placeholder, String output);
/**
* Registers a player placeholder (placeholder different output per player)
*
* @param identifier
* Placeholder identifier
* @param refresh
* Refresh interval
* @param function
* Function for placeholder output
* @return Registered placeholder for further use
* @throws IllegalArgumentException
* 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);
List<String> getUsedPlaceholders();
/**
* Registers a relational placeholder (placeholder with output different for each player duo)
*
* @param identifier
* Placeholder identifier
* @param refresh
* Refresh interval
* @param function
* Function for placeholder output
* @return Registered placeholder for further use
* @throws IllegalArgumentException
* If {@code identifier} does not start and end with {@code %},
* 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);
Placeholder getPlaceholder(String identifier);
/**
* Returns placeholder from specified identifier. If it does not exist, it is registered
* as a PlaceholderAPI placeholder and returned.
*
* @param identifier
* Placeholder identifier
* @return Placeholder with specified identifier
*/
@NotNull Placeholder getPlaceholder(@NonNull String identifier);
/**
* Unregisters placeholder and makes plugin no longer refresh it.
*
* @param placeholder
* Placeholder to unregister
*/
void unregisterPlaceholder(@NonNull Placeholder placeholder);
/**
* Unregisters placeholder and makes plugin no longer refresh it.
*
* @param identifier
* identifier of placeholder to unregister
*/
void unregisterPlaceholder(@NonNull String identifier);
}
@@ -1,28 +1,31 @@
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)
*/
public interface PlayerPlaceholder extends Placeholder {
void updateValue(TabPlayer player, Object value);
/**
* Calls the placeholder request function and returns the output.
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
* file and "ERROR" is returned.
*
* @param p
* player to get placeholder value for
* @return value placeholder returned or "ERROR" if it threw an error
*/
Object request(TabPlayer p);
/**
* Returns last known value for given player.
* Force-updates placeholder value. It will still be overridden by periodic refresh later,
* so this is (only) useful for force an update when value is supposed to change, and you have
* a high refresh interval set.
*
* @param player
* player to get last value for
* @return last known value for entered player
* Player to update value for
* @param value
* New value
*/
String getLastValue(TabPlayer player);
void updateValue(@NonNull TabPlayer player, @Nullable Object value);
/**
* Force-updates placeholder value using previously provided update function.
*
* @param player
* Player to update value for
*/
void update(@NonNull TabPlayer player);
}
@@ -1,33 +1,36 @@
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 {
void updateValue(TabPlayer viewer, TabPlayer target, Object value);
/**
* Calls the placeholder request function and returns the output.
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
* file and "ERROR" is returned.
* Force-updates placeholder value. It will still be overridden by periodic refresh later,
* so this is (only) useful for force an update when value is supposed to change, and you have
* a high refresh interval set.
*
* @param viewer
* player looking at output of the placeholder
* Placeholder viewer
* @param target
* player the placeholder is displayed on
* @return value placeholder returned or "ERROR" if it threw an error
* Placeholder target
* @param value
* New value
*/
Object request(TabPlayer viewer, TabPlayer target);
void updateValue(@NonNull TabPlayer viewer, @NonNull TabPlayer target, @Nullable Object value);
/**
* Returns last known value for given players. First player is viewer,
* second player is target.
* Force-updates placeholder value using previously provided update function.
*
* @param viewer
* viewer of the placeholder
* Placeholder viewer
* @param target
* target who is the text displayed on
* @return last known value for entered player duo
* Placeholder target
*/
String getLastValue(TabPlayer viewer, TabPlayer target);
void update(@NonNull TabPlayer viewer, @NonNull TabPlayer target);
}
@@ -1,22 +1,24 @@
package me.neznamy.tab.api.placeholder;
import org.jetbrains.annotations.Nullable;
/**
* An interface for server placeholders (returning same value for all players)
*/
public interface ServerPlaceholder extends Placeholder {
void updateValue(Object value);
/**
* Force-updates placeholder value. It will still be overridden by periodic refresh later,
* so this is (only) useful for force an update when value is supposed to change, and you have
* a high refresh interval set.
*
* @param value
* New value
*/
void updateValue(@Nullable Object value);
/**
* Calls the placeholder request function and returns the output.
* If the placeholder threw an exception, it is logged in {@code placeholder-errors.log}
* file and "ERROR" is returned.
*
* @return value placeholder returned or "ERROR" if it threw an error
* Force-updates placeholder value using previously provided update function.
*/
Object request();
/**
* Returns last known value of the placeholder.
*
* @return last known value
*/
String getLastValue();
void update();
}
@@ -1,269 +0,0 @@
package me.neznamy.tab.api.protocol;
import java.util.HashMap;
import java.util.Map;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.api.util.BiFunctionWithException;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class for packet building, methods are overridden in
* platform-specific builders which extend this class to build actual packets.
* Packets which don't have a method override are forwarded into
* sendPacket method, where they are handled using platform's API instead.
*/
public class PacketBuilder {
/** Function map turning custom packet class into platform-specific packets */
protected final Map<Class<? extends TabPacket>, BiFunctionWithException<TabPacket, ProtocolVersion, Object>> buildMap = new HashMap<>();
/**
* Constructs new instance and fills {@link #buildMap} with abstract build methods
* of packets present on all platforms.
*/
public PacketBuilder() {
buildMap.put(PacketPlayOutBoss.class, (packet, version) -> build((PacketPlayOutBoss)packet, version));
buildMap.put(PacketPlayOutChat.class, (packet, version) -> build((PacketPlayOutChat)packet, version));
buildMap.put(PacketPlayOutPlayerInfo.class, (packet, version) -> build((PacketPlayOutPlayerInfo)packet, version));
buildMap.put(PacketPlayOutPlayerListHeaderFooter.class, (packet, version) -> build((PacketPlayOutPlayerListHeaderFooter)packet, version));
buildMap.put(PacketPlayOutScoreboardDisplayObjective.class, (packet, version) -> build((PacketPlayOutScoreboardDisplayObjective)packet, version));
buildMap.put(PacketPlayOutScoreboardObjective.class, (packet, version) -> build((PacketPlayOutScoreboardObjective)packet, version));
buildMap.put(PacketPlayOutScoreboardScore.class, (packet, version) -> build((PacketPlayOutScoreboardScore)packet, version));
buildMap.put(PacketPlayOutScoreboardTeam.class, (packet, version) -> build((PacketPlayOutScoreboardTeam)packet, version));
}
/**
* Converts custom packet into platform-specific packet by calling a function from
* {@link #buildMap}.
*
* @param packet
* Packet to build
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if reflection fails
*/
public Object build(TabPacket packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
Preconditions.checkNotNull(packet, "packet");
Preconditions.checkNotNull(clientVersion, "clientVersion");
return buildMap.get(packet.getClass()).apply(packet, clientVersion);
}
/**
* Constructs platform-specific PacketPlayOutBoss class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
}
/**
* Constructs platform-specific PacketPlayOutChat class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
}
/**
* Constructs platform-specific PacketPlayOutPlayerInfo class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
}
/**
* Constructs platform-specific PacketPlayOutPlayerListHeaderFooter class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutPlayerListHeaderFooter packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
}
/**
* Constructs platform-specific PacketPlayOutScoreboardDisplayObjective class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutScoreboardDisplayObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
}
/**
* Constructs platform-specific PacketPlayOutScoreboardObjective class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutScoreboardObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
}
/**
* Constructs platform-specific PacketPlayOutScoreboardScore class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
}
/**
* Constructs platform-specific PacketPlayOutScoreboardTeam class based on custom packet class
*
* @param packet
* Custom packet to be built
* @param clientVersion
* Protocol version of player to build the packet for
* @return Platform-specific packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return packet;
}
/**
* Cuts given string to specified character length (or length-1 if last character is a color character)
* and translates RGB to legacy colors. If string is not that long, the original string is returned.
* RGB codes are converted into legacy, since cutting is only needed for &lt;1.13.
* If {@code string} is {@code null}, empty string is returned.
*
* @param string
* String to cut
* @param length
* Length to cut to
* @return string cut to {@code length} characters
*/
public String cutTo(String string, int length) {
if (string == null) return "";
String legacyText = string;
if (string.contains("#")) {
//converting RGB to legacy colors
legacyText = RGBUtils.getInstance().convertRGBtoLegacy(string);
}
if (legacyText.length() <= length) return legacyText;
if (legacyText.charAt(length-1) == EnumChatFormat.COLOR_CHAR) {
return legacyText.substring(0, length-1); //cutting one extra character to prevent prefix ending with "&"
} else {
return legacyText.substring(0, length);
}
}
/**
* If {@code clientVersion} is &gt;= 1.13, creates a component from given text and returns
* it as a serialized component, which BungeeCord uses.
* <p>
* If {@code clientVersion} is &lt; 1.12, the text is cut to {@code length} characters if
* needed and returned.
*
* @param text
* Text to convert
* @param clientVersion
* Version of player to convert text for
* @return serialized component for 1.13+ clients, cut string for 1.12-
*/
public String jsonOrCut(String text, ProtocolVersion clientVersion, int length) {
if (text == null) return null;
if (clientVersion.getMinorVersion() >= 13) {
return IChatBaseComponent.optimizedComponent(text).toString(clientVersion);
} else {
return cutTo(text, length);
}
}
/**
* Converts platform-specific instance of player info packet into
* {@link PacketPlayOutPlayerInfo} object.
*
* @param packet
* platform-specific info packet
* @param clientVersion
* Version of client receiving the packet
* @return The packet converted into {@link PacketPlayOutPlayerInfo}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public PacketPlayOutPlayerInfo readPlayerInfo(Object packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return null;
}
/**
* Converts platform-specific instance of objective packet into
* {@link PacketPlayOutScoreboardObjective} object.
*
* @param packet
* platform-specific objective packet
* @return The packet converted into {@link PacketPlayOutScoreboardObjective}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public PacketPlayOutScoreboardObjective readObjective(Object packet) throws ReflectiveOperationException {
return null;
}
/**
* Converts platform-specific instance of display objective packet into
* {@link PacketPlayOutScoreboardDisplayObjective} object.
*
* @param packet
* platform-specific display objective packet
* @return The packet converted into {@link PacketPlayOutScoreboardDisplayObjective}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object packet) throws ReflectiveOperationException {
return null;
}
}
@@ -1,302 +0,0 @@
package me.neznamy.tab.api.protocol;
import java.util.UUID;
import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class representing platform specific packet class
*/
public class PacketPlayOutBoss implements TabPacket {
/** UUID of the BossBar */
private final UUID id;
/** Action of this packet */
private final Action action;
/** BossBar title */
private String name;
/** BossBar progress (0-1)*/
private float pct;
/** BossBar color */
private BarColor color;
/** BossBar style */
private BarStyle overlay;
/** Darken screen flag */
private boolean darkenScreen;
/** Play music flag */
private boolean playMusic;
/** Create fog flag */
private boolean createWorldFog;
/**
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.ADD action
*
* @param id
* BossBar uuid
* @param name
* BossBar title
* @param pct
* BossBar progress
* @param color
* BossBar color
* @param overlay
* BossBar style
*/
public PacketPlayOutBoss(UUID id, String name, float pct, BarColor color, BarStyle overlay) {
Preconditions.checkNotNull(id, "uuid");
Preconditions.checkNotNull(name, "title");
Preconditions.checkNotNull(color, "color");
Preconditions.checkNotNull(overlay, "style");
this.action = Action.ADD;
this.id = id;
this.name = name;
this.pct = pct;
this.color = color;
this.overlay = overlay;
}
/**
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.REMOVE action
*
* @param id
* BossBar uuid
*/
public PacketPlayOutBoss(UUID id) {
Preconditions.checkNotNull(id, "uuid");
this.action = Action.REMOVE;
this.id = id;
}
/**
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PCT action
*
* @param id
* BossBar uuid
* @param pct
* BossBar progress
*/
public PacketPlayOutBoss(UUID id, float pct) {
Preconditions.checkNotNull(id, "uuid");
this.action = Action.UPDATE_PCT;
this.id = id;
this.pct = pct;
}
/**
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_NAME action
*
* @param id
* BossBar uuid
* @param name
* BossBar title
*/
public PacketPlayOutBoss(UUID id, String name) {
Preconditions.checkNotNull(id, "uuid");
Preconditions.checkNotNull(name, "title");
this.action = Action.UPDATE_NAME;
this.id = id;
this.name = name;
}
/**
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_STYLE action
*
* @param id
* BossBar uuid
* @param color
* BossBar color
* @param overlay
* BossBar style
*/
public PacketPlayOutBoss(UUID id, BarColor color, BarStyle overlay) {
Preconditions.checkNotNull(id, "uuid");
Preconditions.checkNotNull(color, "color");
Preconditions.checkNotNull(overlay, "style");
this.action = Action.UPDATE_STYLE;
this.id = id;
this.color = color;
this.overlay = overlay;
}
/**
* Constructs new instance with given parameters and
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PROPERTIES action
*
* @param darkenScreen
* Darken screen flag
* @param playMusic
* Play music flag
* @param createWorldFog
* Create fog flag
*/
public PacketPlayOutBoss(UUID id, boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
Preconditions.checkNotNull(id, "uuid");
this.action = Action.UPDATE_PROPERTIES;
this.id = id;
this.darkenScreen = darkenScreen;
this.playMusic = playMusic;
this.createWorldFog = createWorldFog;
}
/**
* Returns bitmask based on {@link #darkenScreen}, {@link #playMusic} and {@link #darkenScreen} values.
* <p>
* {@link #darkenScreen} adds {@code 1}, {@link #playMusic} {@code 2} and {@link #darkenScreen} {@code 4}
* to the final value.
*
* @return the bitmask
*/
public byte getFlags(){
byte value = 0;
if (darkenScreen) value += 1;
if (playMusic) value += 2;
if (createWorldFog) value += 4;
return value;
}
@Override
public String toString() {
return String.format("PacketPlayOutBoss{id=%s,operation=%s,name=%s,pct=%s,color=%s,overlay=%s,darkenScreen=%s,playMusic=%s,createWorldFog=%s}",
id, action, name, pct, color, overlay, darkenScreen, playMusic, createWorldFog);
}
/**
* Returns {@link #color}
*
* @return color
*/
public BarColor getColor() {
return color;
}
/**
* Returns {@link #overlay}
*
* @return style
*/
public BarStyle getOverlay() {
return overlay;
}
/**
* Returns {@link #name}
*
* @return name
*/
public String getName() {
return name;
}
/**
* Returns {@link #id}
*
* @return id
*/
public UUID getId() {
return id;
}
/**
* Returns {@link #pct}
*
* @return progress
*/
public float getPct() {
return pct;
}
/**
* Returns {@link #action}
*
* @return packet action
*/
public Action getAction() {
return action;
}
/**
* Returns {@link #darkenScreen}
*
* @return darkenScreen
*/
public boolean isDarkenScreen() {
return darkenScreen;
}
/**
* Returns {@link #createWorldFog}
*
* @return createWorldFog
*/
public boolean isCreateWorldFog() {
return createWorldFog;
}
/**
* Returns {@link #playMusic}
*
* @return playMusic
*/
public boolean isPlayMusic() {
return playMusic;
}
/**
* Sets {@link #darkenScreen} to specified value
*
* @param darkenScreen
* Darken screen flag
*/
public void setDarkenScreen(boolean darkenScreen) {
this.darkenScreen = darkenScreen;
}
/**
* Sets {@link #createWorldFog} to specified value
*
* @param createWorldFog
* Create fog flag
*/
public void setCreateWorldFog(boolean createWorldFog) {
this.createWorldFog = createWorldFog;
}
/**
* Sets {@link #playMusic} to specified value
*
* @param playMusic
* Play music flag
*/
public void setPlayMusic(boolean playMusic) {
this.playMusic = playMusic;
}
/**
* An enum representing all valid boss packet actions.
* Calling ordinal() will return action's network ID.
*/
public enum Action {
ADD,
REMOVE,
UPDATE_PCT,
UPDATE_NAME,
UPDATE_STYLE,
UPDATE_PROPERTIES
}
}
@@ -1,65 +0,0 @@
package me.neznamy.tab.api.protocol;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class representing platform specific packet class
*/
public class PacketPlayOutChat implements TabPacket {
/** Message to be sent */
private final IChatBaseComponent message;
/** Message position */
private final ChatMessageType type;
/**
* Constructs new instance with given parameters
*
* @param message
* Chat message to be sent
* @param type
* Message position
*/
public PacketPlayOutChat(IChatBaseComponent message, ChatMessageType type) {
Preconditions.checkNotNull(message, "message");
Preconditions.checkNotNull(type, "message type");
this.message = message;
this.type = type;
}
@Override
public String toString() {
return String.format("PacketPlayOutChat{message=%s,type=%s}", message, type);
}
/**
* Returns {@link #message}
*
* @return message
*/
public IChatBaseComponent getMessage() {
return message;
}
/**
* Returns {@link #type}
*
* @return type
*/
public ChatMessageType getType() {
return type;
}
/**
* An enum representing positions of a chat message
* Calling ordinal() will return type's network ID.
*/
public enum ChatMessageType {
CHAT,
SYSTEM,
GAME_INFO
}
}
@@ -1,440 +0,0 @@
package me.neznamy.tab.api.protocol;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class representing platform specific packet class
*/
public class PacketPlayOutPlayerInfo implements TabPacket {
/** Packet actions */
private final EnumSet<EnumPlayerInfoAction> actions;
/** List of affected entries */
private final List<PlayerInfoData> entries;
/**
* Constructs new instance with given parameters.
*
* @param action
* Packet action
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, PlayerInfoData... entries) {
this(action, Arrays.asList(entries));
}
/**
* Constructs new instance with given parameters.
*
* @param action
* Packet action
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
Preconditions.checkNotNull(action, "action");
Preconditions.checkNotNull(entries, "entries");
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
actions = EnumSet.of(EnumPlayerInfoAction.ADD_PLAYER,
EnumPlayerInfoAction.INITIALIZE_CHAT,
EnumPlayerInfoAction.UPDATE_GAME_MODE,
EnumPlayerInfoAction.UPDATE_LISTED,
EnumPlayerInfoAction.UPDATE_LATENCY,
EnumPlayerInfoAction.UPDATE_DISPLAY_NAME);
} else {
this.actions = EnumSet.of(action);
}
this.entries = entries;
}
/**
* Constructs new instance with given parameters.
*
* @param actions
* Packet actions
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, PlayerInfoData... entries) {
Preconditions.checkNotNull(actions, "action");
Preconditions.checkNotNull(entries, "entries");
this.actions = actions;
this.entries = Arrays.asList(entries);
}
/**
* Constructs new instance with given parameters.
*
* @param actions
* Packet actions
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, List<PlayerInfoData> entries) {
Preconditions.checkNotNull(actions, "action");
Preconditions.checkNotNull(entries, "entries");
this.actions = actions;
this.entries = entries;
}
@Override
public String toString() {
return String.format("PacketPlayOutPlayerInfo{actions=%s,entries=%s}", actions, entries);
}
/**
* Returns {@link #actions}
*
* @return packet action
*/
public EnumSet<EnumPlayerInfoAction> getActions() {
return actions;
}
/**
* Returns {@link #entries}
*
* @return affected entries
*/
public List<PlayerInfoData> getEntries() {
return entries;
}
/**
* A subclass representing player list entry
*/
public static class PlayerInfoData {
/** Latency */
private int latency;
/** I have no idea what this is */
private boolean listed;
/** GameMode */
private EnumGamemode gameMode = EnumGamemode.SURVIVAL; //ProtocolLib causes NPE even when action does not use GameMode
/**
* Display name displayed in TabList. Using {@code null} results in no display name
* and scoreboard team prefix/suffix being visible in TabList instead.
*/
private IChatBaseComponent displayName;
/** Real name of affected player */
private String name;
/** Player UUID */
private UUID uniqueId;
/** Player's skin, null for empty skin */
private Skin skin;
/** Chat session ID */
private UUID chatSessionId;
/** Player's chat signing key */
private Object profilePublicKey;
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction#ADD_PLAYER} action
*
* @param name
* Player's name
* @param uniqueId
* Player's uuid
* @param skin
* Player's platform-specific skin object
* @param latency
* Player's ping
* @param gameMode
* Player's GameMode
* @param displayName
* Player's display name
* @param profilePublicKey
* Player's chat signing key
*/
public PlayerInfoData(String name, UUID uniqueId, Skin skin, boolean listed, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, UUID chatSessionId, Object profilePublicKey) {
Preconditions.checkNotNull(uniqueId, "uuid");
this.name = name;
this.uniqueId = uniqueId;
this.skin = skin;
this.listed = listed;
this.latency = latency;
this.gameMode = gameMode;
this.displayName = displayName;
this.chatSessionId = chatSessionId;
this.profilePublicKey = profilePublicKey;
}
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction#UPDATE_GAME_MODE} action
*
* @param uniqueId
* Player's uuid
* @param gameMode
* Player's GameMode
*/
public PlayerInfoData(UUID uniqueId, EnumGamemode gameMode) {
Preconditions.checkNotNull(uniqueId, "uuid");
this.uniqueId = uniqueId;
this.gameMode = gameMode;
}
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction#UPDATE_LATENCY} action
*
* @param uniqueId
* Player's uuid
* @param latency
* Player's ping
*/
public PlayerInfoData(UUID uniqueId, int latency) {
Preconditions.checkNotNull(uniqueId, "uuid");
this.uniqueId = uniqueId;
this.latency = latency;
}
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction#UPDATE_DISPLAY_NAME} action
*
* @param uniqueId
* Player's uuid
* @param displayName
* Player's display name
*/
public PlayerInfoData(UUID uniqueId, IChatBaseComponent displayName) {
Preconditions.checkNotNull(uniqueId, "uuid");
this.uniqueId = uniqueId;
this.displayName = displayName;
}
/**
* Constructs new instance with given parameter. Suitable for
* {@link EnumPlayerInfoAction#REMOVE_PLAYER} action
*
* @param uniqueId
* Player's uuid
*/
public PlayerInfoData(UUID uniqueId) {
this.uniqueId = uniqueId;
}
@Override
public String toString() {
return String.format("PlayerInfoData{latency=%s,gameMode=%s,displayName=%s,name=%s,uniqueId=%s,skin=%s,profilePublicKey=%s}",
latency, gameMode, displayName, name, uniqueId, skin, profilePublicKey);
}
/**
* Returns {@link #latency}
*
* @return latency
*/
public int getLatency() {
return latency;
}
/**
* Sets {@link #latency} to specified value
*
* @param latency
* Latency to use
*/
public void setLatency(int latency) {
this.latency = latency;
}
/**
* Returns {@link #listed}
*
* @return listed
*/
public boolean isListed() {
return listed;
}
/**
* Sets {@link #listed} to specified value
*
* @param listed
* Listed flag
*/
public void setListed(boolean listed) {
this.listed = listed;
}
/**
* Returns {@link #displayName}
*
* @return displayName
*/
public IChatBaseComponent getDisplayName() {
return displayName;
}
/**
* Sets {@link #displayName} to specified value
*
* @param displayName
* Display name to use
*/
public void setDisplayName(IChatBaseComponent displayName) {
this.displayName = displayName;
}
/**
* Returns {@link #uniqueId}
*
* @return uniqueId
*/
public UUID getUniqueId() {
return uniqueId;
}
/**
* Sets {@link #uniqueId} to specified value
*
* @param uniqueId
* UUID to use
*/
public void setUniqueId(UUID uniqueId) {
this.uniqueId = uniqueId;
}
/**
* Returns {@link #name}
*
* @return name
*/
public String getName() {
return name;
}
/**
* Sets {@link #name} to specified value
*
* @param name
* name to use
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns {@link #gameMode}
*
* @return gameMode
*/
public EnumGamemode getGameMode() {
return gameMode;
}
/**
* Sets {@link #gameMode} to specified value
*
* @param gameMode
* GameMode to use
*/
public void setGameMode(EnumGamemode gameMode) {
this.gameMode = gameMode;
}
/**
* Returns {@link #skin}
*
* @return skin
*/
public Skin getSkin() {
return skin;
}
/**
* Sets {@link #skin} to specified value
*
* @param skin
* Skin to use
*/
public void setSkin(Skin skin) {
this.skin = skin;
}
/**
* Returns {@link #chatSessionId}
*
* @return chatSessionId
*/
public UUID getChatSessionId() {
return chatSessionId;
}
/**
* Sets {@link #chatSessionId} to specified value
*
* @param chatSessionId
* chatSessionId to use
*/
public void setChatSessionId(UUID chatSessionId) {
this.chatSessionId = chatSessionId;
}
/**
* Returns {@link #profilePublicKey}
*
* @return profilePublicKey
*/
public Object getProfilePublicKey() {
return profilePublicKey;
}
/**
* Sets {@link #profilePublicKey} to specified value
*
* @param profilePublicKey
* profilePublicKey to use
*/
public void setProfilePublicKey(Object profilePublicKey) {
this.profilePublicKey = profilePublicKey;
}
}
/**
* En enum representing packet action
*/
public enum EnumPlayerInfoAction {
ADD_PLAYER,
INITIALIZE_CHAT,
UPDATE_GAME_MODE,
UPDATE_LISTED,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
REMOVE_PLAYER
}
/**
* An enum representing GameMode
*/
public enum EnumGamemode {
NOT_SET,
SURVIVAL,
CREATIVE,
ADVENTURE,
SPECTATOR;
/** Value array to iterate over to avoid array creations on each call */
public static final EnumGamemode[] VALUES = values();
}
}
@@ -1,70 +0,0 @@
package me.neznamy.tab.api.protocol;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class representing platform specific packet class
*/
public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
/** TabList header */
private final IChatBaseComponent header;
/** TabList footer */
private final IChatBaseComponent footer;
/**
* Constructs new instance with given parameters. They are converted to {@link IChatBaseComponent}
* using {@link IChatBaseComponent#optimizedComponent(String)} method.
*
* @param header
* TabList header
* @param footer
* TabList footer
*/
public PacketPlayOutPlayerListHeaderFooter(String header, String footer) {
Preconditions.checkNotNull(header, "header");
Preconditions.checkNotNull(footer, "footer");
this.header = IChatBaseComponent.optimizedComponent(header);
this.footer = IChatBaseComponent.optimizedComponent(footer);
}
/**
* Constructs new instance with given parameters.
*
* @param header
* TabList header
* @param footer
* TabList footer
*/
public PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent header, IChatBaseComponent footer) {
Preconditions.checkNotNull(header, "header");
Preconditions.checkNotNull(footer, "footer");
this.header = header;
this.footer = footer;
}
@Override
public String toString() {
return String.format("PacketPlayOutPlayerListHeaderFooter{header=%s,footer=%s}", header, footer);
}
/**
* Returns {@link #header}
*
* @return header
*/
public IChatBaseComponent getHeader() {
return header;
}
/**
* Returns {@link #footer}
*
* @return footer
*/
public IChatBaseComponent getFooter() {
return footer;
}
}
@@ -1,60 +0,0 @@
package me.neznamy.tab.api.protocol;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class representing platform specific packet class
*/
public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
/**
* Display slot.
* 0 = PlayerList,
* 1 = SideBar,
* 2 = BelowName.
*/
private final int slot;
/** Up to 16 characters long objective name */
private final String objectiveName;
/**
* Constructs new instance with given parameters
*
* @param slot
* Display slot
* @param objectiveName
* Internal name of the objective
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*/
public PacketPlayOutScoreboardDisplayObjective(int slot, String objectiveName) {
Preconditions.checkNotNull(objectiveName, "objective name");
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
this.slot = slot;
this.objectiveName = objectiveName;
}
@Override
public String toString() {
return String.format("PacketPlayOutScoreboardDisplayObjective{slot=%s,objectiveName=%s}", slot, objectiveName);
}
/**
* Returns {@link #slot}
*
* @return slot
*/
public int getSlot() {
return slot;
}
/**
* Returns {@link #objectiveName}
*
* @return objectiveName
*/
public String getObjectiveName() {
return objectiveName;
}
}
@@ -1,119 +0,0 @@
package me.neznamy.tab.api.protocol;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class representing platform specific packet class
*/
public class PacketPlayOutScoreboardObjective implements TabPacket {
/** Up to 16 characters long objective name */
private final String objectiveName;
/** Display name */
private final String displayName;
/** Display type, only takes effect in PlayerList */
private final EnumScoreboardHealthDisplay renderType;
/**
* Packet action.
* 0 = register,
* 1 = unregister,
* 2 = update title
*/
private final int action;
/**
* Constructs new instance with given parameters.
*
* @param action
* Packet action (0 = add, 1 = remove, 2 = update title)
* @param objectiveName
* objective name, up to 16 characters long
* @param displayName
* title
* @param renderType
* display type
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*/
public PacketPlayOutScoreboardObjective(int action, String objectiveName, String displayName, EnumScoreboardHealthDisplay renderType) {
Preconditions.checkRange(action, 0, 2, "action");
Preconditions.checkNotNull(objectiveName, "objective name");
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
Preconditions.checkNotNull(renderType, "render type");
this.objectiveName = objectiveName;
this.displayName = displayName;
this.renderType = renderType;
this.action = action;
}
/**
* Constructs new packet with given objective name and 1 (unregister) action.
*
* @param objectiveName
* objective name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*/
public PacketPlayOutScoreboardObjective(String objectiveName) {
Preconditions.checkNotNull(objectiveName, "objective name");
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
this.objectiveName = objectiveName;
this.displayName = ""; //avoiding NPE on <1.7
this.action = 1;
this.renderType = null;
}
@Override
public String toString() {
return String.format("PacketPlayOutScoreboardObjective{objectiveName=%s,displayName=%s,renderType=%s,method=%s}",
objectiveName, displayName, renderType, action);
}
/**
* Returns {@link #displayName}
*
* @return displayName
*/
public String getDisplayName() {
return displayName;
}
/**
* Returns {@link #objectiveName}
*
* @return objectiveName
*/
public String getObjectiveName() {
return objectiveName;
}
/**
* Returns {@link #renderType}
*
* @return renderType
*/
public EnumScoreboardHealthDisplay getRenderType() {
return renderType;
}
/**
* Returns {@link #action}
*
* @return packet action
*/
public int getAction() {
return action;
}
/**
* An enum representing available display types.
* They only take effect in PlayerList position.
*/
public enum EnumScoreboardHealthDisplay {
INTEGER, HEARTS
}
}
@@ -1,97 +0,0 @@
package me.neznamy.tab.api.protocol;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class representing platform specific packet class
*/
public class PacketPlayOutScoreboardScore implements TabPacket {
/** Packet action */
private final Action action;
/** Objective name */
private final String objectiveName;
/** Affected player */
private final String player;
/** Player's score */
private final int score;
/**
* Constructs new instance with given parameters
*
* @param action
* Packet action
* @param objectiveName
* Objective name
* @param player
* Affected player
* @param score
* Player's score
* @throws IllegalArgumentException
* if {@code objectiveName} is null or longer than 16 characters
*/
public PacketPlayOutScoreboardScore(Action action, String objectiveName, String player, int score) {
Preconditions.checkNotNull(action, "action");
Preconditions.checkNotNull(objectiveName, "objective name");
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
Preconditions.checkNotNull(player, "player");
this.action = action;
this.objectiveName = objectiveName;
this.player = player;
this.score = score;
}
@Override
public String toString() {
return String.format("PacketPlayOutScoreboardScore{action=%s,objectiveName=%s,player=%s,score=%s}",
action, objectiveName, player, score);
}
/**
* Returns {@link #action}
*
* @return packet action
*/
public Action getAction() {
return action;
}
/**
* Returns {@link #objectiveName}
*
* @return objective name
*/
public String getObjectiveName() {
return objectiveName;
}
/**
* Returns {@link #player}
*
* @return player
*/
public String getPlayer() {
return player;
}
/**
* Returns {@link #score}
*
* @return score
*/
public int getScore() {
return score;
}
/**
* An enum representing action types
*/
public enum Action {
CHANGE,
REMOVE
}
}
@@ -1,285 +0,0 @@
package me.neznamy.tab.api.protocol;
import java.util.Collection;
import java.util.Collections;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class representing platform specific packet class
*/
public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Up to 16 characters long team name.
* It is used to sort players alphabetically in TabList by team names.
* Team-less players appear on top, sorted by usernames.
* Players with teams are below them, players of the same team are
* sorted alphabetically by usernames.
*/
private final String name;
/** Team prefix, limited to 16 characters, on &lt;1.13 */
private String playerPrefix;
/** Team suffix, limited to 16 characters, on &lt;1.13 */
private String playerSuffix;
/**
* NameTag visibility rule since 1.8. Possible options are:
* {@code always}, {@code hideForOtherTeams}, {@code hideForOwnTeam}, {@code never}
*/
private String nameTagVisibility;
/**
* Collision rule added in 1.9. Possible options are:
* {@code always}, {@code pushOtherTeams}, {@code pushOwnTeam}, {@code never}
*/
private String collisionRule;
/**
* Team color field added in 1.13. It determines name color,
* prefix and suffix color start.
*/
private EnumChatFormat color;
/**
* Affected entities. For players, it is their name, for other entities their UUID
* Player name length is limited to 16 characters for &lt;1.8, 40 characters since 1.8.
* Entity UUID option was added in 1.8.
*/
private Collection<String> players = Collections.emptyList();
/**
* Packet action.
* 0 = create team,
* 1 = remove team,
* 2 = update team info,
* 3 = add entries,
* 4 = remove entries
*/
private final int action;
/**
* Bit mask.
* 0x01: Allow friendly fire,
* 0x02: Can see invisible players on the same team.
*/
private int options;
/**
* Constructs new instance with given parameters. Private constructor used
* internally to validate team name and set action.
*
* @param action
* Packet action (0 = create team, 1 = remove team, 2 = update team info,
* 3 = add entries, 4 = remove entries)
* @param name
* Team name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
private PacketPlayOutScoreboardTeam(int action, String name) {
Preconditions.checkNotNull(name, "team name");
Preconditions.checkMaxLength(name, 16, "team name");
this.action = action;
this.name = name;
}
/**
* Constructs new instance with given parameters and 0 (CREATE) action
*
* @param team
* Team name, up to 16 characters long
* @param prefix
* Prefix of players in team
* @param suffix
* Suffix of players in team
* @param visibility
* NameTag visibility rule
* @param collision
* Collision rule
* @param players
* Affected entities
* @param options
* bitmask of team options
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
public PacketPlayOutScoreboardTeam(String team, String prefix, String suffix, String visibility, String collision, Collection<String> players, int options) {
this(0, team);
Preconditions.checkNotNull(prefix, "prefix");
Preconditions.checkNotNull(suffix, "suffix");
Preconditions.checkNotNull(visibility, "visibility");
Preconditions.checkNotNull(collision, "collision");
Preconditions.checkNotNull(players, "players");
this.playerPrefix = prefix;
this.playerSuffix = suffix;
this.nameTagVisibility = visibility;
this.collisionRule = collision;
this.players = players;
this.options = options;
}
/**
* Constructs new instance with given parameters and 1 (REMOVE) action
*
* @param team
* Team name, up to 16 characters long
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
public PacketPlayOutScoreboardTeam(String team) {
this(1, team);
}
/**
* Constructs new instance with given parameters and 2 (UPDATE_TEAM_INFO) action
*
* @param team
* Team name, up to 16 characters long
* @param prefix
* Prefix of players in team
* @param suffix
* Suffix of players in team
* @param visibility
* NameTag visibility rule
* @param collision
* Collision rule
* @param options
* bitmask of team options
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
public PacketPlayOutScoreboardTeam(String team, String prefix, String suffix, String visibility, String collision, int options) {
this(2, team);
Preconditions.checkNotNull(prefix, "prefix");
Preconditions.checkNotNull(suffix, "suffix");
Preconditions.checkNotNull(visibility, "visibility");
Preconditions.checkNotNull(collision, "collision");
Preconditions.checkNotNull(players, "players");
this.playerPrefix = prefix;
this.playerSuffix = suffix;
this.nameTagVisibility = visibility;
this.collisionRule = collision;
this.options = options;
}
/**
* Constructs new instance with given parameters and 3 (ADD_PLAYERS) if {@code add} is {@code true},
* 4 (REMOVE_PLAYERS) if {@code add} is {@code false} action.
*
* @param team
* Team name, up to 16 characters long
* @param players
* Affected entities
* @param add
* {@code true} if players should be added, {@code false} if removed from team
* @throws IllegalArgumentException
* if {@code name} is null, empty or longer than 16 characters
*/
public PacketPlayOutScoreboardTeam(String team, Collection<String> players, boolean add) {
this(add ? 3 : 4, team);
Preconditions.checkNotNull(players, "players");
this.players = players;
}
@Override
public String toString() {
return String.format("PacketPlayOutScoreboardTeam{name=%s,playerPrefix=%s,playerSuffix=%s,nameTagVisibility=%s,"
+ "collisionRule=%s,color=%s,players=%s,method=%s,options=%s}",
name, playerPrefix, playerSuffix, nameTagVisibility, collisionRule, color, players, action, options);
}
/**
* Returns {@link #name}
*
* @return team name
*/
public String getName() {
return name;
}
/**
* Returns {@link #playerPrefix}
*
* @return playerPrefix
*/
public String getPlayerPrefix() {
return playerPrefix;
}
/**
* Returns {@link #playerSuffix}
*
* @return playerSuffix
*/
public String getPlayerSuffix() {
return playerSuffix;
}
/**
* Returns {@link #nameTagVisibility}
*
* @return nameTagVisibility
*/
public String getNameTagVisibility() {
return nameTagVisibility;
}
/**
* Returns {@link #collisionRule}
*
* @return collisionRule
*/
public String getCollisionRule() {
return collisionRule;
}
/**
* Sets {@link #color} to specified color
*
* @param color
* color to use
*/
public void setColor(EnumChatFormat color) {
this.color = color;
}
/**
* Returns {@link #color}
*
* @return color
*/
public EnumChatFormat getColor() {
return color;
}
/**
* Returns {@link #players}
*
* @return players
*/
public Collection<String> getPlayers() {
return players;
}
/**
* Returns {@link #options}
*
* @return team options
*/
public int getOptions() {
return options;
}
/**
* Returns {@link #action}
*
* @return packet action
*/
public int getAction() {
return action;
}
}
@@ -1,59 +0,0 @@
package me.neznamy.tab.api.protocol;
import me.neznamy.tab.api.util.Preconditions;
/**
* Class representing a minecraft skin as a value - signature pair.
*/
public class Skin {
/** Skin value */
private final String value;
/** Skin signature, can be null */
private final String signature;
/**
* Constructs new instance with given parameters
*
* @param value
* skin value
* @param signature
* skin signature
* @throws IllegalArgumentException
* if {@code value} is {@code null}
*/
public Skin(String value, String signature) {
Preconditions.checkNotNull(value, "value");
this.value = value;
this.signature = signature;
}
/**
* Returns skin value
*
* @return skin value
*/
public String getValue() {
return value;
}
/**
* Returns skin signature
*
* @return skin signature
*/
public String getSignature() {
return signature;
}
/**
* Returns user-friendly representation of this object
*
* @return user-friendly representation of this object
*/
@Override
public String toString() {
return String.format("Skin{value=%s,signature=%s}", value, signature);
}
}
@@ -1,17 +0,0 @@
package me.neznamy.tab.api.protocol;
/**
* Interface representing a packet that can be sent to client independently of platform.
* It is technically empty, existence is only present to restrict type access
* when sending packets to player, so {@link me.neznamy.tab.api.TabPlayer#sendCustomPacket(TabPacket)} cannot
* be called with an actual packet.
*/
public interface TabPacket {
/**
* Returns user-friendly string representation of the packet
*
* @return user-friendly string representation of the packet
*/
String toString();
}
@@ -1,5 +1,8 @@
package me.neznamy.tab.api.scoreboard;
import lombok.NonNull;
import org.jetbrains.annotations.NotNull;
/**
* An interface allowing to work with a line of
* text in scoreboard.
@@ -13,7 +16,7 @@ public interface Line {
*
* @return raw text of this line
*/
String getText();
@NotNull String getText();
/**
* Changes text to new value. Supports placeholders, which are
@@ -25,5 +28,5 @@ public interface Line {
* @param text
* Raw text to display in the line
*/
void setText(String text);
void setText(@NonNull String text);
}
@@ -1,5 +1,8 @@
package me.neznamy.tab.api.scoreboard;
import lombok.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
@@ -8,6 +11,7 @@ import java.util.List;
* {@link ScoreboardManager#createScoreboard(String, String, List)},
* for scoreboards from config {@link ScoreboardManager#getRegisteredScoreboards()}.
*/
@SuppressWarnings("unused") // API class
public interface Scoreboard {
/**
@@ -17,7 +21,7 @@ public interface Scoreboard {
*
* @return custom name of this scoreboard
*/
String getName();
@NotNull String getName();
/**
* Returns raw title of this scoreboard. Placeholders stay in their
@@ -26,7 +30,7 @@ public interface Scoreboard {
* @return scoreboard title
* @see #setTitle(String)
*/
String getTitle();
@NotNull String getTitle();
/**
* Sets title to specified value. Placeholders are refreshed automatically
@@ -43,7 +47,7 @@ public interface Scoreboard {
* New title to use with placeholder support
* @see #getTitle()
*/
void setTitle(String title);
void setTitle(@NonNull String title);
/**
* Returns list of lines in this scoreboard in the order they appear
@@ -54,7 +58,7 @@ public interface Scoreboard {
* @see #addLine(String)
* @see #removeLine(int)
*/
List<Line> getLines();
@NotNull List<Line> getLines();
/**
* Adds line with specified text on the bottom of scoreboard. Supports
@@ -65,7 +69,7 @@ public interface Scoreboard {
* @see #getLines()
* @see #removeLine(int)
*/
void addLine(String text);
void addLine(@NonNull String text);
/**
* Removes line with specified index. Index starts at {@code 0} and ends at
@@ -3,7 +3,10 @@ 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;
/**
* Interface allowing work with scoreboards, such as creating,
@@ -11,8 +14,9 @@ import me.neznamy.tab.api.TabPlayer;
* <p>
* Instance can be obtained using {@link me.neznamy.tab.api.TabAPI#getScoreboardManager()}.
* This requires the scoreboard feature to be enabled in config,
* the method will return null otherwise.
* the method will return {@code null} otherwise.
*/
@SuppressWarnings("unused") // API class
public interface ScoreboardManager {
/**
@@ -39,7 +43,7 @@ public interface ScoreboardManager {
* @throws NullPointerException
* if {@code name} or {@code lines} is {@code null}
*/
Scoreboard createScoreboard(String name, String title, List<String> lines);
@NotNull Scoreboard createScoreboard(@NonNull String name, @NonNull String title, @NonNull List<String> lines);
/**
* Returns map of registered scoreboards via config and API.
@@ -49,7 +53,7 @@ public interface ScoreboardManager {
*
* @return map of registered scoreboards
*/
Map<String, Scoreboard> getRegisteredScoreboards();
@NotNull Map<String, Scoreboard> getRegisteredScoreboards();
/**
* Displays scoreboard to defined player. This will disable all display
@@ -66,7 +70,7 @@ public interface ScoreboardManager {
* @see #hasCustomScoreboard(TabPlayer)
* @see #resetScoreboard(TabPlayer)
*/
void showScoreboard(TabPlayer player, Scoreboard scoreboard);
void showScoreboard(@NonNull TabPlayer player, @NonNull Scoreboard scoreboard);
/**
* Returns {@code true} if player has custom scoreboard set using
@@ -79,7 +83,7 @@ public interface ScoreboardManager {
* @see #showScoreboard(TabPlayer, Scoreboard)
* @see #resetScoreboard(TabPlayer)
*/
boolean hasCustomScoreboard(TabPlayer player);
boolean hasCustomScoreboard(@NonNull TabPlayer player);
/**
* Hides custom scoreboard sent using {@link #showScoreboard(TabPlayer, Scoreboard)}
@@ -91,7 +95,7 @@ public interface ScoreboardManager {
* @see #showScoreboard(TabPlayer, Scoreboard)
* @see #hasCustomScoreboard(TabPlayer)
*/
void resetScoreboard(TabPlayer player);
void resetScoreboard(@NonNull TabPlayer player);
/**
* Returns {@code true} if player has scoreboard enabled, {@code false}
@@ -105,7 +109,7 @@ public interface ScoreboardManager {
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
* @see #toggleScoreboard(TabPlayer, boolean)
*/
boolean hasScoreboardVisible(TabPlayer player);
boolean hasScoreboardVisible(@NonNull TabPlayer player);
/**
* Sets scoreboard visibility of player to defined value. If visibility status
@@ -122,7 +126,7 @@ public interface ScoreboardManager {
* @see #toggleScoreboard(TabPlayer, boolean)
* @see #hasScoreboardVisible(TabPlayer)
*/
void setScoreboardVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
void setScoreboardVisible(@NonNull TabPlayer player, boolean visible, boolean sendToggleMessage);
/**
* Toggles scoreboard for specified player. If player had scoreboard visible,
@@ -136,7 +140,7 @@ public interface ScoreboardManager {
* @see #hasScoreboardVisible(TabPlayer)
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
*/
void toggleScoreboard(TabPlayer player, boolean sendToggleMessage);
void toggleScoreboard(@NonNull TabPlayer player, boolean sendToggleMessage);
/**
* Temporarily displays scoreboard to all players for specified amount of
@@ -151,7 +155,7 @@ public interface ScoreboardManager {
* if no scoreboard was found with such name or {@code duration}
* is &lt; 0.
*/
void announceScoreboard(String scoreboard, int duration);
void announceScoreboard(@NonNull String scoreboard, int duration);
/**
* Returns player's currently displayed scoreboard. This can be either with
@@ -162,5 +166,5 @@ public interface ScoreboardManager {
* player to get active scoreboard of
* @return player's active scoreboard or {@code null} if player has no scoreboard
*/
Scoreboard getActiveScoreboard(TabPlayer player);
@Nullable Scoreboard getActiveScoreboard(@NonNull TabPlayer player);
}
@@ -1,4 +1,9 @@
package me.neznamy.tab.api;
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.Nullable;
/**
* Interface allowing to send header/footer to players.
@@ -7,39 +12,40 @@ package me.neznamy.tab.api;
* 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 {
/**
* Sets raw value of header to specified value. This overrides plugin
* configuration until reset is called. Has full placeholder support
* and will send update packet whenever a placeholder used inside
* changes its value.
* changes its value. Use {@code null} to reset value back to original.
*
* @param player
* Player to set header for
* @param header
* Raw header value
*/
void setHeader(TabPlayer player, String header);
void setHeader(@NonNull TabPlayer player, @Nullable String header);
/**
* Sets raw value of footer to specified value. This overrides plugin
* configuration until reset is called. Has full placeholder support
* and will send update packet whenever a placeholder used inside
* changes its value.
* changes its value. Use {@code null} to reset value back to original.
*
* @param player
* Player to set header for
* @param footer
* Raw footer value
*/
void setFooter(TabPlayer player, String footer);
void setFooter(@NonNull TabPlayer player, @Nullable String footer);
/**
* Sets raw value of header and footer to specified values. This overrides plugin
* configuration until reset is called. Has full placeholder support
* and will send update packet whenever a placeholder used inside
* changes its value.
* changes its value. Use {@code null} to reset value back to original.
*
* @param player
* Player to set header for
@@ -48,35 +54,5 @@ public interface HeaderFooterManager {
* @param footer
* Raw footer value
*/
void setHeaderAndFooter(TabPlayer player, String header, String footer);
/**
* Resets header value previously set using {@link #setHeader(TabPlayer, String)}
* or {@link #setHeaderAndFooter(TabPlayer, String, String)} and restores internal
* logic using configuration files.
*
* @param player
* Player to reset header for
*/
void resetHeader(TabPlayer player);
/**
* Resets footer value previously set using {@link #setFooter(TabPlayer, String)}
* or {@link #setHeaderAndFooter(TabPlayer, String, String)} and restores internal
* logic using configuration files.
*
* @param player
* Player to reset header for
*/
void resetFooter(TabPlayer player);
/**
* Resets values previously set using {@link #setHeader(TabPlayer, String)},
* {@link #setFooter(TabPlayer, String)} or {@link #setHeaderAndFooter(TabPlayer, String, String)}
* and restores internal logic using configuration files.
*
* @param player
* Player to reset header and footer for
*/
void resetHeaderAndFooter(TabPlayer player);
void setHeaderAndFooter(@NonNull TabPlayer player, @Nullable String header, @Nullable String footer);
}
@@ -0,0 +1,46 @@
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 {
/**
* Forces new team name for the player until this method is called again with null argument and
* performs all actions to change player's team name
*
* @param player
* player to set team name of
* @param teamName
* forced team name
*/
void forceTeamName(@NonNull TabPlayer player, @Nullable String teamName);
/**
* Returns forced team name of player or null if not forced
*
* @param player
* player to check forced team name of
* @return forced team name of player or null if not forced
*/
@Nullable String getForcedTeamName(@NonNull TabPlayer player);
/**
* Returns original team name of the player set by the plugin.
*
* @param player
* Player to get team name of
* @return Player's team name based on configuration.
*/
@NotNull String getOriginalTeamName(@NonNull TabPlayer player);
}
@@ -1,92 +1,63 @@
package me.neznamy.tab.api;
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;
/**
* An interface allowing to modify tablist display names
* An interface allowing to modify TabList display names
* of players.
* <p>
* Instance can be obtained using {@link TabAPI#getTablistFormatManager()}.
* Instance can be obtained using {@link TabAPI#getTabListFormatManager()}.
* This requires the feature to be enabled in config, otherwise the method will
* return {@code null}.
*/
public interface TablistFormatManager {
@SuppressWarnings("unused") // API class
public interface TabListFormatManager {
/**
* Changes player's prefix to provided value. Supports placeholders,
* as well as any supported RGB formats.
* as well as any supported RGB formats. Use {@code null} to reset
* value back to original.
*
* @param player
* player to change prefix of
* @param prefix
* new prefix value
* @see #resetPrefix(TabPlayer)
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
void setPrefix(TabPlayer player, String prefix);
void setPrefix(@NonNull TabPlayer player, @Nullable String prefix);
/**
* Changes player's name to provided value. Supports placeholders,
* as well as any supported RGB formats.
* as well as any supported RGB formats. Use {@code null} to reset
* value back to original.
*
* @param player
* player to change prefix of
* @param customName
* new customName value
* @see #resetName(TabPlayer)
* @see #getCustomName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
void setName(TabPlayer player, String customName);
void setName(@NonNull TabPlayer player, @Nullable String customName);
/**
* Changes player's suffix to provided value. Supports placeholders,
* as well as any supported RGB formats.
* as well as any supported RGB formats. Use {@code null} to reset
* value back to original.
*
* @param player
* player to change prefix of
* @param suffix
* new suffix value
* @see #resetSuffix(TabPlayer)
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
void setSuffix(TabPlayer player, String suffix);
/**
* Resets player's prefix previously assigned using {@link #setPrefix(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset prefix of
* @see #setPrefix(TabPlayer, String)
* @see #getCustomPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
void resetPrefix(TabPlayer player);
/**
* Resets player's custom name previously assigned using {@link #setName(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset name of
* @see #setName(TabPlayer, String)
* @see #getCustomName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
void resetName(TabPlayer player);
/**
* Resets player's suffix previously assigned using {@link #setSuffix(TabPlayer, String)}
* method. If nothing was set, this method will not do anything.
*
* @param player
* player to reset suffix of
* @see #setSuffix(TabPlayer, String)
* @see #getCustomSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
void resetSuffix(TabPlayer player);
void setSuffix(@NonNull TabPlayer player, @Nullable String suffix);
/**
* Returns prefix assigned using {@link #setPrefix(TabPlayer, String)} method.
@@ -96,10 +67,9 @@ 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 #resetPrefix(TabPlayer)
* @see #getOriginalPrefix(TabPlayer)
*/
String getCustomPrefix(TabPlayer player);
@Nullable String getCustomPrefix(@NonNull TabPlayer player);
/**
* Returns custom name assigned using {@link #setName(TabPlayer, String)} method.
@@ -109,10 +79,9 @@ public interface TablistFormatManager {
* player to get custom name of
* @return player's custom name or {@code null} if not defined
* @see #setName(TabPlayer, String)
* @see #resetName(TabPlayer)
* @see #getOriginalName(TabPlayer)
*/
String getCustomName(TabPlayer player);
@Nullable String getCustomName(@NonNull TabPlayer player);
/**
* Returns suffix assigned using {@link #setSuffix(TabPlayer, String)} method.
@@ -122,10 +91,9 @@ public interface TablistFormatManager {
* player to get custom suffix of
* @return player's custom suffix or {@code null} if not defined
* @see #setSuffix(TabPlayer, String)
* @see #resetSuffix(TabPlayer)
* @see #getOriginalSuffix(TabPlayer)
*/
String getCustomSuffix(TabPlayer player);
@Nullable String getCustomSuffix(@NonNull TabPlayer player);
/**
* Returns player's original prefix applied using plugin's internal logic.
@@ -135,10 +103,9 @@ public interface TablistFormatManager {
* player to get original prefix of
* @return player's original prefix
* @see #setPrefix(TabPlayer, String)
* @see #resetPrefix(TabPlayer)
* @see #getCustomPrefix(TabPlayer)
*/
String getOriginalPrefix(TabPlayer player);
@NotNull String getOriginalPrefix(@NonNull TabPlayer player);
/**
* Returns player's original name applied using plugin's internal logic.
@@ -148,10 +115,9 @@ public interface TablistFormatManager {
* player to get original name of
* @return player's original name
* @see #setName(TabPlayer, String)
* @see #resetName(TabPlayer)
* @see #getCustomName(TabPlayer)
*/
String getOriginalName(TabPlayer player);
@NotNull String getOriginalName(@NonNull TabPlayer player);
/**
* Returns player's original suffix applied using plugin's internal logic.
@@ -161,8 +127,7 @@ public interface TablistFormatManager {
* player to get original suffix of
* @return player's original suffix
* @see #setSuffix(TabPlayer, String)
* @see #resetSuffix(TabPlayer)
* @see #getCustomSuffix(TabPlayer)
*/
String getOriginalSuffix(TabPlayer player);
@NotNull String getOriginalSuffix(@NonNull TabPlayer player);
}
@@ -0,0 +1,79 @@
package me.neznamy.tab.api.tablist.layout;
import lombok.NonNull;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface representing a layout.
*/
@SuppressWarnings("unused") // API class
public interface Layout {
/**
* Returns name of the layout.
*
* @return name of the layout
*/
@NotNull String getName();
/**
* Adds fixed slot with specified parameters
*
* @param slot
* Slot (1-80)
* @param text
* Text to display
*/
void addFixedSlot(int slot, @NonNull String text);
/**
* Adds fixed slot with specified parameters
*
* @param slot
* Slot (1-80)
* @param text
* Text to display
* @param skin
* Skin definition like in config
*/
void addFixedSlot(int slot, @NonNull String text, @NonNull String skin);
/**
* Adds fixed slot with specified parameters
*
* @param slot
* Slot (1-80)
* @param text
* Text to display
* @param ping
* Slot's ping
*/
void addFixedSlot(int slot, @NonNull String text, 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
*/
void addFixedSlot(int slot, @NonNull String text, @NonNull String skin, int ping);
/**
* Adds a player group.
*
* @param condition
* Condition that must be met for player to appear in the group.
* May be null for no requirement. Syntax is like in config -
* either condition name or short format.
* @param slots
* Slots for this player group
*/
void addGroup(@Nullable String condition, int[] slots);
}
@@ -0,0 +1,45 @@
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.Nullable;
/**
* Interface for working with layouts. Instance can be obtained using
* {@link TabAPI#getLayoutManager()}. If the feature is disabled in
* config, it will return {@code null}.
*/
@SuppressWarnings("unused") // API class
public interface LayoutManager {
/**
* Creates new layout
*
* @param name
* Unique layout name
* @return Created layout
*/
Layout createNewLayout(String name);
/**
* Sends layout to player. Set to {@code null} to make player not see
* any layout, but default TabList instead.
*
* @param player
* Player to send layout to
* @param layout
* Layout to send
* @see #resetLayout(TabPlayer)
*/
void sendLayout(@NonNull TabPlayer player, @Nullable Layout layout);
/**
* Reset layout back to original one based on configuration.
*
* @param player
* Player to reset layout for
* @see #sendLayout(TabPlayer, Layout)
*/
void resetLayout(@NonNull TabPlayer player);
}
@@ -1,96 +0,0 @@
package me.neznamy.tab.api.task;
import java.util.concurrent.Future;
import me.neznamy.tab.api.TabFeature;
/**
* Manager for scheduling and executing tasks in TAB's
* main thread to avoid concurrency issues.
*/
public interface ThreadManager {
/**
* Starts a task in new thread and measures how long it took to process
*
* @param feature
* feature to add cpu usage to
* @param type
* usage type to add cpu usage to
* @param task
* the task
*/
Future<Void> runMeasuredTask(TabFeature feature, String type, Runnable task);
/**
* Starts a task in new thread and measures how long it took to process
*
* @param feature
* feature to add cpu usage to
* @param type
* usage type to add cpu usage to
* @param task
* the task
*/
Future<Void> runMeasuredTask(String feature, String type, Runnable task);
/**
* Runs task in a new thread
*
* @param task
* the task
*/
Future<Void> runTask(Runnable task);
/**
* Starts a new task with defined repeat interval that measures cpu usage
*
* @param intervalMilliseconds
* task interval
* @param feature
* feature to add cpu usage to
* @param type
* usage type to add cpu usage to
* @param task
* the task
* @return Future representing the submitted task
*/
Future<?> startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task);
/**
* Starts a new task with defined repeat interval
*
* @param intervalMilliseconds
* task interval
* @param task
* the task
* @return Future representing the submitted task
*/
Future<?> startRepeatingTask(int intervalMilliseconds, Runnable task);
/**
* Runs task with a delay and measures how long it took to process
*
* @param delayMilliseconds
* how long to run the task after
* @param feature
* feature to add cpu usage to
* @param type
* usage type to add cpu usage to
* @param task
* the task
* @return Future representing the submitted task
*/
Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task);
/**
* Runs task with a delay
*
* @param delayMilliseconds
* how long to run the task after
* @param task
* the task
* @return Future representing the submitted task
*/
Future<?> runTaskLater(int delayMilliseconds, Runnable task);
}
@@ -1,182 +0,0 @@
package me.neznamy.tab.api.team;
import me.neznamy.tab.api.TabPlayer;
public interface TeamManager {
/**
* Makes player's NameTag globally invisible
*
* @param player
* player to hide nametag of
* @see #showNametag(TabPlayer)
* @see #hasHiddenNametag(TabPlayer)
*/
void hideNametag(TabPlayer player);
/**
* Hides player's NameTag for specified player until it's shown again
*
* @param player
* player to hide nametag of
* @param viewer
* player to hide NameTag for
*/
void hideNametag(TabPlayer player, TabPlayer viewer);
/**
* Makes player's NameTag visible again
*
* @param player
* player to show nametag of
* @see #hideNametag(TabPlayer)
* @see #hasHiddenNametag(TabPlayer)
*/
void showNametag(TabPlayer player);
/**
* Shows player's NameTag for specified viewer if it was hidden before
*
* @param player
* player to show nametag of
* @param viewer
* player to show NameTag back for
*/
void showNametag(TabPlayer player, TabPlayer viewer);
/**
* Return whether player has hidden NameTag or not
*
* @param player
* player to check nametag visibility status of
* @return Whether player has hidden NameTag or not
* @see #hideNametag(TabPlayer)
* @see #showNametag(TabPlayer)
*/
boolean hasHiddenNametag(TabPlayer player);
/**
* Returns true if NameTag is hidden for specified viewer, false if not
*
* @param player
* player to check visibility status of
* @param viewer
* player to check visibility status for
* @return true if hidden, false if not
*/
boolean hasHiddenNametag(TabPlayer player, TabPlayer viewer);
/**
* Unregisters player's team and no longer handles it, as well as disables anti-override for teams.
* This can be resumed using resumeTeamHandling(). If team handling was paused already, nothing happens.
*
* @param player
* player to pause team handling of
*/
void pauseTeamHandling(TabPlayer player);
/**
* Resumes team handling if it was before paused using pauseTeamHandling(), if not, nothing happens
*
* @param player
* player to resume team handling of
*/
void resumeTeamHandling(TabPlayer player);
/**
* Returns true if team handling is paused for this player using pauseTeamHandling(), false if not, or
* it was resumed already using resumeTeamHandling
*
* @param player
* player to check handling status of
* @return true if paused, false if not
*/
boolean hasTeamHandlingPaused(TabPlayer player);
/**
* Forces new team name for the player until this method is called again with null argument and
* performs all actions to change player's team name
*
* @param player
* player to set team name of
* @param teamName
* forced team name
*/
void forceTeamName(TabPlayer player, String teamName);
/**
* Returns forced team name of player or null if not forced
*
* @param player
* player to check forced team name of
* @return forced team name of player or null if not forced
*/
String getForcedTeamName(TabPlayer player);
/**
* Forces collision rule for the player. Setting it to null will remove forced value
*
* @param player
* player to set collision rule of
* @param collision
* forced collision rule
*/
void setCollisionRule(TabPlayer player, Boolean collision);
/**
* Returns forced collision rule or null if collision is not forced using setCollisionRule
*
* @param player
* player to get forced collision of
* @return forced value or null if not forced
*/
Boolean getCollisionRule(TabPlayer player);
/**
* Sends update team's properties packet of player's team to everyone
*
* @param player
* player to update team data of
*/
void updateTeamData(TabPlayer player);
void setPrefix(TabPlayer player, String prefix);
void setSuffix(TabPlayer player, String suffix);
void resetPrefix(TabPlayer player);
void resetSuffix(TabPlayer player);
String getCustomPrefix(TabPlayer player);
String getCustomSuffix(TabPlayer player);
String getOriginalPrefix(TabPlayer player);
String getOriginalSuffix(TabPlayer player);
/**
* Toggles nametag visibility view on all players for specified player.
* On first call, nametags of all players will become invisible for specified
* player. On second call, they will become visible again.
*
* @param player
* player to toggle nametag visibility view for
* @param sendToggleMessage
* {@code true} if configured toggle message should be sent, {@code false} if not
*/
void toggleNameTagVisibilityView(TabPlayer player, boolean sendToggleMessage);
/**
* Returns {@code true} if player has hidden nametags by either calling
* {@link #toggleNameTagVisibilityView(TabPlayer, boolean)} or using a command,
* {@code false} if not.
*
* @param player
* player to check
* @return {@code true} if hidden, {@code false} if not
*/
boolean hasHiddenNameTagVisibilityView(TabPlayer player);
}
@@ -1,32 +0,0 @@
package me.neznamy.tab.api.team;
import java.util.List;
import me.neznamy.tab.api.TabPlayer;
public interface UnlimitedNametagManager extends TeamManager {
void disableArmorStands(TabPlayer player);
void enableArmorStands(TabPlayer player);
boolean hasDisabledArmorStands(TabPlayer player);
void setName(TabPlayer player, String customName);
void setLine(TabPlayer player, String line, String value);
void resetName(TabPlayer player);
void resetLine(TabPlayer player, String line);
String getCustomName(TabPlayer player);
String getCustomLineValue(TabPlayer player, String line);
String getOriginalName(TabPlayer player);
String getOriginalLineValue(TabPlayer player, String line);
List<String> getDefinedLines();
}
@@ -1,78 +0,0 @@
package me.neznamy.tab.api.util;
import me.neznamy.tab.api.TabPlayer;
/**
* Class with utility methods used in the plugin
*/
public class Preconditions {
/**
* Private constructor preventing the class from being instantiated
*/
private Preconditions() {}
/**
* Checks object and throws {@code IllegalArgumentException} if it is {@code null}.
*
* @param obj
* Object to check
* @param name
* Name of the variable to use in exception message
* @throws IllegalArgumentException
* if {@code obj} is {@code null}
*/
public static void checkNotNull(Object obj, String name) {
if (obj == null) throw new IllegalArgumentException(name + " cannot be null");
}
/**
* Checks length of String and throws {@code IllegalArgumentException} if it is
* longer than limit.
*
* @param string
* String to check
* @param maxLength
* Maximum length of the string
* @param name
* Name of the variable to use in exception message
* @throws IllegalArgumentException
* if {@code string} is {@code null} or longer than {@code maxLength} characters
*/
public static void checkMaxLength(String string, int maxLength, String name) {
checkNotNull(string, name);
if (string.length() > maxLength) throw new IllegalArgumentException(name + " is longer than " + maxLength + " characters (" + string.length() + ")");
}
/**
* Checks number range and throws {@code IllegalArgumentException} if number
* is out of range.
*
* @param number
* Number to check
* @param min
* Minimum allowed value
* @param max
* Maximum allowed value
* @param variable
* Variable name to use in exception message
* @throws IllegalArgumentException
* if {@code number} is out of range ({@code number < min || number > max})
*/
public static void checkRange(Number number, Number min, Number max, String variable) {
if (number.doubleValue() < min.doubleValue() || number.doubleValue() > max.doubleValue())
throw new IllegalArgumentException(variable + " index out of range (" + min + " - " + max + "), was " + number);
}
/**
* Checks if player is loaded and throws {@code IllegalStateException} if not.
*
* @param player
* Player to check
* @throws IllegalStateException
* If player is not loaded
*/
public static void checkLoaded(TabPlayer player) {
if (!player.isLoaded()) throw new IllegalStateException("Player is not loaded yet. Try again later.");
}
}
+3 -11
View File
@@ -3,15 +3,7 @@ plugins {
}
dependencies {
implementation(libs.plugin.indra)
implementation(libs.plugin.shadow)
}
dependencies {
compileOnly(files(libs::class.java.protectionDomain.codeSource.location))
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
implementation("com.github.johnrengelman:shadow:8.1.1")
implementation("io.freefair.gradle:lombok-plugin:8.6")
implementation("fabric-loom:fabric-loom.gradle.plugin:1.6-SNAPSHOT")
}
+1 -8
View File
@@ -2,15 +2,8 @@ rootProject.name = "build-logic"
dependencyResolutionManagement {
repositories {
maven("https://maven.fabricmc.net/")
gradlePluginPortal()
mavenCentral()
}
// You're a great IDE IntelliJ, but sometimes you really annoy me. >:(
// repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
versionCatalogs {
register("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
@@ -1,24 +1,19 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id("net.kyori.indra")
}
indra {
javaVersions {
target(8)
}
github("NEZNAMY", "TAB") {
ci(true)
}
`java-library`
id("io.freefair.lombok")
}
tasks {
processResources {
filter<ReplaceTokens>("tokens" to mapOf(
"id" to rootProject.ext.get("id")!!.toString(),
"name" to rootProject.name,
"version" to project.version,
"description" to project.description
"description" to project.description,
"website" to rootProject.ext.get("website")!!.toString(),
"author" to rootProject.ext.get("author")!!.toString()
))
}
javadoc {
@@ -27,4 +22,15 @@ tasks {
options.encoding = Charsets.UTF_8.name()
(options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet")
}
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(8)
options.compilerArgs.addAll(listOf("-nowarn", "-Xlint:-unchecked", "-Xlint:-deprecation"))
}
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
@@ -9,5 +9,7 @@ tasks {
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")
}
}
@@ -1,12 +1,25 @@
plugins {
id("tab.base-conventions")
id("net.kyori.indra.publishing")
`maven-publish`
}
indra {
publishReleasesTo("krypton", "https://repo.kryptonmc.org/releases")
publishSnapshotsTo("krypton", "https://repo.kryptonmc.org/snapshots")
configurePublications {
artifactId = "tab-$artifactId"
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"])
}
}
+11 -3
View File
@@ -4,15 +4,23 @@ plugins {
allprojects {
group = "me.neznamy"
version = property("projectVersion") as String // from gradle.properties
version = "4.1.5"
description = "An all-in-one solution that works"
ext.set("id", "tab")
ext.set("website", "https://github.com/NEZNAMY/TAB")
ext.set("author", "NEZNAMY")
}
val platforms = setOf(
projects.bukkit,
projects.bungeecord,
// projects.krypton,
projects.velocity
projects.velocity,
projects.sponge7,
projects.sponge8,
projects.fabric,
projects.fabric.v1144,
projects.fabric.v1182
).map { it.dependencyProject }
val special = setOf(
+14 -8
View File
@@ -1,12 +1,18 @@
dependencies {
implementation(projects.shared)
implementation(libs.bstats.bukkit)
compileOnly(libs.bukkit)
compileOnly(libs.placeholderapi)
compileOnly(libs.vault)
compileOnly(libs.via)
compileOnly(libs.authlib)
compileOnly(libs.essentials) {
exclude("org.spigotmc", "spigot-api")
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")
}
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")
}
@@ -1,6 +1,10 @@
package me.neznamy.tab.platforms.bukkit;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.platform.EventListener;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@@ -8,73 +12,60 @@ 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;
/**
* The core for bukkit forwarding events into all enabled features
*/
public class BukkitEventListener implements Listener {
/** Platform instance */
private final BukkitPlatform platform;
public class BukkitEventListener implements EventListener<Player>, Listener {
/**
* Constructs new instance with given parameter
*
* @param platform
* Platform instance
*/
public BukkitEventListener(BukkitPlatform platform) {
this.platform = platform;
}
/**
* Listener to PlayerQuitEvent to remove player data and forward the event to features
* Listens to player quit event.
*
* @param e
* quit event
* Disconnect event
*/
@EventHandler(priority = EventPriority.HIGHEST)
public void onQuit(PlayerQuitEvent e){
if (TabAPI.getInstance().isPluginDisabled()) return;
TabAPI.getInstance().getThreadManager().runTask(() -> TabAPI.getInstance().getFeatureManager().onQuit(TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId())));
public void onQuit(PlayerQuitEvent e) {
quit(e.getPlayer().getUniqueId());
}
/**
* Listener to PlayerJoinEvent to create player data and forward the event to features
* Listens to player join event.
*
* @param e
* join event
* Join event
*/
@EventHandler(priority = EventPriority.LOWEST)
@EventHandler(priority = EventPriority.LOW)
public void onJoin(PlayerJoinEvent e) {
if (TabAPI.getInstance().isPluginDisabled()) return;
TabAPI.getInstance().getThreadManager().runTask(() ->
TabAPI.getInstance().getFeatureManager().onJoin(new BukkitTabPlayer(e.getPlayer(), platform.getProtocolVersion(e.getPlayer()))));
join(e.getPlayer());
}
/**
* Listener to PlayerChangedWorldEvent to forward the event to features
* Listens to world change event.
*
* @param e
* world changed event
* World change event
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldChange(PlayerChangedWorldEvent e){
if (TabAPI.getInstance().isPluginDisabled()) return;
TabAPI.getInstance().getThreadManager().runTask(() ->
TabAPI.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName()));
public void onWorldChange(PlayerChangedWorldEvent e) {
worldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
}
/**
* Listener to PlayerChangedWorldEvent to forward the event to features
* Listens to command event to potentially cancel it.
*
* @param e
* command preprocess event
* Command event
*/
@EventHandler
public void onCommand(PlayerCommandPreprocessEvent e) {
if (TabAPI.getInstance().isPluginDisabled()) return;
if (TabAPI.getInstance().getFeatureManager().onCommand(
TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
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);
}
}
@@ -1,556 +0,0 @@
package me.neznamy.tab.platforms.bukkit;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.chat.WrappedChatComponent;
import me.neznamy.tab.api.protocol.*;
import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
import me.neznamy.tab.platforms.bukkit.nms.*;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.Collectors;
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitPacketBuilder extends PacketBuilder {
/** NMS data storage */
private final NMSStorage nms = NMSStorage.getInstance();
/** Map of entity type ids for entities used by the plugin */
private final EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
/** Component cache for better performance (1.16+ players) */
private final Map<IChatBaseComponent, Object> componentCacheModern = new HashMap<>();
/** Component cache for better performance (1.15- players) */
private final Map<IChatBaseComponent, Object> componentCacheLegacy = new HashMap<>();
/**
* Constructs new instance
*/
public BukkitPacketBuilder() {
if (nms.getMinorVersion() >= 19) {
entityIds.put(EntityType.ARMOR_STAND, 2);
} else if (nms.getMinorVersion() >= 13) {
entityIds.put(EntityType.ARMOR_STAND, 1);
} else {
entityIds.put(EntityType.WITHER, 64);
if (nms.getMinorVersion() >= 8){
entityIds.put(EntityType.ARMOR_STAND, 30);
}
}
buildMap.put(PacketPlayOutEntityMetadata.class, (packet, version) -> build((PacketPlayOutEntityMetadata)packet));
buildMap.put(PacketPlayOutEntityTeleport.class, (packet, version) -> build((PacketPlayOutEntityTeleport)packet));
buildMap.put(PacketPlayOutEntityDestroy.class, (packet, version) -> build((PacketPlayOutEntityDestroy)packet));
buildMap.put(PacketPlayOutSpawnEntityLiving.class, (packet, version) -> build((PacketPlayOutSpawnEntityLiving)packet));
}
@Override
public Object build(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() >= 9 || clientVersion.getMinorVersion() >= 9) {
//1.9+ server or client, handled by bukkit api or ViaVersion
return packet;
}
//<1.9 client and server
return buildBossPacketEntity(packet, clientVersion);
}
@Override
public Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
Object component = toNMSComponent(packet.getMessage(), clientVersion);
if (nms.getMinorVersion() >= 19)
try {
return nms.newPacketPlayOutChat.newInstance(component, packet.getType() == PacketPlayOutChat.ChatMessageType.GAME_INFO);
} catch (Exception e) {
//1.19.0
return nms.newPacketPlayOutChat.newInstance(component, packet.getType().ordinal());
}
if (nms.getMinorVersion() >= 16)
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.getType().toString()), UUID.randomUUID());
if (nms.getMinorVersion() >= 12)
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.getType().toString()));
if (nms.getMinorVersion() >= 8)
return nms.newPacketPlayOutChat.newInstance(component, (byte) packet.getType().ordinal());
if (nms.getMinorVersion() == 7)
return nms.newPacketPlayOutChat.newInstance(component);
return packet;
}
@Override
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() < 8) return null;
if (nms.ClientboundPlayerInfoRemovePacket != null) {
//1.19.3+
if (packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
return nms.newClientboundPlayerInfoRemovePacket.newInstance(
packet.getEntries().stream().map(PlayerInfoData::getUniqueId).collect(Collectors.toList())
);
}
Enum[] array = packet.getActions().stream().map(action -> Enum.valueOf(nms.EnumPlayerInfoAction, action.toString())).toArray(Enum[]::new);
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(array[0], nms.getHandle.invoke(Bukkit.getOnlinePlayers().iterator().next()));
List<Object> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
if (data.getSkin() != null) profile.getProperties().put("textures",
new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
Object obj = nms.newPlayerInfoData.newInstance(
data.getUniqueId(),
profile,
data.isListed(),
data.getLatency(),
data.getGameMode() == null ? null : Enum.valueOf(nms.EnumGamemode, data.getGameMode().toString()),
data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion),
data.getProfilePublicKey() == null ? null : nms.newRemoteChatSession$Data.newInstance(data.getChatSessionId(), data.getProfilePublicKey()));
items.add(obj);
}
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_ACTION, EnumSet.of(array[0], array));
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
return nmsPacket;
} else {
//1.19.2-
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(Enum.valueOf(nms.EnumPlayerInfoAction, action.toString()), Array.newInstance(nms.EntityPlayer, 0));
List<Object> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
if (data.getSkin() != null) profile.getProperties().put("textures", new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
List<Object> parameters = new ArrayList<>();
if (nms.newPlayerInfoData.getParameterTypes()[0] == nms.PacketPlayOutPlayerInfo) {
parameters.add(nmsPacket);
}
parameters.add(profile);
parameters.add(data.getLatency());
parameters.add(data.getGameMode() == null ? null : Enum.valueOf(nms.EnumGamemode, data.getGameMode().toString()));
parameters.add(data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion));
if (nms.getMinorVersion() >= 19) parameters.add(data.getProfilePublicKey());
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
}
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
return nmsPacket;
}
}
@Override
public Object build(PacketPlayOutPlayerListHeaderFooter packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() < 8) return null;
if (nms.newPacketPlayOutPlayerListHeaderFooter.getParameterCount() == 2) {
return nms.newPacketPlayOutPlayerListHeaderFooter.newInstance(toNMSComponent(packet.getHeader(), clientVersion), toNMSComponent(packet.getFooter(), clientVersion));
}
Object nmsPacket = nms.newPacketPlayOutPlayerListHeaderFooter.newInstance();
nms.setField(nmsPacket, nms.PacketPlayOutPlayerListHeaderFooter_HEADER, toNMSComponent(packet.getHeader(), clientVersion));
nms.setField(nmsPacket, nms.PacketPlayOutPlayerListHeaderFooter_FOOTER, toNMSComponent(packet.getFooter(), clientVersion));
return nmsPacket;
}
@Override
public Object build(PacketPlayOutScoreboardDisplayObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
return nms.newPacketPlayOutScoreboardDisplayObjective.newInstance(packet.getSlot(), newScoreboardObjective(packet.getObjectiveName()));
}
@Override
public Object build(PacketPlayOutScoreboardObjective packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
String displayName = clientVersion.getMinorVersion() < 13 ? cutTo(packet.getDisplayName(), 32) : packet.getDisplayName();
if (nms.getMinorVersion() >= 13) {
return nms.newPacketPlayOutScoreboardObjective.newInstance(nms.newScoreboardObjective.newInstance(null, packet.getObjectiveName(), null,
toNMSComponent(IChatBaseComponent.optimizedComponent(displayName), clientVersion),
packet.getRenderType() == null ? null : Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString())),
packet.getAction());
}
Object nmsPacket = nms.newPacketPlayOutScoreboardObjective.newInstance();
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME, packet.getObjectiveName());
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_DISPLAYNAME, displayName);
if (nms.getMinorVersion() >= 8 && packet.getRenderType() != null) {
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_RENDERTYPE, Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString()));
}
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_METHOD, packet.getAction());
return nmsPacket;
}
@Override
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() >= 13) {
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(Enum.valueOf(nms.EnumScoreboardAction, packet.getAction().toString()), packet.getObjectiveName(), packet.getPlayer(), packet.getScore());
}
if (packet.getAction() == PacketPlayOutScoreboardScore.Action.REMOVE) {
return nms.newPacketPlayOutScoreboardScore_String.newInstance(packet.getPlayer());
}
Object score = nms.newScoreboardScore.newInstance(nms.emptyScoreboard, newScoreboardObjective(packet.getObjectiveName()), packet.getPlayer());
nms.ScoreboardScore_setScore.invoke(score, packet.getScore());
if (nms.getMinorVersion() >= 8) {
return nms.newPacketPlayOutScoreboardScore.newInstance(score);
}
return nms.newPacketPlayOutScoreboardScore.newInstance(score, 0);
}
@Override
public Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.PacketPlayOutScoreboardTeam == null) return null; //fabric
Object team = nms.newScoreboardTeam.newInstance(nms.emptyScoreboard, packet.getName());
String prefix = packet.getPlayerPrefix();
String suffix = packet.getPlayerSuffix();
if (clientVersion.getMinorVersion() < 13) {
prefix = cutTo(prefix, 16);
suffix = cutTo(suffix, 16);
}
((Collection<String>)nms.ScoreboardTeam_getPlayerNameSet.invoke(team)).addAll(packet.getPlayers());
nms.ScoreboardTeam_setAllowFriendlyFire.invoke(team, (packet.getOptions() & 0x1) > 0);
nms.ScoreboardTeam_setCanSeeFriendlyInvisibles.invoke(team, (packet.getOptions() & 0x2) > 0);
if (nms.getMinorVersion() >= 13) {
createTeamModern(packet, clientVersion, team, prefix, suffix);
} else {
createTeamLegacy(packet, team, prefix, suffix);
}
if (nms.getMinorVersion() >= 17) {
switch (packet.getAction()) {
case 0:
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, true);
case 1:
return nms.PacketPlayOutScoreboardTeam_of.invoke(null, team);
case 2:
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, false);
case 3:
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().iterator().next(), Enum.valueOf(nms.PacketPlayOutScoreboardTeam_PlayerAction, "ADD"));
case 4:
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().iterator().next(), Enum.valueOf(nms.PacketPlayOutScoreboardTeam_PlayerAction, "REMOVE"));
default:
throw new IllegalArgumentException("Invalid action: " + packet.getAction());
}
}
return nms.newPacketPlayOutScoreboardTeam.newInstance(team, packet.getAction());
}
/**
* Builds entity destroy packet from custom packet class
*
* @param packet
* Destroy packet
* @return NMS destroy packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityDestroy packet) throws ReflectiveOperationException {
try {
return nms.newPacketPlayOutEntityDestroy.newInstance(new Object[]{packet.getEntities()});
} catch (IllegalArgumentException e) {
//1.17.0
return nms.newPacketPlayOutEntityDestroy.newInstance(packet.getEntities()[0]);
}
}
/**
* Builds entity metadata packet from custom packet class
*
* @param packet
* Metadata packet
* @return NMS metadata packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityMetadata packet) throws ReflectiveOperationException {
if (nms.newPacketPlayOutEntityMetadata.getParameterCount() == 2) {
//1.19.3+
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), nms.DataWatcher_b.invoke(packet.getDataWatcher().toNMS()));
} else {
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
}
}
/**
* Builds entity spawn packet from custom packet class
*
* @param packet
* Spawn packet
* @return NMS spawn packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutSpawnEntityLiving packet) throws ReflectiveOperationException {
Object nmsPacket;
if (nms.getMinorVersion() >= 17) {
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance(nms.dummyEntity);
} else {
nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance();
}
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYID, packet.getEntityId());
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_YAW, (byte)(packet.getLocation().getYaw() * 256.0f / 360.0f));
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_PITCH, (byte)(packet.getLocation().getPitch() * 256.0f / 360.0f));
if (nms.getMinorVersion() <= 14) {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER, packet.getDataWatcher().toNMS());
}
if (nms.getMinorVersion() >= 9) {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_UUID, packet.getUniqueId());
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_X, packet.getLocation().getX());
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_Y, packet.getLocation().getY());
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_Z, packet.getLocation().getZ());
} else {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_X, floor(packet.getLocation().getX()*32));
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_Y, floor(packet.getLocation().getY()*32));
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_Z, floor(packet.getLocation().getZ()*32));
}
int id = entityIds.get(packet.getEntityType());
if (nms.getMinorVersion() >= 19) {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, nms.EntityTypes_ARMOR_STAND); // :(
} else {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, id);
}
return nmsPacket;
}
/**
* Builds entity teleport packet from custom packet class
*
* @param packet
* Teleport packet
* @return NMS teleport packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityTeleport packet) throws ReflectiveOperationException {
Object nmsPacket;
if (nms.getMinorVersion() >= 17) {
nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance(nms.dummyEntity);
} else {
nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance();
}
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_ENTITYID, packet.getEntityId());
if (nms.getMinorVersion() >= 9) {
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_X, packet.getLocation().getX());
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_Y, packet.getLocation().getY());
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_Z, packet.getLocation().getZ());
} else {
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_X, floor(packet.getLocation().getX()*32));
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_Y, floor(packet.getLocation().getY()*32));
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_Z, floor(packet.getLocation().getZ()*32));
}
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_YAW, (byte) (packet.getLocation().getYaw()/360*256));
nms.setField(nmsPacket, nms.PacketPlayOutEntityTeleport_PITCH, (byte) (packet.getLocation().getPitch()/360*256));
return nmsPacket;
}
/**
* Writes data into NMS team from custom team packet. Used on 1.13+ servers.
*
* @param packet
* Packet to read from
* @param clientVersion
* Version of player
* @param team
* Team to write to
* @param prefix
* Prefix to use
* @param suffix
* Suffix to use
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private void createTeamModern(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion, Object team, String prefix, String suffix) throws ReflectiveOperationException {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(prefix), clientVersion));
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(suffix), clientVersion));
EnumChatFormat format = packet.getColor() != null ? packet.getColor() : EnumChatFormat.lastColorsOf(prefix);
nms.ScoreboardTeam_setColor.invoke(team, Enum.valueOf(nms.EnumChatFormat, format.toString()));
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(nms.EnumNameTagVisibility, String.valueOf(packet.getNameTagVisibility()).equals("always") ? "ALWAYS" : "NEVER"));
nms.ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(nms.EnumTeamPush, String.valueOf(packet.getCollisionRule()).equals("always") ? "ALWAYS" : "NEVER"));
}
/**
* Writes data into NMS team from custom team packet. Used on 1.12- servers.
*
* @param packet
* Packet to read from
* @param team
* Team to write to
* @param prefix
* Prefix to use
* @param suffix
* Suffix to use
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private void createTeamLegacy(PacketPlayOutScoreboardTeam packet, Object team, String prefix, String suffix) throws ReflectiveOperationException {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, prefix);
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, suffix);
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(nms.EnumNameTagVisibility, packet.getNameTagVisibility().equals("always") ? "ALWAYS" : "NEVER"));
if (nms.getMinorVersion() >= 9) nms.ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(nms.EnumTeamPush, packet.getCollisionRule().equals("always") ? "ALWAYS" : "NEVER"));
}
@Override
public PacketPlayOutPlayerInfo readPlayerInfo(Object nmsPacket, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() < 8) return null;
if (nms.ClientboundPlayerInfoRemovePacket != null) {
//1.19.3+
if (nms.ClientboundPlayerInfoRemovePacket.isInstance(nmsPacket)) {
List<UUID> entries = (List<UUID>) nms.ClientboundPlayerInfoRemovePacket_getEntries.invoke(nmsPacket);
return new PacketPlayOutPlayerInfo(
EnumPlayerInfoAction.REMOVE_PLAYER,
entries.stream().map(PlayerInfoData::new).collect(Collectors.toList())
);
}
EnumSet<?> set = (EnumSet<?>) nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket);
EnumPlayerInfoAction[] array = set.stream().map(action -> EnumPlayerInfoAction.valueOf(action.toString())).toArray(EnumPlayerInfoAction[]::new);
EnumSet<EnumPlayerInfoAction> actions = EnumSet.of(array[0], array);
List<PlayerInfoData> listData = new ArrayList<>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = profile.getProperties().get("textures").iterator().next();
skin = new Skin(pr.getValue(), pr.getSignature());
}
Object remoteChatSession = nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData);
listData.add(
new PlayerInfoData(
profile.getName(),
profile.getId(),
skin,
(boolean) nms.PlayerInfoData_isListed.invoke(nmsData),
(int) nms.PlayerInfoData_getLatency.invoke(nmsData),
gameMode,
nmsComponent == null ? null : new WrappedChatComponent(nmsComponent),
remoteChatSession == null ? null : (UUID) nms.RemoteChatSession$Data_getSessionId.invoke(remoteChatSession),
remoteChatSession == null ? null : nms.RemoteChatSession$Data_getProfilePublicKey.invoke(remoteChatSession)));
}
return new PacketPlayOutPlayerInfo(actions, listData);
} else {
//1.19.2-
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
List<PlayerInfoData> listData = new ArrayList<>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
IChatBaseComponent listName = nmsComponent == null ? null : new WrappedChatComponent(nmsComponent);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = profile.getProperties().get("textures").iterator().next();
skin = new Skin(pr.getValue(), pr.getSignature());
}
Object profilePublicKey = nms.getMinorVersion() >= 19 ? nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData) : null;
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), skin, true,
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName, null, profilePublicKey));
}
return new PacketPlayOutPlayerInfo(action, listData);
}
}
@Override
public PacketPlayOutScoreboardObjective readObjective(Object nmsPacket) throws ReflectiveOperationException {
return new PacketPlayOutScoreboardObjective(nms.PacketPlayOutScoreboardObjective_METHOD.getInt(nmsPacket),
(String) nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME.get(nmsPacket), null,
PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay.INTEGER
);
}
@Override
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object nmsPacket) throws ReflectiveOperationException {
return new PacketPlayOutScoreboardDisplayObjective(
nms.PacketPlayOutScoreboardDisplayObjective_POSITION.getInt(nmsPacket),
(String) nms.PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME.get(nmsPacket)
);
}
/**
* Builds entity packet representing requested BossBar packet using Wither on 1.8- clients.
*
* @param packet
* packet to build
* @param clientVersion
* client version
* @return entity BossBar packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private Object buildBossPacketEntity(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (packet.getAction() == Action.UPDATE_STYLE) return null; //nothing to do here
int entityId = packet.getId().hashCode();
if (packet.getAction() == Action.REMOVE) {
return build(new PacketPlayOutEntityDestroy(entityId));
}
DataWatcher w = new DataWatcher();
if (packet.getAction() == Action.UPDATE_PCT || packet.getAction() == Action.ADD) {
float health = 300*packet.getPct();
if (health == 0) health = 1;
w.helper().setHealth(health);
}
if (packet.getAction() == Action.UPDATE_NAME || packet.getAction() == Action.ADD) {
w.helper().setCustomName(packet.getName(), clientVersion);
}
if (packet.getAction() == Action.ADD) {
w.helper().setEntityFlags((byte) 32);
w.helper().setWitherInvulnerableTime(880); // Magic number
return build(new PacketPlayOutSpawnEntityLiving(entityId, new UUID(0, 0), EntityType.WITHER, new Location(null, 0,0,0), w));
} else {
return build(new PacketPlayOutEntityMetadata(entityId, w));
}
}
/**
* A method taken from minecraft code used to convert double to int
*
* @param paramDouble
* double value
* @return int value
*/
private int floor(double paramDouble){
int i = (int)paramDouble;
return paramDouble < i ? i - 1 : i;
}
/**
* Converts TAB's IChatBaseComponent into minecraft's component using String deserialization.
* If the requested component is found in cache, it is returned. If not, it is created, added into cache and returned.
* If {@code component} is {@code null}, returns {@code null}
*
* @param component
* component to convert
* @param clientVersion
* client version used to decide RGB conversion
* @return converted component or {@code null} if {@code component} is {@code null}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object toNMSComponent(IChatBaseComponent component, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (component == null) return null;
if (component instanceof WrappedChatComponent) return ((WrappedChatComponent) component).get();
Map<IChatBaseComponent, Object> cache = clientVersion.getMinorVersion() >= 16 ? componentCacheModern : componentCacheLegacy;
if (cache.containsKey(component)) return cache.get(component);
if (cache.size() > 10000) cache.clear();
Object chat = nms.ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion));
cache.put(component, chat);
return chat;
}
/**
* Creates a new Scoreboard Objective with given name.
*
* @param objectiveName
* Objective name
* @return NMS Objective
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private Object newScoreboardObjective(String objectiveName) throws ReflectiveOperationException {
if (nms.getMinorVersion() >= 13) {
return nms.newScoreboardObjective.newInstance(null, objectiveName, null, toNMSComponent(new IChatBaseComponent(""), TabAPI.getInstance().getServerVersion()), null);
}
return nms.newScoreboardObjective.newInstance(null, objectiveName, nms.IScoreboardCriteria_self.get(null));
}
}
@@ -1,149 +1,75 @@
package me.neznamy.tab.platforms.bukkit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Function;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PipelineInjector;
import me.neznamy.tab.shared.features.sorting.Sorting;
import io.netty.channel.Channel;
import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.features.injection.NettyPipelineInjector;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.util.FunctionWithException;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Pipeline injection for bukkit
* Pipeline injection for Bukkit 1.8+.
*/
public class BukkitPipelineInjector extends PipelineInjector {
public class BukkitPipelineInjector extends NettyPipelineInjector {
/** NMS data storage */
private final NMSStorage nms = NMSStorage.getInstance();
/** Function for getting player's channel */
private static FunctionWithException<TabPlayer, Channel> getChannel;
/**
* Constructs new instance
*/
public BukkitPipelineInjector(){
public BukkitPipelineInjector() {
super("packet_handler");
}
@Override
public Function<TabPlayer, ChannelDuplexHandler> getChannelFunction() {
return BukkitChannelDuplexHandler::new;
/**
* Attempts to load required classes, fields and methods and marks class as available.
* If something fails, error message is printed and class is not marked as available.
*/
public static void tryLoad() {
try {
Class<?> NetworkManager = BukkitReflection.getClass("network.Connection", "network.NetworkManager", "NetworkManager");
Class<?> PlayerConnection = BukkitReflection.getClass("server.network.ServerGamePacketListenerImpl",
"server.network.PlayerConnection", "PlayerConnection");
Class<?> EntityPlayer = BukkitReflection.getClass("server.level.ServerPlayer", "server.level.EntityPlayer", "EntityPlayer");
Method getHandle = BukkitReflection.getBukkitClass("entity.CraftPlayer").getMethod("getHandle");
Field PLAYER_CONNECTION = ReflectionUtils.getOnlyField(EntityPlayer, PlayerConnection);
Field NETWORK_MANAGER;
if (BukkitReflection.is1_20_2Plus()) {
NETWORK_MANAGER = ReflectionUtils.getOnlyField(PlayerConnection.getSuperclass(), NetworkManager);
} else {
NETWORK_MANAGER = ReflectionUtils.getOnlyField(PlayerConnection, NetworkManager);
}
Field CHANNEL = ReflectionUtils.getOnlyField(NetworkManager, Channel.class);
getChannel = player -> (Channel) CHANNEL.get(NETWORK_MANAGER.get(PLAYER_CONNECTION.get(getHandle.invoke(player.getPlayer()))));
} catch (Exception e) {
BukkitUtils.compatibilityError(e, "network channel injection", null,
"Unlimited nametag mode not working and being replaced with regular nametags",
"Anti-override for tablist & nametags not working",
"Compatibility with nickname plugins changing player names will not work",
"Scoreboard will not be checking for other plugins");
}
}
/**
* Custom channel duplex handler override
* Returns {@code true} if pipeline injection is available, {@code false} if not.
*
* @return {@code true} if pipeline injection is available, {@code false} if not
*/
public class BukkitChannelDuplexHandler extends ChannelDuplexHandler {
public static boolean isAvailable() {
return getChannel != null;
}
/** Injected player */
private final TabPlayer player;
/**
* Constructs new instance with given player
*
* @param player
* player to inject
*/
public BukkitChannelDuplexHandler(TabPlayer player) {
this.player = player;
}
@Override
public void channelRead(@NotNull ChannelHandlerContext context, @NotNull Object packet) {
try {
if (TAB.getInstance().getFeatureManager().onPacketReceive(player, packet)) return;
super.channelRead(context, packet);
} catch (Exception e){
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
}
}
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
try {
if (nms.PacketPlayOutPlayerInfo.isInstance(packet) ||
(nms.ClientboundPlayerInfoRemovePacket != null && nms.ClientboundPlayerInfoRemovePacket.isInstance(packet))) {
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
return;
}
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam != null && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
long time = System.nanoTime();
modifyPlayers(packet);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
super.write(context, packet, channelPromise);
return;
}
if (nms.PacketPlayOutScoreboardDisplayObjective.isInstance(packet)){
TAB.getInstance().getFeatureManager().onDisplayObjective(player, packet);
}
if (nms.PacketPlayOutScoreboardObjective.isInstance(packet)) {
TAB.getInstance().getFeatureManager().onObjective(player, packet);
}
TAB.getInstance().getFeatureManager().onPacketSend(player, packet);
} catch (Exception e){
TAB.getInstance().getErrorManager().printError("An error occurred when reading packets", e);
}
try {
super.write(context, packet, channelPromise);
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to forward packet " + packet.getClass().getSimpleName() + " to " + player.getName(), e);
}
}
/**
* Removes all real players from team if packet does not come from TAB and reports this to override log
*
* @param packetPlayOutScoreboardTeam
* team packet
* @throws ReflectiveOperationException
* nmsGameMode
*/
@SuppressWarnings("unchecked")
private void modifyPlayers(Object packetPlayOutScoreboardTeam) throws ReflectiveOperationException {
int action = nms.PacketPlayOutScoreboardTeam_ACTION.getInt(packetPlayOutScoreboardTeam);
if (action == 1 || action == 2 || action == 4) return;
Collection<String> players = (Collection<String>) nms.PacketPlayOutScoreboardTeam_PLAYERS.get(packetPlayOutScoreboardTeam);
String teamName = (String) nms.PacketPlayOutScoreboardTeam_NAME.get(packetPlayOutScoreboardTeam);
if (players == null) return;
//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 = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (!((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(sorting.getShortTeamName(p))) {
logTeamOverride(teamName, p.getName(), sorting.getShortTeamName(p));
} else {
newList.add(entry);
}
}
nms.setField(packetPlayOutScoreboardTeam, nms.PacketPlayOutScoreboardTeam_PLAYERS, newList);
}
/**
* Returns player with matching game profile name. This is different from
* real name when a nick plugin changing names of players is used. If no
* player was found, returns {@code null}.
*
* @param name
* Game profile name
* @return Player with matching game profile name
*/
private TabPlayer getPlayer(String name) {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (p.getNickname().equals(name)) return p;
}
return null;
}
@Override
@Nullable
@SneakyThrows
protected Channel getChannel(@NotNull TabPlayer player) {
return getChannel.apply(player);
}
}
@@ -1,147 +0,0 @@
package me.neznamy.tab.platforms.bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.util.Locale;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
import me.neznamy.tab.shared.TAB;
import net.ess3.api.events.NickChangeEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import com.earth2me.essentials.Essentials;
import me.neznamy.tab.api.placeholder.PlaceholderManager;
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
import net.milkbowl.vault.chat.Chat;
import org.bukkit.plugin.java.JavaPlugin;
/**
* Bukkit registry to register bukkit-only and universal placeholders
*/
public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
/** Number formatter for 2 decimal places */
private final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
/** JavaPlugin reference for registering events */
private final JavaPlugin plugin;
/** Vault Chat hook */
private Chat chat;
/** NMS server to get TPS from on spigot */
private Object server;
/** TPS field*/
private Field recentTps;
/** Detection for presence of Paper's TPS getter */
private Method paperTps;
/** Detection for presence of Paper's MSPT getter */
private Method paperMspt;
/** Detection for presence of Purpur's AFK getter */
private Method purpurIsAfk;
/**
* Constructs new instance with given parameter and loads hooks
*
* @param plugin
* reference to the plugin's main class
*/
public BukkitPlaceholderRegistry(JavaPlugin plugin) {
this.plugin = plugin;
numberFormat.setMaximumFractionDigits(2);
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
RegisteredServiceProvider<Chat> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
if (rspChat != null) chat = rspChat.getProvider();
}
try {
server = Bukkit.getServer().getClass().getMethod("getServer").invoke(Bukkit.getServer());
recentTps = server.getClass().getField("recentTps");
} catch (ReflectiveOperationException e) {
//not spigot
}
try { paperTps = Bukkit.class.getMethod("getTPS"); } catch (NoSuchMethodException ignored) {}
try { paperMspt = Bukkit.class.getMethod("getAverageTickTime"); } catch (NoSuchMethodException ignored) {}
try { purpurIsAfk = Player.class.getMethod("isAfk"); } catch (NoSuchMethodException ignored) {}
}
@SuppressWarnings("deprecation")
@Override
public void registerPlaceholders(PlaceholderManager manager) {
manager.registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((Player) p.getPlayer()).getDisplayName());
if (paperTps != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> formatTPS(Bukkit.getTPS()[0]));
} else if (recentTps != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> {
try {
return formatTPS(((double[]) recentTps.get(server))[0]);
} catch (IllegalAccessException e) {
return -1;
}
});
} else {
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, -1, () -> -1);
}
if (paperMspt != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.MSPT, 1000, () -> numberFormat.format(Bukkit.getAverageTickTime()));
}
Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
manager.registerPlayerPlaceholder(TabConstants.Placeholder.AFK, 500, p -> {
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
return purpurIsAfk != null && ((Player)p.getPlayer()).isAfk();
});
if (essentials != null) {
PlayerPlaceholder nick = manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, p -> {
String nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
return nickname == null ? p.getName() : nickname;
});
Listener nickListener = new Listener() {
@EventHandler
public void onNickChange(NickChangeEvent e) {
String name = e.getValue() == null ? e.getController().getName() : e.getValue();
TabPlayer player = TAB.getInstance().getPlayer(e.getController().getUUID());
if (player == null) return;
nick.updateValue(player, name);
}
};
nick.enableTriggerMode(() -> Bukkit.getPluginManager().registerEvents(nickListener, plugin),
() -> HandlerList.unregisterAll(nickListener));
} else {
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, TabPlayer::getName);
}
if (chat != null) {
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, 1000, p -> chat.getPlayerPrefix((Player) p.getPlayer()));
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, 1000, p -> chat.getPlayerSuffix((Player) p.getPlayer()));
} else {
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, -1, () -> "");
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, -1, () -> "");
}
manager.registerPlayerPlaceholder(TabConstants.Placeholder.HEALTH, 100, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
super.registerPlaceholders(manager);
}
/**
* Formats TPS using number formatter with 2 decimal places.
*
* @param tps
* TPS to format
* @return Formatted TPS as a String
*/
private String formatTPS(double tps) {
return numberFormat.format(Math.min(20, tps));
}
}
@@ -1,285 +0,0 @@
package me.neznamy.tab.platforms.bukkit;
import com.viaversion.viaversion.api.Via;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.platforms.bukkit.features.PetFix;
import me.neznamy.tab.platforms.bukkit.features.WitherBossBar;
import me.neznamy.tab.platforms.bukkit.features.unlimitedtags.BukkitNameTagX;
import me.neznamy.tab.platforms.bukkit.permission.Vault;
import me.neznamy.tab.shared.Platform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.None;
import me.neznamy.tab.shared.permission.PermissionPlugin;
import me.neznamy.tab.shared.placeholders.PlayerPlaceholderImpl;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Collection;
/**
* Implementation of Platform interface for Bukkit platform
*/
public class BukkitPlatform extends Platform {
/** Plugin instance for registering tasks and events */
private final JavaPlugin plugin;
/** Variables checking presence of other plugins to hook into */
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API);
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
private Plugin viaVersion;
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
/**
* Constructs new instance with given plugin parameter
*
* @param plugin
* plugin instance
*/
public BukkitPlatform(JavaPlugin plugin) {
super(new BukkitPacketBuilder());
this.plugin = plugin;
}
@Override
public PermissionPlugin detectPermissionPlugin() {
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LUCKPERMS)) {
return new LuckPerms(getPluginVersion(TabConstants.Plugin.LUCKPERMS));
} else if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
RegisteredServiceProvider<Permission> provider = Bukkit.getServicesManager().getRegistration(Permission.class);
if (provider == null) return new None();
return new Vault(provider.getProvider(), getPluginVersion(TabConstants.Plugin.VAULT));
} else {
return new None();
}
}
@Override
public void loadFeatures() {
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VIAVERSION)) {
try {
Class.forName("com.viaversion.viaversion.api.Via");
viaVersion = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.VIAVERSION);
} catch (ClassNotFoundException e) {
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion(TabConstants.Plugin.VIAVERSION) + ") was detected.", true);
TAB.getInstance().sendConsoleMessage("&cTAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
TAB.getInstance().sendConsoleMessage("&cThis might cause problems, such as limitations still being present for latest MC clients as well as RGB not working.", true);
}
}
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.SORTING, new Sorting());
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && tab.getServerVersion().getMinorVersion() >= 8) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, new BukkitNameTagX(plugin));
} else {
tab.getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS, new NameTag());
}
}
tab.loadUniversalFeatures();
if (tab.getConfiguration().getConfig().getBoolean("bossbar.enabled", false)) {
if (tab.getServerVersion().getMinorVersion() < 9) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new WitherBossBar(plugin));
} else {
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new BossBarManagerImpl());
}
}
if (tab.getServerVersion().getMinorVersion() >= 9 && tab.getConfiguration().getConfig().getBoolean("fix-pet-names.enabled", false))
tab.getFeatureManager().registerFeature(TabConstants.Feature.PET_FIX, new PetFix());
if (tab.getConfiguration().getConfig().getBoolean("per-world-playerlist.enabled", false))
tab.getFeatureManager().registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, new PerWorldPlayerList(plugin));
if (placeholderAPI && tab.getConfiguration().getConfig().getBoolean("placeholders.register-tab-expansion", true)) {
BukkitTabExpansion expansion = new BukkitTabExpansion();
expansion.register();
TAB.getInstance().getPlaceholderManager().setTabExpansion(expansion);
}
for (Player p : getOnlinePlayers()) {
tab.addPlayer(new BukkitTabPlayer(p, getProtocolVersion(p)));
}
}
@Override
public String getPluginVersion(String plugin) {
Plugin pl = Bukkit.getPluginManager().getPlugin(plugin);
return pl == null ? null : pl.getDescription().getVersion();
}
/**
* Returns online players from Bukkit API
*
* @return online players from Bukkit API
*/
@SuppressWarnings("unchecked")
private Player[] getOnlinePlayers() {
try {
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]);
}
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get online players", e);
return new Player[0];
}
}
@Override
public void registerUnknownPlaceholder(String identifier) {
PlaceholderManagerImpl pl = TAB.getInstance().getPlaceholderManager();
if (identifier.startsWith("%rel_")) {
//relational placeholder
TAB.getInstance().getPlaceholderManager().registerRelationalPlaceholder(identifier, pl.getRelationalRefresh(identifier), (viewer, target) ->
placeholderAPI ? PlaceholderAPI.setRelationalPlaceholders((Player) viewer.getPlayer(), (Player) target.getPlayer(), identifier) : identifier);
} else {
//normal placeholder
if (identifier.startsWith("%sync:")) {
int refresh = pl.getServerPlaceholderRefreshIntervals().getOrDefault(identifier,
pl.getPlayerPlaceholderRefreshIntervals().getOrDefault(identifier, pl.getDefaultRefresh()));
String syncedPlaceholder = "%" + identifier.substring(6, identifier.length()-1) + "%";
pl.registerPlaceholder(new PlayerPlaceholderImpl(identifier, refresh, null) {
@Override
public Object request(TabPlayer p) {
Bukkit.getScheduler().runTask(plugin, () -> {
long time = System.nanoTime();
updateValue(p, placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), syncedPlaceholder) : identifier);
TAB.getInstance().getCPUManager().addPlaceholderTime(getIdentifier(), System.nanoTime()-time);
});
return null;
}
});
return;
}
if (pl.getServerPlaceholderRefreshIntervals().containsKey(identifier)) {
TAB.getInstance().getPlaceholderManager().registerServerPlaceholder(identifier, pl.getServerPlaceholderRefreshIntervals().get(identifier), () ->
placeholderAPI ? PlaceholderAPI.setPlaceholders(null, identifier) : identifier);
} else {
int refresh = pl.getPlayerPlaceholderRefreshIntervals().getOrDefault(identifier, pl.getDefaultRefresh());
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder(identifier, refresh, p ->
placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), identifier) : identifier);
}
}
}
/**
* Returns status of LibsDisguises plugin presence
*
* @return {@code true} if plugin is enabled, {@code false} if not
*/
public boolean isLibsDisguisesEnabled() {
return libsDisguises;
}
/**
* Sets LibsDisguises presence status to provided value. This is used
* to disable LibsDisguises hook in case the plugin is not correctly loaded
* for any reason to avoid error spam in the hook.
*
* @param enabled
* New status of LibsDisguises presence
*/
public void setLibsDisguisesEnabled(boolean enabled) {
libsDisguises = enabled;
}
/**
* Gets protocol version of requested player and returns it.
*
* @param player
* Player to get protocol version of
* @return protocol version of the player
*/
public int getProtocolVersion(Player player) {
if (protocolSupport){
int version = getProtocolVersionPS(player);
//some PS versions return -1 on unsupported server versions instead of throwing exception
if (version != -1 && version < TAB.getInstance().getServerVersion().getNetworkId()) return version;
}
if (viaVersion != null) {
return getProtocolVersionVia(player, 0);
}
return TAB.getInstance().getServerVersion().getNetworkId();
}
/**
* Returns protocol version of requested player using ProtocolSupport
*
* @param player
* Player to get protocol version of
* @return protocol version of the player using ProtocolSupport
*/
private int getProtocolVersionPS(Player player){
try {
Object protocolVersion = Class.forName("protocolsupport.api.ProtocolSupportAPI").getMethod("getProtocolVersion", Player.class).invoke(null, player);
int version = (int) protocolVersion.getClass().getMethod("getId").invoke(protocolVersion);
TAB.getInstance().debug("ProtocolSupport returned protocol version " + version + " for " + player.getName() + " (online=" + player.isOnline() + ")");
return version;
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ProtocolSupport", player.getName()), e);
return TAB.getInstance().getServerVersion().getNetworkId();
}
}
/**
* Returns protocol version of requested player using ViaVersion
*
* @param player
* Player to get protocol version of
* @return protocol version of the player using ViaVersion
*/
private int getProtocolVersionVia(Player player, int retryLevel){
try {
if (retryLevel == 10) {
TAB.getInstance().debug("Failed to get protocol version of " + player.getName() + " after 10 retries");
return TAB.getInstance().getServerVersion().getNetworkId();
}
int version = Via.getAPI().getPlayerVersion(player.getUniqueId());
if (version == -1) {
if (!player.isOnline()) return TAB.getInstance().getServerVersion().getNetworkId();
Thread.sleep(5);
return getProtocolVersionVia(player, retryLevel + 1);
}
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + player.getName() + " (online=" + player.isOnline() + ")");
return version;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
} catch (Exception | LinkageError e) {
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ViaVersion v%s", player.getName(), viaVersion.getDescription().getVersion()), e);
return TAB.getInstance().getServerVersion().getNetworkId();
}
}
/**
* Sends console message using ConsoleCommandSender, due to
* Paper not translating colors correctly in Logger messages
*
* @param message
* Message to send
* @param translateColors
* Whether color codes should be translated or not
*/
@Override
public void sendConsoleMessage(String message, boolean translateColors) {
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ? EnumChatFormat.color(message) : message));
}
}
@@ -0,0 +1,24 @@
package me.neznamy.tab.platforms.bukkit;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.platforms.bukkit.platform.FoliaPlatform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.util.ReflectionUtils;
import org.bukkit.plugin.java.JavaPlugin;
/**
* Main class for Bukkit.
*/
public class BukkitTAB extends JavaPlugin {
@Override
public void onEnable() {
boolean folia = ReflectionUtils.classExists("io.papermc.paper.threadedregions.RegionizedServer");
TAB.create(folia ? new FoliaPlatform(this) : new BukkitPlatform(this));
}
@Override
public void onDisable() {
TAB.getInstance().unload();
}
}
@@ -0,0 +1,49 @@
package me.neznamy.tab.platforms.bukkit;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import me.neznamy.tab.shared.platform.TabPlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* Command handler for /tab command
*/
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));
}
} else {
TabPlayer p = null;
if (sender instanceof Player) {
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
if (p == null) return true; //player not loaded correctly
}
TAB.getInstance().getCommand().execute(p, args);
}
return false;
}
@Override
@NotNull
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
TabPlayer p = null;
if (sender instanceof Player) {
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
if (p == null) return Collections.emptyList(); //player not loaded correctly
}
return TAB.getInstance().getCommand().complete(p, args);
}
}
@@ -1,236 +1,70 @@
package me.neznamy.tab.platforms.bukkit;
import java.util.*;
import com.mojang.authlib.properties.Property;
import me.neznamy.tab.api.protocol.PacketPlayOutChat;
import me.neznamy.tab.api.protocol.Skin;
import me.neznamy.tab.api.util.Preconditions;
import org.bukkit.Bukkit;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarFlag;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Entity;
import lombok.Getter;
import me.neznamy.tab.platforms.bukkit.bossbar.BossBarLoader;
import me.neznamy.tab.platforms.bukkit.hook.LibsDisguisesHook;
import me.neznamy.tab.platforms.bukkit.entity.PacketEntityView;
import me.neznamy.tab.platforms.bukkit.nms.PingRetriever;
import me.neznamy.tab.platforms.bukkit.platform.BukkitPlatform;
import me.neznamy.tab.platforms.bukkit.scoreboard.ScoreboardLoader;
import me.neznamy.tab.platforms.bukkit.tablist.TabListBase;
import me.neznamy.tab.shared.backend.entityview.DummyEntityView;
import me.neznamy.tab.shared.backend.entityview.EntityView;
import me.neznamy.tab.shared.platform.BossBar;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.platform.TabList;
import me.neznamy.tab.shared.platform.Scoreboard;
import me.neznamy.tab.shared.backend.BackendTabPlayer;
import org.bukkit.entity.Player;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.potion.PotionEffectType;
import com.mojang.authlib.GameProfile;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
import com.viaversion.viaversion.api.legacy.bossbar.BossFlag;
import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
import io.netty.channel.Channel;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* TabPlayer implementation for Bukkit platform
*/
public class BukkitTabPlayer extends ITabPlayer {
@SuppressWarnings("deprecation")
@Getter
public class BukkitTabPlayer extends BackendTabPlayer {
/** Player's NMS handle (EntityPlayer), preloading for speed */
private Object handle;
@NotNull
private final Scoreboard<BukkitTabPlayer, ?> scoreboard = ScoreboardLoader.getInstance().apply(this);
/** Player's connection for sending packets, preloading for speed */
private Object playerConnection;
/** Bukkit BossBars the player can currently see */
private final Map<UUID, BossBar> bossBars = new HashMap<>();
@NotNull
private final TabListBase<?> tabList = TabListBase.getInstance().apply(this);
/** ViaVersion BossBars this 1.9+ player can see on 1.8 server */
private final Map<UUID, com.viaversion.viaversion.api.legacy.bossbar.BossBar> viaBossBars = new HashMap<>();
@NotNull
private final BossBar bossBar = BossBarLoader.findInstance(this);
@NotNull
private final EntityView entityView = PacketEntityView.isAvailable() ? new PacketEntityView(this) : new DummyEntityView();
/**
* Constructs new instance with given bukkit player and protocol version
* Constructs new instance with given bukkit player
*
* @param platform
* Server platform
* @param p
* bukkit player
* @param protocolVersion
* Player's protocol network id
*/
public BukkitTabPlayer(Player p, int protocolVersion){
super(p, p.getUniqueId(), p.getName(), TAB.getInstance().getConfiguration().getServerName(), p.getWorld().getName(), protocolVersion, true);
try {
handle = NMSStorage.getInstance().getHandle.invoke(player);
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get playerConnection of " + p.getName(), e);
}
public BukkitTabPlayer(@NotNull BukkitPlatform platform, @NotNull Player p) {
super(platform, p, p.getUniqueId(), p.getName(), p.getWorld().getName(), platform.getServerVersion().getNetworkId());
}
@Override
public boolean hasPermission(String permission) {
Preconditions.checkNotNull(permission, "permission");
long time = System.nanoTime();
boolean value = getPlayer().hasPermission(permission);
TAB.getInstance().getCPUManager().addMethodTime("hasPermission", System.nanoTime()-time);
return value;
public boolean hasPermission(@NotNull String permission) {
return getPlayer().hasPermission(permission);
}
@Override
public int getPing() {
try {
int ping = NMSStorage.getInstance().PING.getInt(handle);
if (ping > 10000 || ping < 0) ping = -1;
return ping;
} catch (IllegalAccessException e) {
return -1;
}
return PingRetriever.getPing(getPlayer());
}
@Override
public void sendPacket(Object nmsPacket) {
if (nmsPacket == null || !getPlayer().isOnline()) return;
long time = System.nanoTime();
try {
if (nmsPacket instanceof PacketPlayOutBoss) {
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
handle((PacketPlayOutBoss) nmsPacket);
} else {
handleVia((PacketPlayOutBoss) nmsPacket);
}
} else if (nmsPacket instanceof PacketPlayOutChat) {
getPlayer().sendMessage(((PacketPlayOutChat) nmsPacket).getMessage().toLegacyText());
} else {
NMSStorage.getInstance().sendPacket.invoke(playerConnection, nmsPacket);
}
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("An error occurred when sending " + nmsPacket.getClass().getSimpleName(), e);
}
TAB.getInstance().getCPUManager().addMethodTime("sendPacket", System.nanoTime()-time);
}
/**
* Handles PacketPlayOutBoss packet send request using Bukkit API,
* since the API offers everything we need and makes us not need to
* deal with NMS code at all.
*
* @param packet
* packet request to handle using Bukkit API
*/
private void handle(PacketPlayOutBoss packet) {
BossBar bar = bossBars.get(packet.getId());
if (packet.getAction() == PacketPlayOutBoss.Action.ADD) {
if (bossBars.containsKey(packet.getId())) return;
bar = Bukkit.createBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16),
BarColor.valueOf(packet.getColor().name()),
BarStyle.valueOf(packet.getOverlay().getBukkitName()));
if (packet.isCreateWorldFog()) bar.addFlag(BarFlag.CREATE_FOG);
if (packet.isDarkenScreen()) bar.addFlag(BarFlag.DARKEN_SKY);
if (packet.isPlayMusic()) bar.addFlag(BarFlag.PLAY_BOSS_MUSIC);
bar.setProgress(packet.getPct());
bossBars.put(packet.getId(), bar);
bar.addPlayer(getPlayer());
return;
}
if (bar == null) return; //no idea how
switch (packet.getAction()) {
case REMOVE:
bar.removePlayer(getPlayer());
bossBars.remove(packet.getId());
break;
case UPDATE_PCT:
bar.setProgress(packet.getPct());
break;
case UPDATE_NAME:
bar.setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16));
break;
case UPDATE_STYLE:
bar.setColor(BarColor.valueOf(packet.getColor().name()));
bar.setStyle(BarStyle.valueOf(packet.getOverlay().getBukkitName()));
break;
case UPDATE_PROPERTIES:
bar = bossBars.get(packet.getId());
processFlag(bar, packet.isCreateWorldFog(), BarFlag.CREATE_FOG);
processFlag(bar, packet.isDarkenScreen(), BarFlag.DARKEN_SKY);
processFlag(bar, packet.isPlayMusic(), BarFlag.PLAY_BOSS_MUSIC);
break;
default:
break;
}
}
/**
* Handles PacketPlayOutBoss packet request for 1.9+ clients on
* 1.8 servers using ViaVersion API instead of using Wither.
*
* @param packet
* packet request to handle using ViaVersion API
*/
private void handleVia(PacketPlayOutBoss packet) {
com.viaversion.viaversion.api.legacy.bossbar.BossBar bar;
switch (packet.getAction()) {
case ADD:
if (viaBossBars.containsKey(packet.getId())) return;
bar = Via.getAPI().legacyAPI().createLegacyBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16),
packet.getPct(),
BossColor.valueOf(packet.getColor().name()),
BossStyle.valueOf(packet.getOverlay().getBukkitName()));
//fog missing from via API
if (packet.isDarkenScreen()) bar.addFlag(BossFlag.DARKEN_SKY);
if (packet.isPlayMusic()) bar.addFlag(BossFlag.PLAY_BOSS_MUSIC);
viaBossBars.put(packet.getId(), bar);
bar.addPlayer(getPlayer().getUniqueId());
break;
case REMOVE:
viaBossBars.get(packet.getId()).removePlayer(getPlayer().getUniqueId());
viaBossBars.remove(packet.getId());
break;
case UPDATE_PCT:
viaBossBars.get(packet.getId()).setHealth(packet.getPct());
break;
case UPDATE_NAME:
viaBossBars.get(packet.getId()).setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16));
break;
case UPDATE_STYLE:
viaBossBars.get(packet.getId()).setColor(BossColor.valueOf(packet.getColor().name()));
viaBossBars.get(packet.getId()).setStyle(BossStyle.valueOf(packet.getOverlay().getBukkitName()));
break;
case UPDATE_PROPERTIES:
bar = viaBossBars.get(packet.getId());
//fog missing from via API
processFlagVia(bar, packet.isDarkenScreen(), BossFlag.DARKEN_SKY);
processFlagVia(bar, packet.isPlayMusic(), BossFlag.PLAY_BOSS_MUSIC);
break;
default:
break;
}
}
/**
* Sets BossBar flag to requested target value.
*
* @param bar
* BossBar to set flag of
* @param targetValue
* Target value of the flag
* @param flag
* Flag to set value of
*/
private void processFlag(BossBar bar, boolean targetValue, BarFlag flag) {
if (targetValue && !bar.hasFlag(flag)) bar.addFlag(flag);
if (!targetValue && bar.hasFlag(flag)) bar.removeFlag(flag);
}
/**
* Sets BossBar flag to requested target value.
*
* @param bar
* BossBar to set flag of
* @param targetValue
* Target value of the flag
* @param flag
* Flag to set value of
*/
private void processFlagVia(com.viaversion.viaversion.api.legacy.bossbar.BossBar bar, boolean targetValue, BossFlag flag) {
if (targetValue && !bar.hasFlag(flag)) bar.addFlag(flag);
if (!targetValue && bar.hasFlag(flag)) bar.removeFlag(flag);
public void sendMessage(@NotNull TabComponent message) {
getPlayer().sendMessage(getPlatform().toBukkitFormat(message, getVersion().supportsRGB()));
}
@Override
@@ -240,93 +74,47 @@ public class BukkitTabPlayer extends ITabPlayer {
@Override
public boolean isDisguised() {
try {
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).isLibsDisguisesEnabled()) return false;
return (boolean) Class.forName("me.libraryaddict.disguise.DisguiseAPI").getMethod("isDisguised", Entity.class).invoke(null, getPlayer());
} catch (LinkageError | ReflectiveOperationException e) {
//java.lang.NoClassDefFoundError: Could not initialize class me.libraryaddict.disguise.DisguiseAPI
TAB.getInstance().getErrorManager().printError("Failed to check disguise status using LibsDisguises", e);
((BukkitPlatform)TAB.getInstance().getPlatform()).setLibsDisguisesEnabled(false);
return false;
}
return LibsDisguisesHook.isDisguised(this);
}
@Override
public Skin getSkin() {
try {
Collection<Property> col = ((GameProfile)NMSStorage.getInstance().getProfile.invoke(handle)).getProperties().get("textures");
if (col.isEmpty()) return null; //offline mode
Property property = col.iterator().next();
return new Skin(property.getValue(), property.getSignature());
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get skin of " + getName(), e);
return null;
}
@Nullable
public TabList.Skin getSkin() {
return tabList.getSkin();
}
@Override
@NotNull
public Player getPlayer() {
return (Player) player;
}
@Override
public boolean isOnline() {
return getPlayer().isOnline();
public BukkitPlatform getPlatform() {
return (BukkitPlatform) platform;
}
@Override
public boolean isVanished() {
return getPlayer().getMetadata("vanished").stream().anyMatch(MetadataValue::asBoolean);
for (MetadataValue v : getPlayer().getMetadata("vanished")) {
if (v.asBoolean()) return true;
}
return false;
}
@SuppressWarnings("deprecation")
@Override
public int getGamemode() {
return getPlayer().getGameMode().getValue();
}
@Override
public Object getProfilePublicKey() {
if (NMSStorage.getInstance().getMinorVersion() < 19) return null;
try {
if (NMSStorage.getInstance().RemoteChatSession != null) {
//1.19.3+
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
if (session == null) return null;
Object key = NMSStorage.getInstance().RemoteChatSession_getProfilePublicKey.invoke(session);
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
} else {
Object key = NMSStorage.getInstance().EntityHuman_ProfilePublicKey.get(handle);
if (key == null) return null;
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
}
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get profile key of " + getName(), e);
return null;
}
public double getHealth() {
return getPlayer().getHealth();
}
@Override
public UUID getChatSessionId() {
if (NMSStorage.getInstance().RemoteChatSession != null) return null;
try {
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
if (session == null) return null;
return (UUID) NMSStorage.getInstance().RemoteChatSession_getSessionId.invoke(session);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get chat session of " + getName(), e);
return null;
}
}
@Override
public Channel getChannel() {
try {
if (NMSStorage.getInstance().CHANNEL != null)
return (Channel) NMSStorage.getInstance().CHANNEL.get(NMSStorage.getInstance().NETWORK_MANAGER.get(playerConnection));
} catch (IllegalAccessException e) {
TAB.getInstance().getErrorManager().printError("Failed to get channel of " + getName(), e);
}
return null;
@NotNull
public String getDisplayName() {
return getPlayer().getDisplayName();
}
}
@@ -0,0 +1,88 @@
package me.neznamy.tab.platforms.bukkit;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.chat.EnumChatFormat;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* Class containing static util methods used on Bukkit.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BukkitUtils {
/** Whether compatibility exceptions should be printed or not, enabling when adding support for new versions */
public static final boolean PRINT_EXCEPTIONS = false;
private static boolean compatibilityIssue;
/**
* 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]);
}
}
/**
* Prints a console warn that some compatibility issue was found.
*
* @param exception
* Exception thrown when initializing fields, if applicable
* @param failedCheck
* Check that failed
* @param fallback
* Fallback implementation, if available
* @param missingFeatures
* Features that will be broken because of the incompatibility
*/
public static void compatibilityError(@NotNull Exception exception, @NotNull String failedCheck,
@Nullable String fallback, @NotNull String... missingFeatures) {
StringBuilder sb = new StringBuilder();
sb.append(EnumChatFormat.RED);
sb.append("[TAB] Failed to initialize minecraft fields for ");
sb.append(failedCheck);
sb.append(" due to a compatibility error. ");
if (fallback != null) {
sb.append("Using fallback solution using ").append(fallback).append(". ");
} else {
sb.append("No fallback solution was found. ");
}
if (missingFeatures.length > 0) sb.append("This will result in: ");
for (int i=0; i<missingFeatures.length; i++) {
sb.append("\n").append("#").append(i + 1).append(": ").append(missingFeatures[i]);
}
Bukkit.getConsoleSender().sendMessage(sb.toString());
if (PRINT_EXCEPTIONS) exception.printStackTrace();
compatibilityIssue = true;
}
/**
* Sends a message asking user to update the plugin if some compatibility issue was found.
*/
public static void sendCompatibilityMessage() {
if (!compatibilityIssue) return;
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.RED + "[TAB] Please update the plugin to " +
"a version with native support for your server version for optimal experience. This plugin version " +
"was made for " + ProtocolVersion.V1_5.getFriendlyName() + " - " + ProtocolVersion.LATEST_KNOWN_VERSION.getFriendlyName() + ".");
}
}
@@ -1,139 +0,0 @@
package me.neznamy.tab.platforms.bukkit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.platforms.bukkit.nms.*;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import org.jetbrains.annotations.NotNull;
/**
* Main class for Bukkit platform
*/
public class Main extends JavaPlugin {
@Override
public void onEnable(){
String version = Bukkit.getBukkitVersion().split("-")[0];
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Server version: " + version + " (" + serverPackage + ")"));
if (!isVersionSupported()){
Bukkit.getPluginManager().disablePlugin(this);
return;
}
BukkitPlatform platform = new BukkitPlatform(this);
TAB.setInstance(new TAB(platform, ProtocolVersion.fromFriendlyName(version), version + " (" + serverPackage + ")", getDataFolder(), getLogger()));
if (TAB.getInstance().getServerVersion() == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Unknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
}
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(platform), this);
TAB.getInstance().load();
Metrics metrics = new Metrics(this, 5304);
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.UNLIMITED_NAME_TAG_MODE_ENABLED, () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PLACEHOLDER_API, () -> Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.PERMISSION_SYSTEM, () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.SERVER_VERSION, () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
PluginCommand cmd = Bukkit.getPluginCommand("tab");
if (cmd == null) return;
TABCommand command = new TABCommand();
cmd.setExecutor(command);
cmd.setTabCompleter(command);
}
@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 and version is marked as compatible,
* {@code false} if anything went wrong or version is not marked as compatible.
*
* @return {@code true} if server version is compatible, {@code false} if not
*/
private boolean isVersionSupported(){
List<String> supportedVersions = Arrays.asList(
"v1_5_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3",
"v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2", "v1_8_R3",
"v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1", "v1_13_R1", "v1_13_R2",
"v1_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1", "v1_18_R1",
"v1_18_R2", "v1_19_R1", "v1_19_R2");
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
try {
long time = System.currentTimeMillis();
int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
if (minorVersion >= 17) {
try {
NMSStorage.setInstance(new BukkitModernNMSStorage());
} catch (ClassNotFoundException e) {
e.printStackTrace();
NMSStorage.setInstance(new MojangModernNMSStorage());
}
} else {
NMSStorage.setInstance(new LegacyNMSStorage());
}
if (supportedVersions.contains(serverPackage)) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
return true;
} else {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on 1.5.2 - 1.19.3. Disabling just to stay safe."));
}
} catch (Exception ex) {
if (supportedVersions.contains(serverPackage)) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report the error below (include your server version & fork too)"));
getLogger().log(Level.SEVERE, "", ex);
} else {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.2 - 1.19.3. Disabling."));
}
}
return false;
}
/**
* Command handler for /tab command
*/
private static class TABCommand implements CommandExecutor, TabCompleter {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (TabAPI.getInstance().isPluginDisabled()) {
for (String message : TAB.getInstance().getDisabledCommand().execute(args, sender.hasPermission(TabConstants.Permission.COMMAND_RELOAD), sender.hasPermission(TabConstants.Permission.COMMAND_ALL))) {
sender.sendMessage(EnumChatFormat.color(message));
}
} else {
TabPlayer p = null;
if (sender instanceof Player) {
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
if (p == null) return true; //player not loaded correctly
}
TAB.getInstance().getCommand().execute(p, args);
}
return false;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
TabPlayer p = null;
if (sender instanceof Player) {
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
if (p == null) return new ArrayList<>(); //player not loaded correctly
}
return TAB.getInstance().getCommand().complete(p, args);
}
}
}
@@ -0,0 +1,31 @@
package me.neznamy.tab.platforms.bukkit.bossbar;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.platform.BossBar;
import org.jetbrains.annotations.NotNull;
/**
* Class for finding BossBar implementation based on server and client version.
*/
public class BossBarLoader {
/**
* Finds best available instance for given player.
*
* @param player
* Player to find instance for
* @return BossBar instance for player
*/
@NotNull
public static BossBar findInstance(@NotNull BukkitTabPlayer player) {
// 1.9+ server, handle using API, potential 1.8 players are handled by ViaVersion
if (BukkitReflection.getMinorVersion() >= 9) return new BukkitBossBar(player);
// 1.9+ player on 1.8 server, handle using ViaVersion API
if (player.getVersion().getMinorVersion() >= 9) return new ViaBossBar(player);
// 1.8- server and player, use wither
return new EntityBossBar(player);
}
}
@@ -0,0 +1,71 @@
package me.neznamy.tab.platforms.bukkit.bossbar;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.platform.BossBar;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* BossBar for 1.9+ servers where Bukkit API is used. If ViaVersion is used
* to allow 1.8 players, it will handle the entity and teleporting by itself.
*/
@RequiredArgsConstructor
public class BukkitBossBar implements BossBar {
/** Style array for fast access */
private static final org.bukkit.boss.BarStyle[] styles = org.bukkit.boss.BarStyle.values();
/** 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(
player.getPlatform().toBukkitFormat(TabComponent.optimized(title), player.getVersion().supportsRGB()),
org.bukkit.boss.BarColor.valueOf(color.name()),
styles[style.ordinal()]
);
bar.setProgress(progress);
bar.addPlayer(player.getPlayer());
bossBars.put(id, bar);
}
@Override
public void update(@NotNull UUID id, @NotNull String title) {
bossBars.get(id).setTitle(player.getPlatform().toBukkitFormat(TabComponent.optimized(title), player.getVersion().supportsRGB()));
}
@Override
public void update(@NotNull UUID id, float progress) {
bossBars.get(id).setProgress(progress);
}
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
bossBars.get(id).setStyle(styles[style.ordinal()]);
}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
bossBars.get(id).setColor(org.bukkit.boss.BarColor.valueOf(color.name()));
}
@Override
public void remove(@NotNull UUID id) {
bossBars.remove(id).removePlayer(player.getPlayer());
}
}
@@ -0,0 +1,74 @@
package me.neznamy.tab.platforms.bukkit.bossbar;
import lombok.RequiredArgsConstructor;
import me.neznamy.tab.shared.platform.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.entity.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 {
/** Max health of wither */
private static final int WITHER_MAX_HEALTH = 300;
/** Ideal value for invulnerable time for minimal wither size */
private static final int IDEAL_INVULNERABLE_TIME = 880;
/** Flag making entity invisible */
private static final byte INVISIBLE_FLAG = 1 << 5;
/** 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 = WITHER_MAX_HEALTH*progress;
if (health == 0) health = 1;
w.setHealth(health);
w.setCustomName(title, player.getVersion());
w.setEntityFlags(INVISIBLE_FLAG);
w.setWitherInvulnerableTime(IDEAL_INVULNERABLE_TIME);
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 = WITHER_MAX_HEALTH*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());
}
}
@@ -0,0 +1,72 @@
package me.neznamy.tab.platforms.bukkit.bossbar;
import com.viaversion.viaversion.api.Via;
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.TabComponent;
import me.neznamy.tab.shared.platform.BossBar;
import me.neznamy.tab.api.bossbar.BarColor;
import me.neznamy.tab.api.bossbar.BarStyle;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
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 {
/** Style array for fast access */
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(
TabComponent.optimized(title).toLegacyText(),
progress,
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(TabComponent.optimized(title).toLegacyText());
}
@Override
public void update(@NotNull UUID id, float progress) {
viaBossBars.get(id).setHealth(progress);
}
@Override
public void update(@NotNull UUID id, @NotNull BarStyle style) {
viaBossBars.get(id).setStyle(styles[style.ordinal()]);
}
@Override
public void update(@NotNull UUID id, @NotNull BarColor color) {
viaBossBars.get(id).setColor(BossColor.valueOf(color.name()));
}
@Override
public void remove(@NotNull UUID id) {
viaBossBars.remove(id).removePlayer(player.getPlayer().getUniqueId());
}
}
@@ -0,0 +1,273 @@
package me.neznamy.tab.platforms.bukkit.entity;
import lombok.*;
import me.neznamy.tab.platforms.bukkit.nms.ComponentConverter;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.Limitations;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.backend.EntityData;
import me.neznamy.tab.shared.chat.TabComponent;
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.*;
/**
* A class representing the n.m.s.DataWatcher class to make work with it much easier
*/
@ToString
public class DataWatcher implements EntityData {
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 = EntityData.getArmorStandFlagsPosition(BukkitReflection.getMinorVersion());
/** 1.19.3+ */
private static Constructor<?> newDataWatcher$Item;
/** 1.19.2- */
public static Class<?> DataWatcher;
private static Constructor<?> newDataWatcher;
private static Method DataWatcher_register;
private static Constructor<?> newDataWatcherObject;
/** Watched data */
private final Map<Integer, Item> dataValues = new HashMap<>();
/**
* Loads all required Fields and throws Exception if something went wrong.
*
* @throws ReflectiveOperationException
* If something goes wrong
*/
public static void load() throws ReflectiveOperationException {
int minorVersion = BukkitReflection.getMinorVersion();
if (minorVersion >= 9) {
loadSerializers();
}
if (BukkitReflection.is1_19_3Plus()) {
Class<?> dataWatcher$Item = BukkitReflection.getClass("network.syncher.SynchedEntityData$DataValue", "network.syncher.DataWatcher$c", "network.syncher.DataWatcher$b");
Class<?> dataWatcherSerializer = BukkitReflection.getClass("network.syncher.EntityDataSerializer",
"network.syncher.DataWatcherSerializer", "DataWatcherSerializer");
newDataWatcher$Item = dataWatcher$Item.getConstructor(int.class, dataWatcherSerializer, Object.class);
} else {
DataWatcher = BukkitReflection.getClass("network.syncher.SynchedEntityData", "network.syncher.DataWatcher", "DataWatcher");
if (minorVersion >= 7) {
ComponentConverter.ensureAvailable();
newDataWatcher = DataWatcher.getConstructor(BukkitReflection.getClass("world.entity.Entity", "Entity"));
} else {
newDataWatcher = DataWatcher.getConstructor();
}
if (minorVersion >= 9) {
Class<?> dataWatcherObject = BukkitReflection.getClass("network.syncher.EntityDataAccessor",
"network.syncher.DataWatcherObject", "DataWatcherObject");
Class<?> dataWatcherSerializer = BukkitReflection.getClass("network.syncher.EntityDataSerializer",
"network.syncher.DataWatcherSerializer", "DataWatcherSerializer");
DataWatcher_register = ReflectionUtils.getMethod(
DataWatcher,
new String[]{"define", "register", "a", "m_135372_"}, // {Mojang, Bukkit, Bukkit 1.18+, Mohist 1.18.2}
dataWatcherObject, Object.class
);
newDataWatcherObject = dataWatcherObject.getConstructor(int.class, dataWatcherSerializer);
} else {
DataWatcher_register = ReflectionUtils.getMethod(
DataWatcher,
new String[]{"func_75682_a", "a"}, int.class, // {Thermos 1.7.10, Bukkit}
Object.class
);
}
}
}
private static void loadSerializers() throws ReflectiveOperationException {
Class<?> dataWatcherRegistry = BukkitReflection.getClass("network.syncher.EntityDataSerializers",
"network.syncher.DataWatcherRegistry", "DataWatcherRegistry");
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 (BukkitReflection.getMinorVersion() >= 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(TabComponent.optimized(customName).convert(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() > Limitations.BOSSBAR_NAME_LENGTH_1_7 ?
customName.substring(0, Limitations.BOSSBAR_NAME_LENGTH_1_7) : 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
@NotNull
public Object build() {
if (BukkitReflection.is1_19_3Plus()) {
List<Object> items = new ArrayList<>();
for (Item item : dataValues.values()) {
items.add(newDataWatcher$Item.newInstance(item.position, item.serializer, item.value));
}
return items;
} else {
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());
}
return nmsWatcher;
}
}
/**
* Class for specific data watcher item.
*/
@RequiredArgsConstructor
@Getter
private static class Item {
/** Item position */
private final int position;
/** Item serializer (1.9+) */
@Nullable
private final Object serializer;
/** Item value */
@NonNull
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;
}
}
}
}
@@ -0,0 +1,432 @@
package me.neznamy.tab.platforms.bukkit.entity;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
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.platforms.bukkit.nms.PacketSender;
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.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.function.Function;
/**
* EntityView implementation for Bukkit using packets.
*/
@RequiredArgsConstructor
@SuppressWarnings("unchecked")
public class PacketEntityView implements EntityView {
/** Version with entity metadata being split from spawn packet */
private static final int SPLIT_METADATA_VERSION = 15;
private static PacketSender packetSender;
/** PacketPlayOutEntityDestroy */
private static Class<?> EntityDestroyClass;
private static BiConsumerWithException<BukkitTabPlayer, int[]> destroyEntities;
private static FunctionWithException<Object, int[]> getDestroyedEntities;
/** PacketPlayOutEntityMetadata */
private static BiFunctionWithException<Integer, EntityData, Object> newEntityMetadata;
/** PacketPlayOutEntityTeleport */
private static BiFunctionWithException<Integer, Location, Object> newEntityTeleport;
private static Class<?> EntityTeleportClass;
private static Field EntityTeleport_EntityId;
/** PacketPlayOutSpawnEntityLiving */
private static QuintFunction<Integer, UUID, Object, Location, EntityData, Object> newSpawnEntity;
/** Other entity packets */
private static Class<?> PacketPlayOutEntity;
private static Field PacketPlayOutEntity_ENTITYID;
private static Field PacketPlayOutEntity_X;
private static Field PacketPlayOutEntity_Y;
private static Field PacketPlayOutEntity_Z;
private static Class<?> PacketPlayOutEntityLook;
private static QuadFunction<Integer, Long, Long, Long, Object> newMovePacket;
private static Class<?> PacketPlayOutNamedEntitySpawn;
private static Field PacketPlayOutNamedEntitySpawn_ENTITYID;
private static Constructor<?> newClientboundBundlePacket;
private static Field ClientboundBundlePacket_packets;
private static Function<Object, Boolean> isBundlePacket = packet -> false;
private static BiConsumerWithException<BukkitTabPlayer, Iterable<?>> sendAsBundle = (player, packets) -> {
for (Object packet : packets) {
packetSender.sendPacket(player.getPlayer(), packet);
}
};
@Getter
private static boolean available;
/** Player this view belongs to */
private final BukkitTabPlayer player;
/**
* Loads all required classes and fields and marks class as available.
* If something goes wrong, a warning is printed and class is not marked as available.
*/
public static void tryLoad() {
try {
DataWatcher.load();
loadEntityMetadata();
loadEntityDestroy();
loadEntityTeleport();
loadEntityMove();
loadEntitySpawn();
if (BukkitReflection.is1_19_4Plus()) {
Class<?> ClientboundBundlePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundBundlePacket");
newClientboundBundlePacket = ClientboundBundlePacket.getConstructor(Iterable.class);
ClientboundBundlePacket_packets = ReflectionUtils.getOnlyField(ClientboundBundlePacket.getSuperclass(), Iterable.class);
isBundlePacket = ClientboundBundlePacket::isInstance;
sendAsBundle = (player, packets) -> packetSender.sendPacket(player.getPlayer(), newClientboundBundlePacket.newInstance(packets));
}
packetSender = new PacketSender();
available = true;
} catch (Exception e) {
List<String> missingFeatures = new ArrayList<>();
if (BukkitReflection.getMinorVersion() >= 8) {
missingFeatures.add("Unlimited nametag mode not working and being replaced with regular nametags");
}
if (BukkitReflection.getMinorVersion() <= 8) {
missingFeatures.add("BossBar feature not working");
}
BukkitUtils.compatibilityError(e, "sending entity packets", null, missingFeatures.toArray(new String[0]));
}
}
@SuppressWarnings("JavaReflectionInvocation")
private static void loadEntityMetadata() throws ReflectiveOperationException {
Class<?> entityMetadataClass = BukkitReflection.getClass("network.protocol.game.ClientboundSetEntityDataPacket",
"network.protocol.game.PacketPlayOutEntityMetadata", "PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
if (BukkitReflection.is1_19_3Plus()) {
Constructor<?> constructor = entityMetadataClass.getConstructor(int.class, List.class);
newEntityMetadata = (entityId, data) -> constructor.newInstance(entityId, data.build());
} else {
Constructor<?> constructor = entityMetadataClass.getConstructor(int.class, DataWatcher.DataWatcher, boolean.class);
newEntityMetadata = (entityId, data) -> constructor.newInstance(entityId, data.build(), true);
}
}
private static void loadEntityDestroy() throws ReflectiveOperationException {
EntityDestroyClass = BukkitReflection.getClass("network.protocol.game.ClientboundRemoveEntitiesPacket",
"network.protocol.game.PacketPlayOutEntityDestroy", "PacketPlayOutEntityDestroy", "Packet29DestroyEntity");
Field entities = ReflectionUtils.getOnlyField(EntityDestroyClass);
try {
Constructor<?> constructor = EntityDestroyClass.getConstructor(int[].class);
destroyEntities = (player, ids) -> packetSender.sendPacket(player.getPlayer(), constructor.newInstance(new Object[]{ids}));
if (BukkitReflection.getMinorVersion() >= 17) {
getDestroyedEntities = packet -> ((List<Integer>) entities.get(packet)).stream().mapToInt(i -> i).toArray();
} else {
getDestroyedEntities = packet -> (int[]) entities.get(packet);
}
} catch (NoSuchMethodException e) {
//1.17.0 Mojank
Constructor<?> constructor = EntityDestroyClass.getConstructor(int.class);
destroyEntities = (player, ids) -> { for (int entity : ids) packetSender.sendPacket(player.getPlayer(), constructor.newInstance(entity));};
getDestroyedEntities = packet -> new int[]{entities.getInt(packet)};
}
}
private static void loadEntityTeleport() throws ReflectiveOperationException {
EntityTeleportClass = BukkitReflection.getClass("network.protocol.game.ClientboundTeleportEntityPacket",
"network.protocol.game.PacketPlayOutEntityTeleport", "PacketPlayOutEntityTeleport", "Packet34EntityTeleport");
Callable<Object> newPacket;
if (BukkitReflection.getMinorVersion() >= 17) {
// Dummy armor stand for constructor
Class<?> world = BukkitReflection.getClass("world.level.Level", "world.level.World", "World");
Class<?> entityArmorStand = BukkitReflection.getClass("world.entity.decoration.ArmorStand",
"world.entity.decoration.EntityArmorStand", "EntityArmorStand");
Constructor<?> newEntityArmorStand = entityArmorStand.getConstructor(world, double.class, double.class, double.class);
Method World_getHandle = BukkitReflection.getBukkitClass("CraftWorld").getMethod("getHandle");
Object dummyEntity = newEntityArmorStand.newInstance(World_getHandle.invoke(Bukkit.getWorlds().get(0)), 0, 0, 0);
Constructor<?> constructor = EntityTeleportClass.getConstructor(BukkitReflection.getClass("world.entity.Entity"));
newPacket = () -> constructor.newInstance(dummyEntity);
} else {
Constructor<?> constructor = EntityTeleportClass.getConstructor();
newPacket = constructor::newInstance;
}
EntityTeleport_EntityId = ReflectionUtils.getFields(EntityTeleportClass, int.class).get(0);
Field EntityTeleport_X;
Field EntityTeleport_Y;
Field EntityTeleport_Z;
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);
}
newEntityTeleport = (entityId, location) -> {
Object packet = newPacket.call();
EntityTeleport_EntityId.set(packet, entityId);
EntityTeleport_X.set(packet, toPosition(location.getX()));
EntityTeleport_Y.set(packet, toPosition(location.getY()));
EntityTeleport_Z.set(packet, toPosition(location.getZ()));
return packet;
};
}
private static void loadEntityMove() throws ReflectiveOperationException {
// Classes
PacketPlayOutEntity = BukkitReflection.getClass("network.protocol.game.ClientboundMoveEntityPacket",
"network.protocol.game.PacketPlayOutEntity", "PacketPlayOutEntity", "Packet30Entity");
PacketPlayOutEntityLook = BukkitReflection.getClass("network.protocol.game.ClientboundMoveEntityPacket$Rot",
"network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntity$PacketPlayOutEntityLook",
"PacketPlayOutEntityLook", "Packet32EntityLook");
Class<?> packetPlayOutRelEntityMove = BukkitReflection.getClass("network.protocol.game.ClientboundMoveEntityPacket$Pos",
"network.protocol.game.PacketPlayOutEntity$PacketPlayOutRelEntityMove", "PacketPlayOutEntity$PacketPlayOutRelEntityMove",
"PacketPlayOutRelEntityMove", "Packet31RelEntityMove");
// Fields
PacketPlayOutEntity_ENTITYID = ReflectionUtils.getFields(PacketPlayOutEntity, int.class).get(0);
if (BukkitReflection.getMinorVersion() >= 14) {
List<Field> fields = ReflectionUtils.getFields(PacketPlayOutEntity, short.class);
PacketPlayOutEntity_X = fields.get(0);
PacketPlayOutEntity_Y = fields.get(1);
PacketPlayOutEntity_Z = fields.get(2);
Constructor<?> constructor = packetPlayOutRelEntityMove.getConstructor(int.class, short.class, short.class, short.class, boolean.class);
newMovePacket = (entityId, x, y, z) -> constructor.newInstance(entityId, x.shortValue(), y.shortValue(), z.shortValue(), false);
} else if (BukkitReflection.getMinorVersion() >= 9) {
List<Field> fields = ReflectionUtils.getFields(PacketPlayOutEntity, int.class);
PacketPlayOutEntity_X = fields.get(1);
PacketPlayOutEntity_Y = fields.get(2);
PacketPlayOutEntity_Z = fields.get(3);
Constructor<?> constructor = packetPlayOutRelEntityMove.getConstructor(int.class, long.class, long.class, long.class, boolean.class);
newMovePacket = (entityId, x, y, z) -> constructor.newInstance(entityId, x, y, z, false);
} else if (BukkitReflection.getMinorVersion() == 8) {
List<Field> fields = ReflectionUtils.getFields(PacketPlayOutEntity, byte.class);
PacketPlayOutEntity_X = fields.get(0);
PacketPlayOutEntity_Y = fields.get(1);
PacketPlayOutEntity_Z = fields.get(2);
Constructor<?> constructor = packetPlayOutRelEntityMove.getConstructor(int.class, byte.class, byte.class, byte.class, boolean.class);
newMovePacket = (entityId, x, y, z) -> constructor.newInstance(entityId, x.byteValue(), y.byteValue(), z.byteValue(), false);
}
}
/**
* Loads required NMS classes, fields and methods. If it fails, exception is thrown.
*
* @throws ReflectiveOperationException
* If anything fails
*/
private static void loadEntitySpawn() throws ReflectiveOperationException {
int minorVersion = BukkitReflection.getMinorVersion();
// Entity ids
EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
if (minorVersion >= 13) {
entityIds.put(EntityType.ARMOR_STAND, 1);
} else {
entityIds.put(EntityType.WITHER, 64);
if (minorVersion >= 8) {
entityIds.put(EntityType.ARMOR_STAND, 30);
}
}
Class<?> SpawnEntityClass = BukkitReflection.getClass("network.protocol.game.ClientboundAddEntityPacket",
"network.protocol.game.PacketPlayOutSpawnEntity", "PacketPlayOutSpawnEntityLiving", "Packet24MobSpawn");
Field SpawnEntity_EntityId = ReflectionUtils.getFields(SpawnEntityClass, int.class).get(0);
if (BukkitReflection.is1_20_2Plus()) {
PacketPlayOutNamedEntitySpawn = SpawnEntityClass;
PacketPlayOutNamedEntitySpawn_ENTITYID = SpawnEntity_EntityId;
} else {
PacketPlayOutNamedEntitySpawn = BukkitReflection.getClass("network.protocol.game.ClientboundAddPlayerPacket",
"network.protocol.game.PacketPlayOutNamedEntitySpawn", "PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
PacketPlayOutNamedEntitySpawn_ENTITYID = ReflectionUtils.getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
}
Constructor<?> constructor;
// 1.17+ constructor using Spawn entity packet
if (minorVersion >= 17) {
Class<?> Vec3D = BukkitReflection.getClass("world.phys.Vec3", "world.phys.Vec3D");
Object Vec3D_Empty = ReflectionUtils.getOnlyField(Vec3D, Vec3D).get(null);
Class<?> EntityTypes = BukkitReflection.getClass("world.entity.EntityType", "world.entity.EntityTypes");
if (minorVersion >= 19) {
Object EntityTypes_ARMOR_STAND = ReflectionUtils.getField(EntityTypes, "ARMOR_STAND", "d").get(null);
constructor = SpawnEntityClass.getConstructor(int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, EntityTypes, int.class, Vec3D, double.class);
newSpawnEntity = (id, uuid, type, l, data) -> constructor.newInstance(id, uuid, l.getX(), l.getY(), l.getZ(), 0, 0, EntityTypes_ARMOR_STAND, 0, Vec3D_Empty, 0.0d);
} else {
Object EntityTypes_ARMOR_STAND = ReflectionUtils.getField(EntityTypes, "ARMOR_STAND", "c", "f_20529_").get(null); // Mohist 1.18.2
constructor = SpawnEntityClass.getConstructor(int.class, UUID.class, double.class, double.class, double.class, float.class, float.class, EntityTypes, int.class, Vec3D);
newSpawnEntity = (id, uuid, type, l, data) -> constructor.newInstance(id, uuid, l.getX(), l.getY(), l.getZ(), 0, 0, EntityTypes_ARMOR_STAND, 0, Vec3D_Empty);
}
return;
}
// 1.16- constructor using spawn entity living packet
constructor = SpawnEntityClass.getConstructor();
@Nullable Field SpawnEntity_UUID;
Field SpawnEntity_X;
Field SpawnEntity_Y;
Field SpawnEntity_Z;
@Nullable Field SpawnEntity_DataWatcher;
Field SpawnEntity_EntityType = ReflectionUtils.getFields(SpawnEntityClass, int.class).get(1);
if (minorVersion >= 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_UUID = null;
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);
}
if (minorVersion < SPLIT_METADATA_VERSION) {
SpawnEntity_DataWatcher = ReflectionUtils.getOnlyField(SpawnEntityClass, DataWatcher.DataWatcher);
} else {
SpawnEntity_DataWatcher = null;
}
newSpawnEntity = (id, uuid, type, l, data) -> {
Object nmsPacket = constructor.newInstance();
SpawnEntity_EntityId.set(nmsPacket, id);
if (minorVersion < SPLIT_METADATA_VERSION) SpawnEntity_DataWatcher.set(nmsPacket, data.build());
if (minorVersion >= 9) SpawnEntity_UUID.set(nmsPacket, uuid);
SpawnEntity_X.set(nmsPacket, toPosition(l.getX()));
SpawnEntity_Y.set(nmsPacket, toPosition(l.getY()));
SpawnEntity_Z.set(nmsPacket, toPosition(l.getZ()));
SpawnEntity_EntityType.set(nmsPacket, entityIds.get((EntityType) type));
return nmsPacket;
};
}
private static Object toPosition(double paramDouble) {
if (BukkitReflection.getMinorVersion() >= 9) {
return paramDouble;
} else {
int i = (int) (paramDouble*32);
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) {
if (BukkitReflection.getMinorVersion() >= SPLIT_METADATA_VERSION) {
sendAsBundle.accept(player, Arrays.asList(
newSpawnEntity.apply(entityId, id, entityType, l, null),
newEntityMetadata.apply(entityId, data)
));
} else {
packetSender.sendPacket(player.getPlayer(), newSpawnEntity.apply(entityId, id, entityType, l, data));
}
}
@Override
@SneakyThrows
public void updateEntityMetadata(int entityId, @NotNull EntityData data) {
packetSender.sendPacket(player.getPlayer(), newEntityMetadata.apply(entityId, data));
}
@SneakyThrows
@Override
public void teleportEntity(int entityId, @NotNull Location location) {
packetSender.sendPacket(player.getPlayer(), newEntityTeleport.apply(entityId, location));
}
@SneakyThrows
@Override
public void destroyEntities(int... entities) {
destroyEntities.accept(player, entities);
}
@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) {
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) {
return PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(playerSpawnPacket);
}
@Override
@SneakyThrows
public int[] getDestroyedEntities(@NotNull Object destroyPacket) {
return getDestroyedEntities.apply(destroyPacket);
}
@Override
public boolean isBundlePacket(@NotNull Object packet) {
return isBundlePacket.apply(packet);
}
@Override
@SneakyThrows
public Iterable<Object> getPackets(@NotNull Object bundlePacket) {
return (Iterable<Object>) ClientboundBundlePacket_packets.get(bundlePacket);
}
@Override
@SneakyThrows
@NotNull
public Location getMoveDiff(@NotNull Object movePacket) {
return new Location(
((Number)PacketPlayOutEntity_X.get(movePacket)).intValue(),
((Number)PacketPlayOutEntity_Y.get(movePacket)).intValue(),
((Number)PacketPlayOutEntity_Z.get(movePacket)).intValue()
);
}
@Override
@SneakyThrows
public void moveEntity(int entityId, @NotNull Location moveDiff) {
packetSender.sendPacket(player.getPlayer(), newMovePacket.apply(
entityId, (long) moveDiff.getX(), (long) moveDiff.getY(), (long) moveDiff.getZ()));
}
}
@@ -0,0 +1,195 @@
package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.platforms.bukkit.BukkitTabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.platforms.bukkit.entity.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.Locale;
import java.util.stream.Collectors;
/**
* The core class for unlimited NameTag mode on Bukkit
*/
public class BukkitNameTagX extends BackendNameTagX implements Listener {
/** Version in which multiple passenger option was added */
private static final int MULTI_PASSENGER_VERSION = 11;
/**
* Constructs new instance and registers the class listener.
*
* @param plugin
* Plugin instance to use for registering listener
*/
public BukkitNameTagX(@NotNull JavaPlugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
}
/**
* Listener to player sneak event to sneak armor stands.
*
* @param e
* Sneak event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSneak(PlayerToggleSneakEvent e) {
sneak(e.getPlayer().getUniqueId(), e.isSneaking());
}
/**
* Listener to respawn event to resend armor stands.
*
* @param e
* Respawn event
*/
@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 (BukkitReflection.getMinorVersion() >= MULTI_PASSENGER_VERSION) {
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(Locale.US);
}
@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 (BukkitReflection.getMinorVersion() >= 14 && p.getPose() == Pose.SWIMMING) return true;
return BukkitReflection.getMinorVersion() == 13 && p.isSwimming();
}
@Override
public boolean isGliding(@NotNull TabPlayer player) {
return BukkitReflection.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(datawatcher.MARKER_FLAG);
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();
}
@Override
public boolean isFlying(@NotNull TabPlayer player) {
return ((BukkitTabPlayer)player).getPlayer().isFlying();
}
}
@@ -1,73 +1,119 @@
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.api.TabAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.TabExpansion;
import me.neznamy.tab.shared.platform.TabPlayer;
import me.neznamy.tab.shared.placeholders.expansion.TabExpansion;
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.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.*;
/**
* 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 */
private final Map<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
/** List of all placeholders offered by the plugin for command suggestions */
@NotNull
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(){
public boolean persist() {
return true;
}
@Override
public @NotNull String getAuthor(){
return "NEZNAMY";
@NotNull
public String getAuthor() {
return TabConstants.PLUGIN_AUTHOR;
}
@Override
public @NotNull String getIdentifier(){
return "tab";
@NotNull
public String getIdentifier() {
return TabConstants.PLUGIN_ID;
}
@Override
public @NotNull String getVersion() {
@NotNull
public String getVersion() {
return TabConstants.PLUGIN_VERSION;
}
@Override
public String onPlaceholderRequest(Player player, @NotNull String identifier){
@NotNull
public List<String> getPlaceholders() {
return placeholders;
}
@Override
@Nullable
public String onPlaceholderRequest(@Nullable Player player, @NotNull String identifier) {
if (identifier.startsWith("replace_")) {
String text = "%" + identifier.substring(8) + "%";
String textBefore;
do {
textBefore = text;
for (String placeholder : TAB.getInstance().getPlaceholderManager().detectPlaceholders(text)) {
text = text.replace(placeholder, TabAPI.getInstance().getPlaceholderManager().findReplacement(placeholder,
text = text.replace(placeholder, TAB.getInstance().getPlaceholderManager().findReplacement(placeholder,
PlaceholderAPI.setPlaceholders(player, placeholder)));
}
} while (!textBefore.equals(text));
return text;
}
if (identifier.startsWith("placeholder_")) {
TabAPI.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", (TabFeature) TabAPI.getInstance().getPlaceholderManager());
TAB.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring("placeholder_".length()) + "%", TAB.getInstance().getPlaceholderManager());
}
if (player == null) return "<Player cannot be null>";
TabPlayer p = TabAPI.getInstance().getPlayer(player.getUniqueId());
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (p == null || !p.isLoaded()) return "<Player is not loaded>";
return values.get(p).get(identifier);
return p.expansionValues.values.get(identifier);
}
@Override
public void setValue(TabPlayer player, String key, String value) {
values.computeIfAbsent(player, p -> new HashMap<>()).put(key, value);
public void setValue(@NotNull TabPlayer player, @NotNull String key, @NotNull String value) {
player.expansionValues.values.put(key, value);
}
@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);
}
}
}
@@ -5,7 +5,9 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.platforms.bukkit.BukkitUtils;
import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.UnLoadable;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -15,52 +17,55 @@ import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabConstants;
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;
/**
* Per-world-PlayerList feature handler
*/
@SuppressWarnings("deprecation")
public class PerWorldPlayerList extends TabFeature implements Listener {
/** Reference to main class */
private final JavaPlugin plugin;
public class PerWorldPlayerList extends TabFeature implements Listener, Loadable, UnLoadable {
/** Config options */
private final boolean allowBypass = TabAPI.getInstance().getConfig().getBoolean("per-world-playerlist.allow-bypass-permission", false);
private final List<String> ignoredWorlds = TabAPI.getInstance().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
private final Map<String, List<String>> sharedWorlds = TabAPI.getInstance().getConfig().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
private final boolean allowBypass = config().getBoolean("per-world-playerlist.allow-bypass-permission", false);
private final List<String> ignoredWorlds = config().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignored_world", "build"));
private final Map<String, List<String>> sharedWorlds = config().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
/**
* Constructs new instance with given parameters and loads config options
* Constructs new instance and registers events
*
* @param plugin
* plugin instance
* Plugin instance to register events
*/
public PerWorldPlayerList(JavaPlugin plugin) {
super("Per world PlayerList", null);
this.plugin = plugin;
Bukkit.getPluginManager().registerEvents(this, plugin);
TabAPI.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
}
@Override
public void load(){
Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getOnlinePlayers().forEach(this::checkPlayer));
public void load() {
for (Player p : BukkitUtils.getOnlinePlayers()) {
checkPlayer(p);
}
}
@Override
public void unload(){
for (Player p : Bukkit.getOnlinePlayers()) {
for (Player pl : Bukkit.getOnlinePlayers()) {
public void unload() {
for (Player p : BukkitUtils.getOnlinePlayers()) {
for (Player pl : BukkitUtils.getOnlinePlayers()) {
p.showPlayer(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();
@@ -68,6 +73,12 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
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();
@@ -83,8 +94,8 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
* @param p
* Player to update
*/
private void checkPlayer(Player p) {
for (Player all : Bukkit.getOnlinePlayers()){
private void checkPlayer(@NotNull Player p) {
for (Player all : BukkitUtils.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);
@@ -101,7 +112,7 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
* Target displayed in the TabList
* @return {@code true} if viewer should see target, {@code false} if not.
*/
private boolean shouldSee(Player viewer, Player target) {
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;
String viewerWorldGroup = viewer.getWorld().getName() + "-default"; //preventing unwanted behavior when some group is called exactly like a world
@@ -114,4 +125,10 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
}
return viewerWorldGroup.equals(targetWorldGroup);
}
@Override
@NotNull
public String getFeatureName() {
return "Per world PlayerList";
}
}
@@ -1,158 +0,0 @@
package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Optional;
import java.util.WeakHashMap;
/**
* A feature to disable minecraft 1.9+ feature making tamed animals
* with custom names copy NameTag properties of their owner.
* This is achieved by listening to entity spawn (&lt;1.15) / entity metadata
* packets and removing owner field from the DataWatcher list.
* Since 1.16 this results in client sending entity use packet twice,
* so we must cancel the 2nd one to prevent double toggle.
*/
public class PetFix extends TabFeature {
/** NMS Storage reference for quick access */
private final NMSStorage nms = NMSStorage.getInstance();
/** DataWatcher position of pet owner field */
private final int petOwnerPosition = getPetOwnerPosition();
/** Logger of last interacts to prevent feature not working on 1.16 */
private final WeakHashMap<TabPlayer, Long> lastInteractFix = new WeakHashMap<>();
/**
* Since 1.16, client sends interact packet twice for entities affected
* by removed owner field. Because of that, we need to cancel the duplicated
* packet to avoid double toggle and preventing the entity from getting
* its pose changed. The duplicated packet is usually sent instantly in the
* same millisecond, however, when installing ProtocolLib with MyPet, the delay
* is up to 3 ticks. When holding right-click on an entity, interact is sent every
* 200 milliseconds, which is the value we should not go above. Optimal value is
* therefore between 150 and 200 milliseconds.
*/
private static final int INTERACT_COOLDOWN = 160;
/**
* Constructs new instance with given parameter
*/
public PetFix() {
super("Pet name fix", null);
TabAPI.getInstance().debug("Loaded PetFix feature");
}
/**
* Returns position of pet owner field based on server version
*
* @return position of pet owner field based on server version
*/
private int getPetOwnerPosition() {
if (nms.getMinorVersion() >= 17) {
//1.17.x, 1.18.x, 1.19.x
return 18;
} else if (nms.getMinorVersion() >= 15) {
//1.15.x, 1.16.x
return 17;
} else if (nms.getMinorVersion() >= 14) {
//1.14.x
return 16;
} else if (nms.getMinorVersion() >= 10) {
//1.10.x - 1.13.x
return 14;
} else {
//1.9.x
return 13;
}
}
/**
* Cancels a packet if previous one arrived with no delay to prevent double toggle since 1.16
*
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@Override
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
if (nms.PacketPlayInUseEntity.isInstance(packet) && isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
if (System.currentTimeMillis() - lastInteractFix.getOrDefault(sender, 0L) < INTERACT_COOLDOWN) {
//last interact packet was sent right now, cancelling to prevent double-toggle due to this feature enabled
return true;
}
//this is the first packet, saving player so the next packet can be cancelled
lastInteractFix.put(sender, System.currentTimeMillis());
}
return false;
}
/**
* Checks if the provided entity use action is INTERACT or not.
*
* @param action
* Action to check
* @return {@code true} if action is INTERACT, {@code false} if not.
*/
private boolean isInteract(Object action) {
if (nms.getMinorVersion() >= 17) {
return nms.PacketPlayInUseEntity$d.isInstance(action);
} else {
return action.toString().equals("INTERACT");
}
}
/**
* Removes pet owner field from DataWatcher
*
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@SuppressWarnings("unchecked")
@Override
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
if (nms.PacketPlayOutEntityMetadata.isInstance(packet)) {
Object removedEntry = null;
List<Object> items = (List<Object>) nms.PacketPlayOutEntityMetadata_LIST.get(packet);
if (items == null) return;
try {
for (Object item : items) {
if (item == null) continue;
int slot;
Object value;
if (nms.is1_19_3Plus()) {
slot = nms.DataWatcher$DataValue_POSITION.getInt(item);
value = nms.DataWatcher$DataValue_VALUE.get(item);
} else {
slot = nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item));
value = nms.DataWatcherItem_VALUE.get(item);
}
if (slot == petOwnerPosition) {
if (value instanceof Optional || value instanceof com.google.common.base.Optional) {
removedEntry = item;
}
}
}
} catch (ConcurrentModificationException e) {
//no idea how can this list change in another thread since it's created for the packet but whatever, try again
onPacketSend(receiver, packet);
}
if (removedEntry != null) items.remove(removedEntry);
} else if (nms.PacketPlayOutSpawnEntityLiving.isInstance(packet) && nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER != null) {
//<1.15
DataWatcher watcher = DataWatcher.fromNMS(nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.get(packet));
DataWatcherItem petOwner = watcher.getItem(petOwnerPosition);
if (petOwner != null && (petOwner.getValue() instanceof Optional || petOwner.getValue() instanceof com.google.common.base.Optional)) {
watcher.removeValue(petOwnerPosition);
nms.setField(packet, nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER, watcher.toNMS());
}
}
}
}
@@ -1,60 +1,64 @@
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.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.bossbar.BossBar;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
import org.jetbrains.annotations.NotNull;
/**
* An additional class with additional code for &lt;1.9 servers due to an entity being required
*/
public class WitherBossBar extends BossBarManagerImpl implements Listener {
@RequiredArgsConstructor
public class WitherBossBar extends BossBarManagerImpl implements Listener, WorldSwitchListener {
/** Distance of the Wither in blocks */
/** Distance of the wither in blocks */
private static final int WITHER_DISTANCE = 60;
/**
* Constructs a new instance of the class and registers events and task
* Constructs new instance and registers events
*
* @param plugin
* plugin instance
* 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,
this, TabConstants.CpuUsageCategory.TELEPORTING_WITHER, this::teleport);
}
@Override
public void load() {
getFeatureName(), TabConstants.CpuUsageCategory.TELEPORTING_WITHER, this::teleport);
super.load();
teleport();
}
/**
* Updates Wither's location for all online players
* 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.containsPlayer(p)) continue;
Location loc = ((Player) p.getPlayer()).getEyeLocation().add(((Player) p.getPlayer()).getEyeLocation().getDirection().normalize().multiply(WITHER_DISTANCE));
Location eyeLocation = ((BukkitTabPlayer)p).getPlayer().getEyeLocation();
Location loc = eyeLocation.add(eyeLocation.getDirection().normalize().multiply(WITHER_DISTANCE));
if (loc.getY() < 1) loc.setY(1);
p.sendCustomPacket(new PacketPlayOutEntityTeleport(line.getUniqueId().hashCode(), loc), TabConstants.PacketCategory.BOSSBAR_WITHER_TELEPORT);
((BackendTabPlayer)p).getEntityView().teleportEntity(line.getUniqueId().hashCode(), new me.neznamy.tab.shared.backend.Location(loc.getX(), loc.getY(), loc.getZ()));
}
}
}
@@ -66,11 +70,24 @@ public class WitherBossBar extends BossBarManagerImpl implements Listener {
}
/**
* Respawning wither as respawn screen destroys all entities in client
* Respawning wither as respawn screen destroys all entities in client.
*
* @param e
* Respawn event
*/
@EventHandler
public void onRespawn(PlayerRespawnEvent e) {
TAB.getInstance().getCPUManager().runMeasuredTask(this, TabConstants.CpuUsageCategory.PLAYER_RESPAWN,
() -> detectBossBarsAndSend(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
TabPlayer player = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
if (player == null) return;
TAB.getInstance().getCPUManager().runMeasuredTask(getFeatureName(), 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,370 +0,0 @@
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import me.neznamy.tab.api.*;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.protocol.TabPacket;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Pose;
import java.util.UUID;
/**
* A class representing an armor stand attached to a player
*/
public class BukkitArmorStand implements ArmorStand {
/** Entity id counter to pick unique entity ID for each armor stand */
private static int idCounter = 2000000000;
/** NameTag feature */
private final BukkitNameTagX manager = (BukkitNameTagX) TabAPI.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
/** Armor stand manager which this armor stand belongs to */
private final BukkitArmorStandManager asm;
/** Owner of the armor stand */
private final TabPlayer owner;
/** Owner as a Bukkit player */
private final Player player;
/** Offset in blocks, 0 for original height */
private double yOffset;
/** If offset is static, or dynamic based on other armor stands */
private final boolean staticOffset;
/** Entity ID of this armor stand */
private final int entityId = idCounter++;
/** Unique ID of this armor stand */
private final UUID uuid = UUID.randomUUID();
/** Sneaking flag of armor stands */
private boolean sneaking;
/** Armor stand visibility */
private boolean visible;
/** Refresh property dedicated to this armor stand */
private final Property property;
/** Entity destroy packet */
private final PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(entityId);
/**
* Constructs new instance with given parameters.
*
* @param asm
* Armor stand manager which this armor stand belongs to
* @param owner
* Owner of the armor stand
* @param propertyName
* Name of refresh property to use
* @param yOffset
* Offset in blocks
* @param staticOffset
* {@code true} if offset is static, {@code false} if not
*/
public BukkitArmorStand(BukkitArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
this.asm = asm;
this.owner = owner;
this.staticOffset = staticOffset;
player = (Player) owner.getPlayer();
this.yOffset = yOffset;
this.property = owner.getProperty(propertyName);
visible = getVisibility();
sneaking = player.isSneaking();
}
@Override
public void refresh() {
visible = getVisibility();
updateMetadata();
}
@Override
public Property getProperty() {
return property;
}
@Override
public boolean hasStaticOffset() {
return staticOffset;
}
@Override
public double getOffset() {
return yOffset;
}
@Override
public void setOffset(double offset) {
if (yOffset == offset) return;
yOffset = offset;
for (TabPlayer all : asm.getNearbyPlayers()) {
all.sendCustomPacket(getTeleportPacket(all), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_OFFSET_CHANGE);
}
}
@Override
public void spawn(TabPlayer viewer) {
for (TabPacket packet : getSpawnPackets(viewer)) {
viewer.sendCustomPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SPAWN);
}
}
@Override
public void destroy() {
for (TabPlayer all : asm.getNearbyPlayers()) all.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
}
@Override
public void destroy(TabPlayer viewer) {
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
}
@Override
public void teleport() {
for (TabPlayer all : asm.getNearbyPlayers()) {
all.sendCustomPacket(getTeleportPacket(all), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
}
}
@Override
public void teleport(TabPlayer viewer) {
if (!asm.isNearby(viewer) && viewer != owner) {
asm.spawn(viewer);
} else {
viewer.sendCustomPacket(getTeleportPacket(viewer), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
}
}
@Override
public void sneak(boolean sneaking) {
if (this.sneaking == sneaking) return; //idk
this.sneaking = sneaking;
for (TabPlayer viewer : asm.getNearbyPlayers()) {
if (viewer.getVersion().getMinorVersion() == 14 && !manager.isArmorStandsAlwaysVisible()) {
//1.14.x client sided bug, de-spawning completely
if (sneaking) {
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SNEAK);
} else {
spawn(viewer);
}
} else {
//respawning so there's no animation and it's instant
respawn(viewer);
}
}
}
@Override
public void updateVisibility(boolean force) {
boolean visibility = getVisibility();
if (visible != visibility || force) {
refresh();
}
}
@Override
public int getEntityId() {
return entityId;
}
/**
* Returns teleport packet for specified viewer
*
* @param viewer
* player to get location for
* @return teleport packet
*/
public PacketPlayOutEntityTeleport getTeleportPacket(TabPlayer viewer) {
return new PacketPlayOutEntityTeleport(entityId, getArmorStandLocationFor(viewer));
}
/**
* Updates armor stand's metadata
*/
public void updateMetadata() {
for (TabPlayer viewer : asm.getNearbyPlayers()) {
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(entityId, createDataWatcher(property.getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
}
}
/**
* Returns general visibility rule for everyone with limited info
*
* @return {@code true} if armor stand should be visible, {@code false} if not
*/
public boolean getVisibility() {
if (manager.isArmorStandsAlwaysVisible()) return true;
if (owner.isDisguised() || manager.getVehicleManager().isOnBoat(owner)) return false;
return !owner.hasInvisibilityPotion() && owner.getGamemode() != 3 && !manager.hasHiddenNametag(owner) && property.get().length() > 0;
}
/**
* Returns location where armor stand should be at time of calling.
* This takes into account everything that affects height, including
* viewer's game version.
*
* @param viewer
* Player looking at the armor stand
* @return Location where armor stand should be for specified viewer
*/
public Location getLocation(TabPlayer viewer) {
double x = player.getLocation().getX();
double y = getY() + yOffset;
double z = player.getLocation().getZ();
if (!player.isSleeping()) {
if (sneaking) {
if (viewer.getVersion().getMinorVersion() >= 15) {
y += 1.37;
} else if (viewer.getVersion().getMinorVersion() >= 9) {
y += 1.52;
} else {
y += 1.7;
}
} else {
y += viewer.getVersion().getMinorVersion() >= 9 ? 1.8 : 1.84; // Normal
}
} else {
y += viewer.getVersion().getMinorVersion() >= 9 ? 0.2 : 0.26; // Sleeping
}
return new Location(null,x,y,z);
}
/**
* Returns Y where player is based on player's vehicle due to bukkit API bug
*
* @return correct player's Y
*/
protected double getY() {
//1.14+ server sided bug
Entity vehicle = player.getVehicle();
if (vehicle != null) {
if (vehicle.getType().toString().contains("HORSE")) { //covering all 3 horse types
return vehicle.getLocation().getY() + 0.85;
}
if (vehicle.getType().toString().equals("DONKEY")) { //1.11+
return vehicle.getLocation().getY() + 0.525;
}
if (vehicle.getType() == EntityType.PIG) {
return vehicle.getLocation().getY() + 0.325;
}
if (vehicle.getType().toString().equals("STRIDER")) { //1.16+
return vehicle.getLocation().getY() + 1.15;
}
}
//1.13+ swimming or 1.9+ flying with elytra
if (isSwimming() || (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
return player.getLocation().getY()-1.22;
}
return player.getLocation().getY();
}
/**
* Returns {@code true} if owner is swimming, {@code false} if not
* @return {@code true} if owner is swimming, {@code false} if not
*/
private boolean isSwimming() {
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
return TabAPI.getInstance().getServerVersion().getMinorVersion() == 13 && player.isSwimming();
}
/**
* Creates data watcher with specified display name for viewer
*
* @param displayName
* armor stand name
* @param viewer
* player to apply checks against
* @return DataWatcher for viewer
*/
public DataWatcher createDataWatcher(String displayName, TabPlayer viewer) {
DataWatcher datawatcher = new DataWatcher();
byte flag = 32; //invisible
if (sneaking) flag += (byte)2;
datawatcher.helper().setEntityFlags(flag);
datawatcher.helper().setCustomName(displayName, viewer.getVersion());
boolean visibility;
if (isNameVisiblyEmpty(displayName) || !((Player) viewer.getPlayer()).canSee(player) ||
manager.hasHiddenNametag(owner, viewer) || manager.hasHiddenNameTagVisibilityView(viewer)) {
visibility = false;
} else {
visibility = visible;
}
datawatcher.helper().setCustomNameVisible(visibility);
if (viewer.getVersion().getMinorVersion() > 8 || manager.isMarkerFor18x()) datawatcher.helper().setArmorStandFlags((byte)16);
return datawatcher;
}
/**
* Returns {@code true} if display name is in fact empty,
* for example only containing color codes, {@code false} if not.
*
* @param displayName
* string to check
* @return {@code true} if it's empty, {@code false} if not
*/
private boolean isNameVisiblyEmpty(String displayName) {
if (displayName.length() == 0) return true;
if (!displayName.startsWith(EnumChatFormat.COLOR_STRING) && !displayName.startsWith("&") && !displayName.startsWith("#")) return false;
String text = IChatBaseComponent.fromColoredText(displayName).toRawText();
if (text.contains(" ")) text = text.replace(" ", "");
return text.length() == 0;
}
/**
* Returns list of packets to send to make armor stand spawn with metadata
*
* @param viewer
* viewer to apply relational placeholders for
* @return List of packets that spawn the armor stand
*/
public TabPacket[] getSpawnPackets(TabPlayer viewer) {
visible = getVisibility();
DataWatcher dataWatcher = createDataWatcher(property.getFormat(viewer), viewer);
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
return new TabPacket[] {
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getArmorStandLocationFor(viewer), null),
new PacketPlayOutEntityMetadata(entityId, dataWatcher)
};
} else {
return new TabPacket[] {
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getArmorStandLocationFor(viewer), dataWatcher),
};
}
}
/**
* Returns location where armor stand should be for specified viewer
*
* @param viewer
* player to get location for
* @return location of armor stand
*/
public Location getArmorStandLocationFor(TabPlayer viewer) {
return viewer.getVersion().getMinorVersion() == 8 && !manager.isMarkerFor18x() ? getLocation(viewer).add(0,-2,0) : getLocation(viewer);
}
@Override
public void respawn(TabPlayer viewer) {
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
// 1.8.0 will not see entity that respawned in the same tick
// creating new delayed task every time someone sneaks can be abused and cause OOM
spawn(viewer);
}
}
Loaded 100 of 520 files, more files were not shown because too many files have changed in this diff. Show more