Compare commits

..
251 Commits
Author SHA1 Message Date
NEZNAMY 9a7a105e7b fix %serveronline% and %worldonline% inaccuracies 2022-01-28 11:25:13 +01:00
NEZNAMY f54d024d97 3.0.2 2022-01-27 20:43:29 +01:00
NEZNAMY 94338bd53f update nested placeholders immediately on plugin message 2022-01-27 13:17:24 +01:00
NEZNAMY 1a9ecc0313 fix belowname update packets being sent in disabled worlds / servers 2022-01-26 22:04:13 +01:00
NEZNAMY 4baeb7b945 properly cancel delayed tasks on shutdown / reload 2022-01-26 18:24:24 +01:00
NEZNAMY baee479fb5 3.0.2-pre6 2022-01-26 01:03:29 +01:00
NEZNAMY 5ad4b2e9c4 so many issues from one wrong method call 2022-01-25 22:57:31 +01:00
NEZNAMY 26657cddfc code style suggestions by intellij 2022-01-25 22:56:52 +01:00
NEZNAMY 947a922f8c reduce amount of logged warns in console 2022-01-25 16:08:46 +01:00
NEZNAMY a2be76184e use tab's thread manager for scoreboard and bossbar announcing 2022-01-25 00:43:57 +01:00
NEZNAMY 8ddb733dc3 do not freeze tab's thread with delayed tasks 2022-01-25 00:33:44 +01:00
NEZNAMY 94e016129b run repeating task for the first time with initial delay 2022-01-25 00:33:21 +01:00
NEZNAMY 5e653ded5d process events in a single thread on velocity as well 2022-01-23 22:04:47 +01:00
NEZNAMY 1bf8097c3f 3.0.2-pre5 2022-01-23 21:39:16 +01:00
NEZNAMY 3d2f11ffd2 move enable call just in case 2022-01-23 18:28:08 +01:00
NEZNAMY 5e77a22d7a further javadoc progress 2022-01-23 18:27:46 +01:00
NEZNAMY 1dffcb1dc3 remove all thread safety checks since they are no longer needed 2022-01-23 17:24:26 +01:00
NEZNAMY edc18cbafb run all tasks in the same thread 2022-01-23 16:49:50 +01:00
NEZNAMY 2f23194432 3.0.2-pre4 2022-01-23 11:06:42 +01:00
NEZNAMY 20709472be fix vehicle / passenger handling 2022-01-23 11:06:16 +01:00
NEZNAMY 7a8c6a0cec some more velocity fixes 2022-01-22 23:11:51 +01:00
NEZNAMY 88558e40a7 round health even when copying max health 2022-01-21 14:49:27 +01:00
NEZNAMY 79386b2b6b allow 64 character scoreboard on <1.13 using "Long|" line prefix 2022-01-21 13:12:03 +01:00
NEZNAMY 3aad8d3cb5 allow per-server / per-world properties through commands 2022-01-21 10:49:06 +01:00
NEZNAMY 272a6f37ad fix NPE 2022-01-20 18:49:33 +01:00
NEZNAMY e10081025d fix load() method not executing methods in correct order resulting in enormous load times with large player counts 2022-01-20 18:31:04 +01:00
NEZNAMY e03a6d8f11 attempt to fix rare error probably caused by concurrent access 2022-01-20 18:29:37 +01:00
NEZNAMY 98e7dc0321 match velocity fixes in AlignedPlayerList as well 2022-01-20 18:24:52 +01:00
NEZNAMY 74438c5d74 so this is why someone was getting a NPE 2022-01-20 18:02:14 +01:00
NEZNAMY 0c4e548565 further increase event processing delay on velocity 2022-01-20 17:06:03 +01:00
NEZNAMY cc70e01e96 merge classes together 2022-01-20 14:34:19 +01:00
NEZNAMY 7c635748af simplify 2022-01-20 13:58:05 +01:00
NEZNAMY 0c874ad56d only keep lowercase entries in sorting map 2022-01-20 13:51:24 +01:00
NEZNAMY 2fb7f80b71 always display bossbars in defined order 2022-01-20 13:14:40 +01:00
NEZNAMY 3f7c3414ce increase server switch processing delay on velocity 2022-01-18 11:23:04 +01:00
NEZNAMY 97165db44d fix bridge placeholders rarely not working in scoreboard 2022-01-18 11:11:10 +01:00
NEZNAMY 82cdf04cf0 add feature override check for onServerChange 2022-01-18 11:10:34 +01:00
NEZNAMY 171d899609 3.0.2-pre3 2022-01-14 20:51:37 +01:00
NEZNAMY 6432278d5d fix scoreboard detection from other plugins not resetting on server switch 2022-01-14 20:41:15 +01:00
NEZNAMY ccaff12451 fix NPE caused by concurrent access 2022-01-14 20:40:54 +01:00
NEZNAMY 11347c080b more debug messages 2022-01-14 15:50:13 +01:00
NEZNAMY 553676c3f9 catch LinkageError instead of more specific ones 2022-01-14 15:49:49 +01:00
NEZNAMY 40b70e7418 fix compatibility with forks that set version field to "Unknown" 2022-01-14 14:22:52 +01:00
NEZNAMY 0ea75019ae catch NoSuchMethodError as well 2022-01-14 14:21:46 +01:00
NEZNAMY dbc092df77 add some missing bossbar command feedback (#462) 2022-01-14 11:09:15 +01:00
NEZNAMY 68049f0dca extract duplicated code into method (and also cancel tasks if plugin fails to load) 2022-01-13 20:54:58 +01:00
NEZNAMY 9db5dfc175 fix NPE when getting last value but placeholder threw error 2022-01-13 20:48:49 +01:00
NEZNAMY 039a316b3b clear players if plugin fails to load 2022-01-13 20:48:16 +01:00
NEZNAMY f4b529f1ce update README.md 2022-01-11 18:50:44 +01:00
NEZNAMY 9c6d606103 code cleanup 2022-01-11 17:57:15 +01:00
NEZNAMY 6cdc06aae7 fix config option path 2022-01-11 17:45:54 +01:00
NEZNAMY 2684654e7b drop redundant remove-strings option 2022-01-11 17:45:07 +01:00
NEZNAMY 25bcc33204 remove harming check 2022-01-10 20:44:41 +01:00
Callum Seabrook e5b3f6215b Added relocation for event 2022-01-10 15:03:16 +00:00
NEZNAMY 9306eadd90 a couple of config conversion fixes 2022-01-09 18:42:13 +01:00
NEZNAMY a27fbb5b8b MessageFile now extends YamlConfigurationFile 2022-01-08 17:27:28 +01:00
NEZNAMY cd4c82b0b8 Merge pull request #477 from JannesStroehlein/master
Fixed localization issue when dealing with floats numbers
2022-01-08 14:45:57 +01:00
Jannes Ströhlein aa6179ee9e made all decimal seperators points instead of some being commas. The commas made the placeholder replacement logic malfunction (2,43 -> 243 instead of 2.43) 2022-01-08 14:39:11 +01:00
NEZNAMY 6594b8c2dc fix replacements being applied twice for bridge placeholders (#476) 2022-01-07 10:47:19 +01:00
NEZNAMY d4c2cdf003 fix startup error when using placeholders with colors inside 2022-01-06 18:41:05 +01:00
NEZNAMY 2b529c37f5 fix rare concurrent modification 2022-01-06 17:55:16 +01:00
NEZNAMY 8599cd925b fix %vanished% refreshing with a much higher delay than it should on bungeecord 2022-01-05 14:32:22 +01:00
NEZNAMY 89f37b4b9e add synchronized modifier to stay safe 2022-01-04 16:46:40 +01:00
NEZNAMY f3136e85ee use equals instead of equalsIgnoreCase for better performance 2022-01-04 16:21:18 +01:00
NEZNAMY 845743073c fix wrong mysql checks 2022-01-04 15:33:36 +01:00
NEZNAMY b2c7bacaf9 fix errors when eventbus fails to load 2022-01-04 15:16:34 +01:00
NEZNAMY ed1673f032 fix #472 2022-01-04 15:15:53 +01:00
NEZNAMY bac9d443ea process all bungee events async 2022-01-03 20:32:28 +01:00
NEZNAMY 90e3868c08 use WeakHashMap for player mysql data & fix playeruuid not working 2022-01-03 15:53:07 +01:00
NEZNAMY c428572e01 add null check that doesn't really make sense, but someone needs it 2022-01-03 14:33:16 +01:00
NEZNAMY 884b5755ec 3.0.2-pre2 2022-01-02 22:47:51 +01:00
NEZNAMY 32d7366795 several fixes in redisbungee support when global playerlist is disabled 2022-01-02 22:47:11 +01:00
NEZNAMY 21bf4a5450 fix using % symbol without any placeholders resulting in %% being displayed 2022-01-02 22:38:59 +01:00
NEZNAMY 8042b02cf5 update README.md 2022-01-02 21:59:08 +01:00
NEZNAMY ae7cdd3982 no longer sending redundant packets on server/world switch 2022-01-01 22:03:19 +01:00
NEZNAMY 9ba945ad76 cache components on velocity as well 2022-01-01 21:51:18 +01:00
NEZNAMY 9cd04c3660 small optimization 2022-01-01 19:12:01 +01:00
NEZNAMY 444bc19781 fix pipeline injection detection on velocity 2022-01-01 18:23:46 +01:00
NEZNAMY eb08c4ba04 convert %afk% replacement when upgrading from free version as well 2022-01-01 14:34:57 +01:00
NEZNAMY 5846e24a29 improve placeholder API 2022-01-01 01:33:03 +01:00
NEZNAMY 9c2c09e433 remove debug messages 2021-12-31 14:20:08 +01:00
NEZNAMY 3c4329697b add PlayerLoadEvent#isJoin() method 2021-12-31 14:12:36 +01:00
NEZNAMY af9951d993 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-31 13:54:52 +01:00
NEZNAMY 630f6df2f5 Merge pull request #467 from NamiUni/patch-1
Correctly display the number of players online.
2021-12-31 13:54:38 +01:00
Namiu/うにたろう fcf07b8e7b Correctly display the number of players online. 2021-12-31 21:49:12 +09:00
NEZNAMY 4824c2f626 send remove packet always, just to stay safe 2021-12-30 14:01:05 +01:00
NEZNAMY 2b6b2e1b63 create a constant to reduce memory allocations 2021-12-29 18:02:22 +01:00
NEZNAMY 6926c1e57f redisbungee should not add/remove players when global playerlist is disabled 2021-12-29 17:12:16 +01:00
NEZNAMY 6a00a7f1ca fix wrong map key type 2021-12-29 00:32:51 +01:00
NEZNAMY 288c3d4c87 add onClickCopyToClipBoard method to ChatModifier class 2021-12-29 00:27:11 +01:00
NEZNAMY ac497bcc41 ignore vanished players in layout 2021-12-28 22:31:45 +01:00
NEZNAMY 7b8d0d6845 fix startup error when fixes slots list is null 2021-12-28 22:05:44 +01:00
NEZNAMY a9a4589fae disable logging of invalid numbers due to false positive on BungeeCord before bridge placeholders are initialized 2021-12-28 18:53:29 +01:00
NEZNAMY b8f972a774 disable scoreboard score packet check due to problems it causes 2021-12-28 18:44:14 +01:00
NEZNAMY 305e388478 translate to a more user-friendly exception when bossbar.yml is missing when trying to convert configuration 2021-12-26 15:18:42 +01:00
NEZNAMY 97e7848a78 add catch for IllegalStateException which may be thrown when using plugman 2021-12-26 15:15:16 +01:00
NEZNAMY d33058a924 fix rare error caused by concurrent access 2021-12-26 15:14:53 +01:00
NEZNAMY eda3cc1f17 fix yellow number & belowname not appearing after client reset packet (on server switch) 2021-12-26 14:47:02 +01:00
NEZNAMY 47043ad4ad fix check if injection is loaded by external plugin 2021-12-24 01:10:26 +01:00
NEZNAMY acf20088cc do not send packet if not needed 2021-12-24 01:09:43 +01:00
NEZNAMY 374cb2766c initialize array just in case 2021-12-24 00:54:31 +01:00
NEZNAMY 31685b9ba5 unregister-before-register should always be disabled on proxy 2021-12-24 00:35:38 +01:00
NEZNAMY 722ff4ce52 3.0.2-pre1 2021-12-23 21:05:45 +01:00
NEZNAMY 161b375043 add floodgate as softdepend to avoid md5 message 2021-12-23 21:04:17 +01:00
NEZNAMY 2c14191cde do not send yellow number to players in disabled worlds/servers 2021-12-23 20:48:45 +01:00
NEZNAMY 486546918f (probably) fix #455 2021-12-23 19:18:09 +01:00
NEZNAMY 23d08e58a4 make help menu text configurable (#448) 2021-12-23 19:15:55 +01:00
NEZNAMY 4c4c34ecaa fix #453 2021-12-23 18:55:29 +01:00
NEZNAMY bd3f2fd70a initialize EventBus in declaration 2021-12-23 18:52:59 +01:00
NEZNAMY b80e42a635 fix NPE in custom lines 2021-12-23 15:32:19 +01:00
NEZNAMY 9446a034d0 fix layout resetting on server switch on velocity 2021-12-23 13:41:13 +01:00
NEZNAMY da0c6ab3fd remove unused import 2021-12-23 11:58:57 +01:00
NEZNAMY c1adb27ee5 remove PacketAPI class as it's no longer needed 2021-12-22 20:54:03 +01:00
NEZNAMY 980d3027e3 use defined constant 2021-12-22 19:23:21 +01:00
NEZNAMY 53ba533f9b small optimizations in reducing amount of sent packets 2021-12-22 19:20:27 +01:00
NEZNAMY e40827da65 don't send empty packets 2021-12-22 18:45:15 +01:00
NEZNAMY b9848b6a96 add debug message for player quit processing time 2021-12-22 18:09:03 +01:00
NEZNAMY 147593b11a use WeakHashMap to store player data in features to not need to remove it on quit 2021-12-22 18:08:47 +01:00
NEZNAMY 21652f928b remove online placeholder update on join/leave 2021-12-22 16:05:07 +01:00
NEZNAMY 5b0352aff0 only unregister teams, no need for scores as well 2021-12-22 15:18:21 +01:00
NEZNAMY b2da2fa096 update README.md 2021-12-22 15:04:12 +01:00
NEZNAMY 0cbb01a2a8 fix #447 2021-12-22 15:02:55 +01:00
NEZNAMY 179840968d Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-22 15:00:53 +01:00
NEZNAMY 3b9c1649a1 Merge pull request #452 from kacperleague9/v3
Setup workflow artifact
2021-12-22 14:47:41 +01:00
NEZNAMY 5750c7dbe1 Merge pull request #440 from NEZNAMY/common-events
Added platform agnostic event system
2021-12-22 14:01:16 +01:00
Callum Seabrook 7ceca359be Let's not recreate the event bus on reload 2021-12-22 12:42:48 +00:00
Callum Seabrook 0f8152a6ed Remove priorities, make events interfaces, and expose bus to API 2021-12-22 11:44:56 +00:00
Callum Seabrook b3258bb020 Added platform agnostic event system 2021-12-22 11:43:30 +00:00
Kacper Smoliński 4b2e705cbc Update build.yml 2021-12-22 08:43:48 +01:00
Kacper Smoliński 31e41382f8 Update build.yml 2021-12-22 08:39:30 +01:00
Kacper Smoliński ecda7192dc Update build.yml 2021-12-22 08:22:32 +01:00
Kacper Smoliński 9a48562b9f test gradle workflow 2021-12-22 08:20:18 +01:00
NEZNAMY c181b58bc1 fix NPE that makes no sense 2021-12-21 17:18:32 +01:00
NEZNAMY 2624afa2ed update README.md 2021-12-21 12:04:18 +01:00
Callum Seabrook 3690af1e35 Removed vanished placeholder from Krypton registry 2021-12-20 18:09:13 +00:00
NEZNAMY 98966d3b38 update README.md 2021-12-20 19:06:09 +01:00
NEZNAMY 41fdd16ce1 fix error when running /tab scoreboard show 2021-12-19 18:52:25 +01:00
NEZNAMY 1bc94859c0 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-19 18:42:23 +01:00
Callum Seabrook 60f6f36437 Error manager good print stack trace bad 2021-12-19 13:16:43 +00:00
Callum Seabrook 82267ba16a Updated dependencies 2021-12-19 13:10:02 +00:00
NEZNAMY 14260deef8 make method synchronized to avoid rare bug caused by misconfiguration 2021-12-18 23:41:11 +01:00
NEZNAMY 8ba7342ccf register vanish placeholder in universal since implementation is same 2021-12-18 12:19:32 +01:00
NEZNAMY f6b935d78e fix double vanish status request on placeholder refresh 2021-12-18 12:17:39 +01:00
NEZNAMY 8cf3ae2091 ping spoof should only affect real players (#444) 2021-12-17 14:55:40 +01:00
NEZNAMY e02b997521 do not use %tps% in default config on bungeecord 2021-12-17 14:50:20 +01:00
NEZNAMY 229cef13c9 add cooldowns for attribute refreshing to prevent massive packet spam with many online players 2021-12-17 14:37:14 +01:00
NEZNAMY f503d6f896 move initialization to declaration to try to fix #442 2021-12-17 14:36:33 +01:00
NEZNAMY d851166bb7 add check for duplicated objectives and score updates without objectives 2021-12-17 13:56:44 +01:00
NEZNAMY 638a861350 display plugin message content in sent packets logging 2021-12-16 20:41:09 +01:00
NEZNAMY 20d7e4cbb0 fix entries with same value skipped in /tab cpu 2021-12-16 20:40:27 +01:00
NEZNAMY 36ccedd7c6 remove unused import 2021-12-16 18:08:38 +01:00
NEZNAMY 31854ba7a6 fix world being requested from bridge twice on reload 2021-12-16 18:07:22 +01:00
NEZNAMY 2639f07f5c recode and fixed 11 issues in config conversion 2021-12-16 17:56:29 +01:00
NEZNAMY b6e13f557f small fix in default config 2021-12-16 17:02:41 +01:00
NEZNAMY 6333d90059 remove redundant check 2021-12-16 17:02:27 +01:00
NEZNAMY fea5d0ec56 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-16 11:07:50 +01:00
NEZNAMY c77f02d1fd disable LD hook until they fix the repository 2021-12-16 11:04:43 +01:00
NEZNAMY c498965cae add API to return player's active scoreboard 2021-12-16 11:03:54 +01:00
Callum Seabrook cdaa77071d Updated Krypton to 0.59.2 2021-12-14 11:48:25 +00:00
NEZNAMY 6a72678c8d simplify 2021-12-14 10:19:47 +01:00
NEZNAMY bb67fc622f fix errors when redisbungee is not enabled correctly 2021-12-13 18:22:16 +01:00
NEZNAMY 5b50c02d93 fix replacements not working in some placeholders 2021-12-13 16:36:54 +01:00
NEZNAMY ffec7e3266 fix world/server groups loading in header/footer 2021-12-13 14:09:56 +01:00
NEZNAMY 039c1e7027 fix velocity compilation 2021-12-11 21:27:29 +01:00
NEZNAMY b27a21b3b8 remove unused variable and imports 2021-12-11 20:03:44 +01:00
NEZNAMY 2bcb5f617e create constants for feature names 2021-12-11 19:51:11 +01:00
NEZNAMY 7bf5294f9f remove redundant check 2021-12-11 17:43:49 +01:00
NEZNAMY c2fd1940f0 separate character width file loading into another method 2021-12-11 17:41:35 +01:00
NEZNAMY 2d095ce6db fix compilation 2021-12-11 17:24:57 +01:00
NEZNAMY e88bc36fca remove unused field 2021-12-11 17:23:20 +01:00
NEZNAMY 5613ea4f77 fix placeholders not updating in replacements 2021-12-11 17:12:58 +01:00
NEZNAMY 9c41599d60 remove redundant escape (again) 2021-12-11 16:04:40 +01:00
NEZNAMY 5f8dd869e0 remove redundant escape in RGB patterns 2021-12-11 15:58:45 +01:00
NEZNAMY fb17df0b7e add registered placeholders to command complete in parse command 2021-12-11 14:53:38 +01:00
NEZNAMY 900ae83028 3.0.1 2021-12-11 12:11:58 +01:00
NEZNAMY 2c09db25b7 Merge pull request #432 from LeonMangler/patch-1
Load TAB after PremiumVanish
2021-12-11 12:10:26 +01:00
NEZNAMY 004e53fa29 Merge pull request #437 from Tanguygab/master
fix NPE when executing scoreboard cmd from console
2021-12-11 12:10:01 +01:00
NEZNAMY e285b143d5 3.0.0-pre3 2021-12-11 00:15:44 +01:00
NEZNAMY f78eb0ffed add API to change group temporarily 2021-12-11 00:14:13 +01:00
NEZNAMY 4d761e1d6e allow decimals in sorting by placeholder 2021-12-11 00:04:20 +01:00
NEZNAMY 4a8c975319 fix startup on bungeecord when assigning groups by permissions AND using bukkit permission manager 2021-12-10 23:45:55 +01:00
NEZNAMY fe56b7ed2d add LP group event listener as well & fix refreshing when assigning by permissions 2021-12-10 23:21:40 +01:00
NEZNAMY 5a74654807 fix scoreboard being enabled by default when converting config 2021-12-10 22:16:08 +01:00
NEZNAMY 533ce4a529 remove unused import 2021-12-10 20:55:19 +01:00
Tanguygab 4ea3780114 fix NPE when executing cmd from console 2021-12-10 19:36:45 +01:00
NEZNAMY 0d229ee26c do not send objective changes before they are registered 2021-12-10 18:25:56 +01:00
NEZNAMY 29079ecad5 add 1.18.1 to recognized versions 2021-12-10 16:01:21 +01:00
NEZNAMY ea88401a58 fix NPE when placeholder returns null 2021-12-10 15:25:17 +01:00
NEZNAMY 298cef4d1f fix startup error if replacement value is null 2021-12-10 15:24:45 +01:00
NEZNAMY a51e9495de add player load status checks to other placeholder types as well 2021-12-10 15:12:43 +01:00
NEZNAMY eead8ca1e2 attempt to avoid rare concurrent modification 2021-12-10 15:11:39 +01:00
NEZNAMY a30712bc38 unregister on bungeecord as well to avoid issues with new bukkit-focused check 2021-12-10 14:56:06 +01:00
NEZNAMY 07b443fb21 actually fixed the error 2021-12-10 14:54:23 +01:00
NEZNAMY 61d927305c add very likely useless check to try to prevent errors 2021-12-10 13:24:29 +01:00
NEZNAMY 8f21c2f27d fix mohist (again) 2021-12-09 22:34:20 +01:00
NEZNAMY 9fcf76e0c4 3.0.1-pre2 2021-12-09 14:53:16 +01:00
NEZNAMY 773afe2fb5 fix _DEFAULT_ not being used when using MySQL (#433) 2021-12-09 14:05:28 +01:00
NEZNAMY 83ce5bd476 Drop <#RRGGBB> format due to conflict with the gradient pattern starting the same way (#430) 2021-12-09 13:50:04 +01:00
NEZNAMY 75a341c240 fix placeholder refreshing being broken for time travelers 2021-12-09 13:44:51 +01:00
NEZNAMY 921a793b5c return identifier instead of null for sync placeholders before initialized 2021-12-09 13:05:31 +01:00
NEZNAMY c1b92868cd fix world forwarding on bungeecord 2021-12-08 20:39:51 +01:00
NEZNAMY 94d19d728d fix condition refresh when only trigger placeholders are used inside 2021-12-08 20:38:39 +01:00
NEZNAMY 8ef518f7e2 add null check for tablist properties when refreshing 2021-12-08 19:11:43 +01:00
NEZNAMY 79fd1148a9 add back animation format converting from 2.8.10 -> 2.9.0 2021-12-08 18:51:00 +01:00
NEZNAMY 313ba8d765 attempt to fix error caused by concurrent access 2021-12-08 18:43:22 +01:00
NEZNAMY c12c422357 fix NPE when editing unknown bossbar 2021-12-08 18:37:56 +01:00
NEZNAMY b14c97d18a add try/catch for ConcurrentModificationException 2021-12-08 18:34:39 +01:00
NEZNAMY 2fe345cb65 add back "barcolors" animation since it's used in default config 2021-12-08 18:11:03 +01:00
NEZNAMY 96ff81370c fix animations lagging when using nested animations 2021-12-08 18:09:48 +01:00
Leon Mangler c4a1f41673 Load TAB after PremiumVanish
When BungeeTabPlayer#isVanished uses PV's API, PV needs to actually be enabled, not just loaded. A soft-dependency should ensure that PV is fully enabled before TAB's onEnable() uses the API.
2021-12-08 17:37:13 +01:00
NEZNAMY ca4c8ccb20 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-08 15:57:51 +01:00
Callum Seabrook 7471006f8a Updated to Krypton 0.58 2021-12-07 18:14:23 +00:00
NEZNAMY 896c709824 fix small memory leak 2021-12-04 22:41:55 +01:00
NEZNAMY 473a37cb6a fix NPE 2021-12-04 22:29:10 +01:00
NEZNAMY 9e84d96051 Add back %essentialsnick% for all the crybabies we get in #community-support who didn't even bother reading changelog of a major plugin update which is supposed to hint big changes were made and the changelog should be read. 2021-12-04 20:56:30 +01:00
NEZNAMY 019466d84d remove unused code 2021-12-04 19:27:02 +01:00
NEZNAMY c222061529 slightly optimize width calculation & partially avoid #347 2021-12-04 19:15:36 +01:00
NEZNAMY acdd99404c use different functions for file operations 2021-12-04 19:08:58 +01:00
NEZNAMY 799381bff3 properly convert objects to string 2021-12-04 19:02:11 +01:00
NEZNAMY b3654977ca cast to long 2021-12-04 18:59:03 +01:00
NEZNAMY b67d1928b5 add "final" modifier 2021-12-04 18:58:00 +01:00
NEZNAMY 052748d2b9 simplify 2021-12-04 18:47:18 +01:00
NEZNAMY e265187ff8 call function instead of duplicating code 2021-12-04 18:10:44 +01:00
NEZNAMY c622a95550 use fixed interval to avoid startup error 2021-12-04 17:09:40 +01:00
NEZNAMY 94d71a245b Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-04 13:28:23 +01:00
NEZNAMY a9403269be add LP softdepend on bungeecord 2021-12-04 13:26:16 +01:00
Callum Seabrook dfe6768eb5 Moved YamlAssist to Krypton repository version 2021-12-04 12:19:55 +00:00
Callum Seabrook d88bf65806 Setup publishing for all but the JAR module 2021-12-04 12:05:35 +00:00
NEZNAMY 30929062d6 what happened here 2021-12-04 12:20:56 +01:00
NEZNAMY 747e3d692d clear registered teams on login packet 2021-12-04 12:16:07 +01:00
Callum Seabrook 1d3ecbb7ed Somehow forgot that display name is now nullable 2021-12-04 10:28:25 +00:00
Callum Seabrook 39b88b0dd5 Updated Krypton to 0.57 2021-12-04 10:24:18 +00:00
NEZNAMY d13d3eb055 bridge placeholders will return identifier until they are initialized instead of null 2021-12-03 16:21:04 +01:00
NEZNAMY f8f72b9074 rgb support for remaining players text as well 2021-12-03 15:47:51 +01:00
NEZNAMY a9d0b0a0ae 3.0.1-pre1 2021-12-03 15:38:46 +01:00
NEZNAMY 37bd624537 double-fix for bungeecord bug kick 2021-12-03 15:37:23 +01:00
NEZNAMY e19f4ed771 reorder bungee.yml to match PluginDescription.java order 2021-12-03 12:11:27 +01:00
NEZNAMY c828fa8d52 Merge remote-tracking branch 'origin/master' 2021-12-03 12:00:36 +01:00
NEZNAMY 9e080a5f5c add argument check 2021-12-03 12:00:20 +01:00
NEZNAMY 7d7d4c05ab remove debug message 2021-12-03 11:59:34 +01:00
NEZNAMY d4c7044708 add proper color support to layout more players text 2021-12-02 20:36:38 +01:00
Callum Seabrook 52dd8c7c8b Added run folder to gitignore for test servers 2021-12-02 18:49:11 +00:00
NEZNAMY 398ac8b6cf register invalid relational placeholders on proxy correctly, so it doesn't throw errors 2021-12-02 18:12:53 +01:00
NEZNAMY 911792b214 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-02 13:50:21 +01:00
Callum Seabrook 8e54989502 Setup new publish repository for API 2021-12-02 12:49:11 +00:00
NEZNAMY 4f16579063 support mineskin by uuid 2021-12-02 11:01:19 +01:00
NEZNAMY e1f1698413 fix startup on mohist 1.12.2 2021-12-02 10:09:25 +01:00
NEZNAMY 5eb999b98a fixed typos, capital letters, more docs 2021-12-01 15:52:18 +01:00
NEZNAMY 5e7af835d8 %online% cannot be trigger due to vanish checks 2021-12-01 10:12:05 +01:00
NEZNAMY f19f2c5a20 add conversion for multiple groups with same sorting priority 2021-12-01 08:57:24 +01:00
NEZNAMY ff2aceb781 fix placeholder in default layout.yml 2021-12-01 08:50:37 +01:00
242 changed files with 4360 additions and 3551 deletions

No files matched your search

+5
View File
@@ -37,3 +37,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
run: ./gradlew clean build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v2.3.1
with:
name: Jar
path: jar/build/libs/TAB-**.jar
+1
View File
@@ -35,6 +35,7 @@ hs_err_pid*
# other
.gitattributes
*.iml
run/
# Gradle
.gradle/
+34 -11
View File
@@ -1,18 +1,41 @@
# About
This is where you can download the plugin for free without needing to buy me a coffee or just grab some code inspiration for your plugin. God bless opensource.
# Compiling from source
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.
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.
# Compiling
Compilation requires JDK 8 and up.
To compile the plugin, run `./gradlew build` from the terminal.
Once the plugin compiles, you can grab the jar from `jar/build/libs/` folder.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
# Links
See [releases](https://github.com/NEZNAMY/TAB/releases) for precompiled jar.
Has the plugin helped you in any way and you wish to support the development?
You can purchase the plugin on [MC-Market](https://www.mc-market.org/resources/20631/), [Polymart](https://polymart.org/resource/484) or just [send a donation](https://paypal.me/neznamy1).
The precompiled JAR can be downloaded for free from
[github releases](https://github.com/NEZNAMY/TAB/releases)
and [MC-Market](https://www.mc-market.org/resources/20631/).
Supporters have access to high-quality [discord support](https://discord.gg/EaSvdk6).
Want to know more about the plugin's functions? Check out the [wiki](https://github.com/NEZNAMY/TAB/wiki).
If you want to show your appreciation for my work, you can purchase the plugin on
[MC-Market](https://www.mc-market.org/resources/14009/),
[Polymart](https://polymart.org/resource/484) or just [send a donation](https://paypal.me/neznamy1).
Support is provided to buyers on [discord](https://discord.gg/EaSvdk6).
A detailed explanation of all the plugin's functions can be found on the
[wiki](https://github.com/NEZNAMY/TAB/wiki).
# Issues
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is **only** available for **bug reports**
or other issues which are not directly a bug (performance issues, errors, etc.).
Support is provided on discord and feature requests are currently not accepted due to high demand and
being busy with bug reports, which take priority over new features.
We are not looking to add any new major features anymore.
We would rather spend time improving existing features instead.
When opening a bug report, make sure you provide all necessary information to reproduce the bug,
otherwise we will not able to fix it. Saying "it only occurs randomly" or "I don't know how to
reproduce it" has no value and cannot be converted into actual code.
-4
View File
@@ -1,7 +1,3 @@
plugins {
id("tab.publishing")
}
dependencies {
compileOnlyApi(libs.netty)
api(libs.jsonSimple) {
@@ -6,77 +6,77 @@ public interface ArmorStand {
* Returns true if offset is static, false if not
* @return true if static, false if dynamic
*/
public boolean hasStaticOffset();
boolean hasStaticOffset();
/**
* Changes offset of the armor stand
* @param offset - new offset
*/
public void setOffset(double offset);
void setOffset(double offset);
/**
* Returns offset of the armor stand
* @return offset from base
*/
public double getOffset();
double getOffset();
/**
* Returns property for armor stand's name
* @return property for armor stand's name
*/
public Property getProperty();
Property getProperty();
/**
* Teleports armor stand to owner's current location for everyone in range
*/
public void teleport();
void teleport();
/**
* Teleports armor stand to owner's current location for specified player
* @param viewer - player to send packet to
*/
public void teleport(TabPlayer viewer);
void teleport(TabPlayer viewer);
/**
* Changes sneaking flag and sends packets to everyone in range
* @param sneaking
* @param sneaking
* new sneaking status
*/
public void sneak(boolean sneaking);
void sneak(boolean sneaking);
/**
* Despawns armor stand for everyone
* DeSpawns armor stand for everyone
*/
public void destroy();
void destroy();
/**
* Despawns armor stand for specified player
* DeSpawns armor stand for specified player
* @param viewer - player to send packet to
*/
public void destroy(TabPlayer viewer);
void destroy(TabPlayer viewer);
/**
* Updates armor stand's name if needed
*/
public void refresh();
void refresh();
/**
* Updates visibility if needed
* @param force - if refresh should be forced
*/
public void updateVisibility(boolean force);
void updateVisibility(boolean force);
/**
* Returns entity ID of this armor stand
* @return entity ID of this armor stand
*/
public int getEntityId();
int getEntityId();
/**
* Spawns armor stand for specified player
* @param viewer - player to spawn for
* @param addToRegistered - if player should be added to players in range
*/
public void spawn(TabPlayer viewer);
void spawn(TabPlayer viewer);
public void respawn(TabPlayer viewer);
void respawn(TabPlayer viewer);
}
@@ -1,7 +1,6 @@
package me.neznamy.tab.api;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -15,7 +14,7 @@ public class ArmorStandManager {
private final Map<String, ArmorStand> armorStands = new LinkedHashMap<>();
//players in entity tracking range
private final List<TabPlayer> nearbyPlayers = Collections.synchronizedList(new ArrayList<>());
private final List<TabPlayer> nearbyPlayers = new ArrayList<>();
//array to iterate over to avoid concurrent modification and slightly boost performance & memory
private ArmorStand[] armorStandArray = new ArmorStand[0];
@@ -27,7 +26,7 @@ public class ArmorStandManager {
* @param name - key name of the armor stand
* @param as - armor stand to add
*/
public synchronized void addArmorStand(String name, ArmorStand as) {
public void addArmorStand(String name, ArmorStand as) {
armorStands.put(name, as);
armorStandArray = armorStands.values().toArray(new ArmorStand[0]);
for (TabPlayer p : nearbyPlayerArray) as.spawn(p);
@@ -57,10 +56,8 @@ public class ArmorStandManager {
* @param viewer - player to spawn armor stands for
*/
public void spawn(TabPlayer viewer) {
synchronized (nearbyPlayers) {
nearbyPlayers.add(viewer);
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
}
nearbyPlayers.add(viewer);
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
if (viewer.getVersion().getMinorVersion() < 8) return;
for (ArmorStand a : armorStandArray) a.spawn(viewer);
}
@@ -108,10 +105,7 @@ public class ArmorStandManager {
* @param viewer - player to remove
*/
public void unregisterPlayer(TabPlayer viewer) {
synchronized (nearbyPlayers) {
nearbyPlayers.remove(viewer);
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
}
if (nearbyPlayers.remove(viewer)) nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
}
/**
@@ -119,10 +113,8 @@ public class ArmorStandManager {
*/
public void destroy() {
for (ArmorStand a : armorStandArray) a.destroy();
synchronized (nearbyPlayers) {
nearbyPlayers.clear();
nearbyPlayerArray = new TabPlayer[0];
}
nearbyPlayers.clear();
nearbyPlayerArray = new TabPlayer[0];
}
/**
@@ -3,19 +3,19 @@ package me.neznamy.tab.api;
public interface FeatureManager {
/**
* Registers a feature, which will start recieiving events
* Registers a feature, which will start receiving events
*
* @param featureName - name of feature
* @param featureHandler - the handler
* @param name - name of feature
* @param feature - the handler
*/
public void registerFeature(String name, TabFeature feature);
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
*/
public void unregisterFeature(String name);
void unregisterFeature(String name);
/**
* Returns whether a feature with said name is registered or not
@@ -23,13 +23,13 @@ public interface FeatureManager {
* @param name - name of feature defined in registerFeature method
* @return true if feature exists, false if not
*/
public boolean isFeatureEnabled(String name);
boolean isFeatureEnabled(String name);
/**
* Returns feature handler by it's 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
*/
public TabFeature getFeature(String name);
TabFeature getFeature(String name);
}
@@ -2,15 +2,15 @@ package me.neznamy.tab.api;
public interface HeaderFooterManager {
public void setHeader(TabPlayer player, String header);
void setHeader(TabPlayer player, String header);
public void setFooter(TabPlayer player, String footer);
void setFooter(TabPlayer player, String footer);
public void setHeaderAndFooter(TabPlayer player, String header, String footer);
void setHeaderAndFooter(TabPlayer player, String header, String footer);
public void resetHeader(TabPlayer player);
void resetHeader(TabPlayer player);
public void resetFooter(TabPlayer player);
void resetFooter(TabPlayer player);
public void resetHeaderAndFooter(TabPlayer player);
void resetHeaderAndFooter(TabPlayer player);
}
@@ -4,57 +4,57 @@ public interface Property {
/**
* Changes raw value to new one
* @param newValue - new value to be used
* @param rawValue - new value to be used
*/
public void changeRawValue(String rawValue);
void changeRawValue(String rawValue);
/**
* Returns original raw value ignoring API calls
* @return original raw value
*/
public String getOriginalRawValue();
String getOriginalRawValue();
/**
* Temporarily overrides current raw value with an API call
* @param temporaryValue - temporary value to be assigned
*/
public void setTemporaryValue(String temporaryValue);
void setTemporaryValue(String temporaryValue);
/**
* Returns current temporary value
* @return temporary value or null if not set
*/
public String getTemporaryValue();
String getTemporaryValue();
/**
* Returns temporary value (via API) if present, raw value otherwise
* @return current raw value
*/
public String getCurrentRawValue();
String getCurrentRawValue();
/**
* Returns last known value
* @return last known value
*/
public String get();
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
*/
public boolean update();
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
*/
public String updateAndGet();
String updateAndGet();
/**
* Returns value for defined viewer by applying relational placeholders to last known value
* @param viewer - the viewer
* @return format for the viewer
*/
public String getFormat(TabPlayer viewer);
String getFormat(TabPlayer viewer);
}
@@ -2,9 +2,9 @@ package me.neznamy.tab.api;
public interface PropertyConfiguration {
public void setProperty(String name, String property, String server, String world, String value);
void setProperty(String name, String property, String server, String world, String value);
public String[] getProperty(String name, String property, String server, String world);
String[] getProperty(String name, String property, String server, String world);
public void remove(String name);
void remove(String name);
}
@@ -5,8 +5,9 @@ package me.neznamy.tab.api;
*/
public enum ProtocolVersion {
UNKNOWN (999, "Unknown"),
PROXY (999, "Proxy"),
UNKNOWN ("Unknown"),
PROXY ("Proxy"),
V1_18_1 (757),
V1_18 (757),
V1_17_1 (756),
V1_17 (755),
@@ -63,7 +64,7 @@ public enum ProtocolVersion {
private final int networkId;
//minor version, such as 16
private final int minorVersion;
private int minorVersion;
//friendly name displayed in %player-version% placeholder
private final String friendlyName;
@@ -72,25 +73,24 @@ public enum ProtocolVersion {
* Constructs new instance with given network id
* @param networkId - network id of this version
*/
private ProtocolVersion(int networkId){
ProtocolVersion(int networkId){
this.networkId = networkId;
this.minorVersion = Integer.parseInt(toString().split("_")[1]);
this.friendlyName = toString().substring(1).replace("_", ".");
}
/**
* Constructs new instance with given network id and friendly name
* @param networkId - network id of this version
* Constructs new instance with given friendly name
* @param friendlyName - friendly name to display
*/
private ProtocolVersion(int networkId, String friendlyName){
this.networkId = networkId;
this.minorVersion = 17;
ProtocolVersion(String friendlyName){
this.networkId = 999;
this.minorVersion = 18;
this.friendlyName = friendlyName;
}
/**
* Returns the vesion's network id
* Returns the version's network id
* @return version's network id
*/
public int getNetworkId() {
@@ -99,7 +99,7 @@ public enum ProtocolVersion {
/**
* Returns user-friendly name of the version (such as 1.16.2 instead of V1_16_2)
* @return
* @return user-friendly name of the version
*/
public String getFriendlyName() {
return friendlyName;
@@ -113,6 +113,17 @@ public enum ProtocolVersion {
return minorVersion;
}
/**
* Overrides minor version of the version instance. This is used internally to
* set minor version of UNKNOWN value to value from package to fix compatibility
* with server forks that set bukkit version field value to "Unknown".
* @param minorVersion
* minor version to change value to
*/
public void setMinorVersion(int minorVersion) {
this.minorVersion = minorVersion;
}
/**
* Returns enum constant of entered version or UNKNOWN if unknown version
* @param friendlyName - friendly name of the version
@@ -4,6 +4,7 @@ import java.util.UUID;
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;
@@ -45,8 +46,8 @@ public abstract class TabAPI {
public abstract TabPlayer[] getOnlinePlayers();
/**
* Return bossbar manager instance if the feature is enabled. Returns null otherwise.
* @return bossbar manager
* Return BossBar manager instance if the feature is enabled. Returns null otherwise.
* @return BossBar manager
*/
public abstract BossBarManager getBossBarManager();
@@ -78,6 +79,13 @@ public abstract class TabAPI {
public abstract TablistFormatManager getTablistFormatManager();
/**
* 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
@@ -104,4 +112,4 @@ public abstract class TabAPI {
public abstract void debug(String message);
public abstract void logError(String message, Throwable t);
}
}
@@ -1,8 +1,6 @@
package me.neznamy.tab.api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.*;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
@@ -16,7 +14,7 @@ public abstract class TabFeature {
private final boolean serverWhitelistMode;
protected final String[] disabledWorlds;
private final boolean worldWhitelistMode;
private final List<TabPlayer> disabledPlayers = new ArrayList<>();
private final Set<TabPlayer> disabledPlayers = Collections.newSetFromMap(new WeakHashMap<>());
private final List<String> methodOverrides = new ArrayList<>();
protected TabFeature(String featureName, String refreshDisplayName) {
@@ -49,6 +47,8 @@ public abstract class TabFeature {
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");
if (getClass().getMethod("onLoginPacket", TabPlayer.class).getDeclaringClass() != TabFeature.class)
@@ -105,7 +105,6 @@ public abstract class TabFeature {
* @param disconnectedPlayer - player who disconnected
*/
public void onQuit(TabPlayer disconnectedPlayer) {
disabledPlayers.remove(disconnectedPlayer);
}
/**
@@ -125,21 +124,20 @@ public abstract class TabFeature {
* @param to - server player changed to
*/
public void onServerChange(TabPlayer changed, String from, String to) {
onWorldChange(changed, null, null);
//empty by default
}
/**
* Processes the packet send and returns true if packet should be cancelled
* @param receiver - player receiving packet
* @param packet - received packet
* @return true if packet should be cancelled, false if not
*/
public boolean onDisplayObjective(TabPlayer receiver, PacketPlayOutScoreboardDisplayObjective packet) {
return false;
public void onDisplayObjective(TabPlayer receiver, PacketPlayOutScoreboardDisplayObjective packet) {
//empty by default
}
/**
* Processes login packet, only available on bungeecord
* Processes login packet, only available on BungeeCord
* @param packetReceiver - player receiving client reset packet
*/
public void onLoginPacket(TabPlayer packetReceiver) {
@@ -158,7 +156,7 @@ public abstract class TabFeature {
/**
* Processes the packet send and possibly modifies it
* @param receiver - player receiving packet
* @param packet - received packet
* @param info - received packet
*/
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
//empty by default
@@ -169,7 +167,8 @@ public abstract class TabFeature {
* @param sender - packet sender
* @param packet - packet received
* @return true if false should be cancelled, false if not
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
return false;
@@ -179,7 +178,8 @@ public abstract class TabFeature {
* Processes raw packet sent to client
* @param receiver - packet receiver
* @param packet - the packet
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
//empty by default
@@ -251,8 +251,8 @@ public abstract class TabFeature {
disabledPlayers.add(p);
}
public boolean removeDisabledPlayer(TabPlayer p) {
return disabledPlayers.remove(p);
public void removeDisabledPlayer(TabPlayer p) {
disabledPlayers.remove(p);
}
public String getRefreshDisplayName() {
@@ -14,63 +14,63 @@ public interface TabPlayer {
/**
* Refreshes all visuals on the player
*/
public void forceRefresh();
void forceRefresh();
/**
* Returns player's name
* @return Player's name
*/
public String getName();
String getName();
/**
* Returns player's UUID
* @return Player's UUID
*/
public UUID getUniqueId();
UUID getUniqueId();
/**
* Returns player's uuid used in tablist. This may only be different than real uuid if
* Returns player's uuid used in TabList. This may only be different from real uuid if
* TAB is installed on velocity with some specific velocity setup
* @return player's uuid in tablist
* @return player's uuid in TabList
*/
public UUID getTablistUUID();
UUID getTablistUUID();
/**
* Returns player's protocol version
* @return player's protocol version
*/
public ProtocolVersion getVersion();
ProtocolVersion getVersion();
/**
* Returns platform-specific entity
* @return an instance of bukkit/bungee player depending on platform
*/
public Object getPlayer();
Object getPlayer();
/**
* Returns player's current world name (on bungeecord this requires bridge installed)
* @return name of world where player is currently in, null on bungeecord if bridge is not installed
* Returns player's current world name (on BungeeCord this requires bridge installed)
* @return name of world where player is currently in, null on BungeeCord if bridge is not installed
*/
public String getWorld();
String getWorld();
/**
* Returns player's current server name on bungeecord (on bukkit this returns null)
* Returns player's current server name on BungeeCord (on bukkit this returns null)
* @return name of server where player is currently in, null on bukkit
*/
public String getServer();
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 has, false if not
* @return - true if player has permission, false if not
*/
public boolean hasPermission(String permission);
boolean hasPermission(String permission);
/**
* Sends the player a custom universal packet
* @param packet - packet to send
*/
public void sendCustomPacket(TabPacket packet);
void sendCustomPacket(TabPacket packet);
/**
* Sends the player a custom universal packet and adds that packet into counter that
@@ -78,7 +78,7 @@ public interface TabPlayer {
* @param packet - packet to send
* @param feature - feature to increment sent packet counter of
*/
public void sendCustomPacket(TabPacket packet, TabFeature feature);
void sendCustomPacket(TabPacket packet, TabFeature feature);
/**
* Sends the player a custom universal packet and adds that packet into counter that
@@ -86,13 +86,13 @@ public interface TabPlayer {
* @param packet - packet to send
* @param feature - feature to increment sent packet counter of
*/
public void sendCustomPacket(TabPacket packet, String feature);
void sendCustomPacket(TabPacket packet, String feature);
/**
* Sends the player a platform-specific packet
* @param packet - an instance of packet depending on platform
*/
public void sendPacket(Object packet);
void sendPacket(Object packet);
/**
* Sends the player a platform-specific packet and adds that packet into counter that
@@ -100,7 +100,7 @@ public interface TabPlayer {
* @param packet - an instance of packet depending on platform
* @param feature - feature to increment sent packet counter of
*/
public void sendPacket(Object packet, TabFeature feature);
void sendPacket(Object packet, TabFeature feature);
/**
* Sends the player a platform-specific packet and adds that packet into counter that
@@ -108,149 +108,156 @@ public interface TabPlayer {
* @param packet - an instance of packet depending on platform
* @param feature - feature to increment sent packet counter of
*/
public void sendPacket(Object packet, String feature);
void sendPacket(Object packet, String feature);
/**
* Returns player's property by name
* @param name - name of property
* @return the property or null if not found
*/
public Property getProperty(String name);
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
*/
public void sendMessage(String message, boolean translateColors);
void sendMessage(String message, boolean translateColors);
/**
* Sends specified component as a chat message
* @param message - message to send
*/
public void sendMessage(IChatBaseComponent message);
void sendMessage(IChatBaseComponent message);
/**
* Returns player's primary permission group
* @return player's group depending on configuration
*/
public String getGroup();
String getGroup();
/**
* Toggles armor stands preview mode for the player
*/
public void toggleNametagPreview();
void toggleNametagPreview();
/**
* Returns whether player is previewing nametag or not
* Returns whether player is previewing NameTag or not
* @return true if previewing, false if not
*/
public boolean isPreviewingNametag();
boolean isPreviewingNametag();
/**
* Returns player's channel or null if server is 1.7 or older
* @return player's channel
*/
public Channel getChannel();
Channel getChannel();
/**
* Returns player's ping calculated by server
* @return player's ping
*/
public int getPing();
int getPing();
/**
* Returns player's platform-specific skin data
* @return player's skin
*/
public Object getSkin();
Object getSkin();
/**
* Returns true once the player is successfully loaded (onJoin method ran through all methods)
* @return true if player is fully loaded, false otherwise
*/
public boolean isLoaded();
boolean isLoaded();
/**
* Sets property with specified name to new value. If property did no exist before, it is
* created and true is returned. If it existed, it is overridden and true is returned. False is returned othewise.
* Sets property with specified name to new value. If property did not exist before, it is
* created and true is returned. If it existed, it is overridden and true is returned. False is returned otherwise.
* @param feature - feature using this property to get placeholders registered
* @param identifier - property name
* @param rawValue - new raw value
* @return true if value changed / did not exist, false if value did not change
*/
public boolean setProperty(TabFeature feature, String identifier, String rawValue);
boolean setProperty(TabFeature feature, String identifier, String rawValue);
/**
* Loads property from config using standard property loading algorithm
* @param property - property name to load
* @return true if value did not exist or changed, false otherwise
*/
public void loadPropertyFromConfig(TabFeature feature, String property);
boolean loadPropertyFromConfig(TabFeature feature, String property);
/**
* Loads property from config using standard property loading algorithm. If the property is
* not set in config, sets it to ifNotSet value
* @param property - property name to load
* @param ifNotSet - value to use if property is not defined in config
* @return true if value did not exist or changed, false otherwise
*/
public void loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
boolean loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
/**
* Returns name of player's scoreboard team or null if nametag feature is disabled
* Returns name of player's scoreboard team or null if NameTag feature is disabled
* @return name of player's team
*/
public String getTeamName();
String getTeamName();
/**
* Returns user-friendly explanation of team name
* @return explanation behind team name
*/
public String getTeamNameNote();
String getTeamNameNote();
/**
* Returns true if player is disguised using iDisguide or LibsDisguises on bukkit, LibsDisguises
* only on bungeecord via bukkit bridge
* Returns true if player is disguised using LibsDisguises
* @return true if player is disguised, false if not
*/
public boolean isDisguised();
boolean isDisguised();
/**
* Returns true if player has invisiblity potion, false if not. For bukkit, API is used, for bungeecord
* Returns true if player has invisibility potion, false if not. For bukkit, API is used, for BungeeCord
* bukkit bridge is used
* @return true if has invisiblity potion, false if not
* @return true if player has invisibility potion, false if not
*/
public boolean hasInvisibilityPotion();
boolean hasInvisibilityPotion();
/**
* Currently only PV hook on bungeecord for global playerlist function. Looking to expand this
* Currently only PV hook on BungeeCord for global PlayerList function. Looking to expand this
* in the future
* @return true if vanished with PV on bungeecord
* @return true if vanished with PV on BungeeCord
*/
public boolean isVanished();
boolean isVanished();
/**
* Returns true if player is online according to server
* @return true if online, false if not
*/
public boolean isOnline();
boolean isOnline();
/**
* Returns player's armor stand manager if unlimited nametag mode is enabled, null if disabled
* Returns player's armor stand manager if unlimited GameMode mode is enabled, null if disabled
* @return player's armor stand manager
*/
public ArmorStandManager getArmorStandManager();
ArmorStandManager getArmorStandManager();
/**
* Sets player's armor stands manager to new instance
* @param armorStandManager - new instance
*/
public void setArmorStandManager(ArmorStandManager armorStandManager);
void setArmorStandManager(ArmorStandManager armorStandManager);
/**
* Returns gamemode of the player (0 for survival, 1 creative, 2 adventure, 3 spectator)
* @return gamemode of the player
* Returns GameMode of the player (0 for survival, 1 creative, 2 adventure, 3 spectator)
* @return GameMode of the player
*/
public int getGamemode();
int getGamemode();
public boolean isBedrockPlayer();
boolean isBedrockPlayer();
void setTemporaryGroup(String group);
boolean hasTemporaryGroup();
void resetTemporaryGroup();
}
@@ -2,28 +2,28 @@ package me.neznamy.tab.api;
public interface TablistFormatManager {
public void setPrefix(TabPlayer player, String prefix);
void setPrefix(TabPlayer player, String prefix);
public void setName(TabPlayer player, String customname);
void setName(TabPlayer player, String customName);
public void setSuffix(TabPlayer player, String suffix);
void setSuffix(TabPlayer player, String suffix);
public void resetPrefix(TabPlayer player);
void resetPrefix(TabPlayer player);
public void resetName(TabPlayer player);
void resetName(TabPlayer player);
public void resetSuffix(TabPlayer player);
void resetSuffix(TabPlayer player);
public String getCustomPrefix(TabPlayer player);
String getCustomPrefix(TabPlayer player);
public String getCustomName(TabPlayer player);
String getCustomName(TabPlayer player);
public String getCustomSuffix(TabPlayer player);
String getCustomSuffix(TabPlayer player);
public String getOriginalPrefix(TabPlayer player);
String getOriginalPrefix(TabPlayer player);
public String getOriginalName(TabPlayer player);
String getOriginalName(TabPlayer player);
public String getOriginalSuffix(TabPlayer player);
String getOriginalSuffix(TabPlayer player);
}
@@ -1,7 +1,7 @@
package me.neznamy.tab.api.bossbar;
/**
* An enum representing all available bossbar colors.
* An enum representing all available BossBar colors.
* Calling ordinal() will return color's network ID.
*/
public enum BarColor {
@@ -12,5 +12,5 @@ public enum BarColor {
GREEN,
YELLOW,
PURPLE,
WHITE;
WHITE
}
@@ -1,7 +1,7 @@
package me.neznamy.tab.api.bossbar;
/**
* An enum representing all available bossbar styles.
* An enum representing all available BossBar styles.
* Calling ordinal() will return style's network ID.
* Style names are equal to NMS names.
*/
@@ -14,7 +14,7 @@ public enum BarStyle {
NOTCHED_20("SEGMENTED_20");
/** Style's name used in Bukkit API */
private String bukkitName;
private final String bukkitName;
/**
* Initializes enum constant with given bukkit name
@@ -22,7 +22,7 @@ public enum BarStyle {
* @param bukkitName
* Name of color to be returned in {@code getBukkitName()}
*/
private BarStyle(String bukkitName){
BarStyle(String bukkitName){
this.bukkitName = bukkitName;
}
@@ -6,36 +6,36 @@ import java.util.UUID;
import me.neznamy.tab.api.TabPlayer;
/**
* An interface representing a bossbar line.
* An interface representing a BossBar line.
* <p>
* For clients 1.8 and lower it uses wither, which only allows 1 bar
* to be displayed at a time. Entity packets are not available on bungeecord,
* to be displayed at a time. Entity packets are not available on BungeeCord,
* where nothing will be displayed for these players.
* <p>
* For 1.9+ it uses the new bossbar feature, allowing configurable styles
* For 1.9+ it uses the new BossBar feature, allowing configurable styles
* and colors, as well as display several bars at once. Limit of maximum
* displayed bossbars depends on client resolution and GUI scale, up to
* 9 concurrent bossbars displayed at once.
* displayed BossBars depends on client resolution and GUI scale, up to
* 9 concurrent BossBars displayed at once.
*/
public interface BossBar {
/**
* Returns name of this bossbar. If it was defined in config, returns
* Returns name of this BossBar. If it was defined in config, returns
* the name specified in config. If it was made using the API, returns
* randomly generated ID given to this bossbar when creating.
* @return name of bossbar
* randomly generated ID given to this BossBar when creating.
* @return name of BossBar
*/
public String getName();
String getName();
/**
* Returns randomly generated uuid of this bossbar used internally to match
* the bossbar in client with packets or with Bukkit API bossbar instances.
* @return uuid of this bossbar
* Returns randomly generated uuid of this BossBar used internally to match
* the BossBar in client with packets or with Bukkit API BossBar instances.
* @return uuid of this BossBar
*/
public UUID getUniqueId();
UUID getUniqueId();
/**
* Changes bossbar title to specified string, supporting placeholders.
* Changes BossBar title to specified string, supporting placeholders.
* <p>
* If title contains new placeholders not used before, they are registered using
* standard registration logic and refreshed periodically. No need to call
@@ -44,12 +44,12 @@ public interface BossBar {
* If specified title is equal to the current one, nothing happens.
*
* @param title
* New title to display in this bossbar
* New title to display in this BossBar
*/
public void setTitle(String title);
void setTitle(String title);
/**
* Changes bossbar progress to specified string, supporting placeholders.
* Changes BossBar progress to specified string, supporting placeholders.
* The string must be a string version of a float value, or a placeholder that
* outputs a float value with range 0-100.
* <p>
@@ -60,21 +60,21 @@ public interface BossBar {
* If specified progress is equal to the current one, nothing happens.
*
* @param progress
* New progress to use in this bossbar
* New progress to use in this BossBar
*/
public void setProgress(String progress);
void setProgress(String progress);
/**
* Changes bossbar progress to specified value ranging from 0 to 100.
* Changes BossBar progress to specified value ranging from 0 to 100.
* <p>
* If specified progress is equal to the current one, nothing happens.
* @param progress
* New progress to use in this bossbar
* New progress to use in this BossBar
*/
public void setProgress(float progress);
void setProgress(float progress);
/**
* Changes bossbar color to specified string, supporting placeholders.
* Changes BossBar color to specified string, supporting placeholders.
* The string must be a string version of one of the supported values,
* or a placeholder that outputs one of them.
* <p>
@@ -85,22 +85,22 @@ public interface BossBar {
* If specified color is equal to the current one, nothing happens.
*
* @param color
* New color to use in this bossbar
* New color to use in this BossBar
*/
public void setColor(String color);
void setColor(String color);
/**
* Changes bossbar color to specified enum constant.
* Changes BossBar color to specified enum constant.
* <p>
* If specified color is equal to the current one, nothing happens.
*
* @param color
* New color to use in this bossbar
* New color to use in this BossBar
*/
public void setColor(BarColor color);
void setColor(BarColor color);
/**
* Changes bossbar style to specified string, supporting placeholders.
* Changes BossBar style to specified string, supporting placeholders.
* The string must be a string version of one of the supported values,
* or a placeholder that outputs one of them.
* <p>
@@ -111,75 +111,75 @@ public interface BossBar {
* If specified style is equal to the current one, nothing happens.
*
* @param style
* New style to use in this bossbar
* New style to use in this BossBar
*/
public void setStyle(String style);
void setStyle(String style);
/**
* Changes bossbar style to specified enum constant.
* Changes BossBar style to specified enum constant.
* <p>
* If specified style is equal to the current one, nothing happens.
*
* @param style
* New style to use in this bossbar
* New style to use in this BossBar
*/
public void setStyle(BarStyle style);
void setStyle(BarStyle style);
/**
* Returns current title of the bossbar in raw format. If it contains placeholders,
* Returns current title of the BossBar in raw format. If it contains placeholders,
* their raw identifiers are used in the result.
* @return title of the bossbar
* @return title of the BossBar
*/
public String getTitle();
String getTitle();
/**
* Returns progress of the bossbar as a string, which is either entered string
* Returns progress of the BossBar as a string, which is either entered string
* containing placeholders or entered number converted to string
* @return entered progress as a string
*/
public String getProgress();
String getProgress();
/**
* Returns color of the bossbar as a string, which is either entered string
* Returns color of the BossBar as a string, which is either entered string
* containing placeholders or entered enum value converted to string
* @return entered color as a string
*/
public String getColor();
String getColor();
/**
* Returns style of the bossbar as a string, which is either entered string
* Returns style of the BossBar as a string, which is either entered string
* containing placeholders or entered enum value converted to string
* @return entered style as a string
*/
public String getStyle();
String getStyle();
/**
* Registers this bossbar to specified player.
* Registers this BossBar to specified player.
* <p>
* If the player already sees this bossbar, nothing happens.
* If the player already sees this BossBar, nothing happens.
*
* @param player
* Player to register this bossbar to
* Player to register this BossBar to
*/
public void addPlayer(TabPlayer player);
void addPlayer(TabPlayer player);
/**
* Unregisters this bossbar from specified player.
* Unregisters this BossBar from specified player.
* <p>
* If the player does not see this bossbar, nothing happens.
* If the player does not see this BossBar, nothing happens.
*
* @param player
* Player to unregister this bossbar from
* Player to unregister this BossBar from
*/
public void removePlayer(TabPlayer player);
void removePlayer(TabPlayer player);
/**
* Returns list of players who can see this bossbar.
* Returns list of players who can see this BossBar.
* <p>
* The returned list can only be used to read. Writing
* will not work properly. For adding/removing playes see {@link #addPlayer(TabPlayer)}
* will not work properly. For adding/removing players see {@link #addPlayer(TabPlayer)}
* and {@link #removePlayer(TabPlayer)}.
* @return List of players seeing this bossbar
* @return List of players seeing this BossBar
*/
public List<TabPlayer> getPlayers();
List<TabPlayer> getPlayers();
}
@@ -7,17 +7,17 @@ import java.util.UUID;
import me.neznamy.tab.api.TabPlayer;
/**
* An interface allowing work with bossbars such as creating, sending
* An interface allowing work with BossBars such as creating, sending
* and toggling.
* <p>
* Instance can be obtained using {@link me.neznamy.tab.api.TabAPI#getBossBarManager()}.
* This requires the bossbar feature to be enabled in config, otherwise the method will
* This requires the BossBar feature to be enabled in config, otherwise the method will
* return null.
*/
public interface BossBarManager {
/**
* Creates bossbar with specified parameters and registers it to bossbar manager,
* Creates BossBar with specified parameters and registers it to BossBar manager,
* allowing to retrieve it later.
*
* @param title
@@ -31,12 +31,12 @@ public interface BossBarManager {
*
* @param style
* Style to use
* @return The newly created bossbar
* @return The newly created BossBar
*/
public BossBar createBossBar(String title, float progress, BarColor color, BarStyle style);
BossBar createBossBar(String title, float progress, BarColor color, BarStyle style);
/**
* Creates bossbar with specified parameters and registers it to bossbar manager,
* Creates BossBar with specified parameters and registers it to BossBar manager,
* allowing to retrieve it later.
*
* @param title
@@ -53,58 +53,58 @@ public interface BossBarManager {
* @param style
* Style to use. Must be a string version of one of enum constants, or
* a placeholder that returns one of them
* @return The newly created bossbar
* @return The newly created BossBar
*/
public BossBar createBossBar(String title, String progress, String color, String style);
BossBar createBossBar(String title, String progress, String color, 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
* 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 null if no such BossBar exists.
*
* @param name
* Name of registered bossbar
* @return bossbar with specified name
* Name of registered BossBar
* @return BossBar with specified name
*/
public BossBar getBossBar(String 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.
* 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
* UUID to return BossBar by
* @return BossBar with specified uuid
*/
public BossBar getBossBar(UUID id);
BossBar getBossBar(UUID id);
/**
* Returns a map of all registered bossbars. This includes both bossbars from config
* Returns a map of all registered BossBars. This includes both BossBars from config
* and those registered via API.
* <p>
* Map key is bossbar's name available in {@link me.neznamy.tab.api.bossbar.BossBar#getName()},
* map value is the bossbar interface.
* @return all registered bossbars
* Map key is BossBar's name available in {@link me.neznamy.tab.api.bossbar.BossBar#getName()},
* map value is the BossBar interface.
* @return all registered BossBars
*/
public Map<String, BossBar> getRegisteredBossBars();
Map<String, BossBar> getRegisteredBossBars();
/**
* Toggles bossbar for this player and sends toggle message if {@code sendToggleMessage} is true.
* Toggles BossBar for this player and sends toggle message if {@code sendToggleMessage} is true.
*
* @param player
* Player to toggle bossbar for
* Player to toggle BossBar for
*
* @param sendToggleMessage
* {@code true} if toggle message should be sent, {@code false} if not
* @see #hasBossBarVisible(TabPlayer)
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
*/
public void toggleBossBar(TabPlayer player, boolean sendToggleMessage);
void toggleBossBar(TabPlayer player, boolean sendToggleMessage);
/**
* Returns true if player can see bossbars, false if toggled via command or API.
* Returns true if player can see BossBars, false if toggled via command or API.
*
* @param player
* Player to check toggle status of
@@ -112,56 +112,56 @@ public interface BossBarManager {
* @see #toggleBossBar(TabPlayer, boolean)
* @see #setBossBarVisible(TabPlayer, boolean, boolean)
*/
public boolean hasBossBarVisible(TabPlayer player);
boolean hasBossBarVisible(TabPlayer player);
/**
* Sets bossbar visibility of player to set value. If value did not change, nothing happens.
* Sets BossBar visibility of player to set value. If value did not change, nothing happens.
* If visibility changed, toggle message is sent if {@code sendToggleMessage} is {@code true}.
*
* @param player
* Player to set bossbar visibility of
* Player to set BossBar visibility of
* @param visible
* {@code true} if bossbar should be visible, {@code false} if not
* {@code true} if BossBar should be visible, {@code false} if not
* @param sendToggleMessage
* {@code true} if toggle message should be sent if value changed, {@code false} if not
* @see #toggleBossBar(TabPlayer, boolean)
* @see #hasBossBarVisible(TabPlayer)
*/
public void setBossBarVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
void setBossBarVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
/**
* Temporarily displays registered bossbar to player for specified amount of time in milliseconds.
* Temporarily displays registered BossBar to player for specified amount of time in milliseconds.
* @param player
* Player to show bossbar to
* @param bossbar
* Name of registered bossbar to show
* Player to show BossBar to
* @param bossBar
* Name of registered BossBar to show
* @param duration
* In milliseconds for how long should bossbar be displayed
* In milliseconds for how long should BossBar be displayed
* @throws IllegalArgumentException
* if no bossbar with specified name exists
* if no BossBar with specified name exists
* @see #announceBossBar(String, int)
* @see #getAnnouncedBossBars()
*/
public void sendBossBarTemporarily(TabPlayer player, String bossbar, int duration);
void sendBossBarTemporarily(TabPlayer player, String bossBar, int duration);
/**
* Temporarily displays registered bossbar to all players for specified amount of time in milliseconds.
* @param bossbar
* Name of registered bossbar to show
* Temporarily displays registered BossBar to all players for specified amount of time in milliseconds.
* @param bossBar
* Name of registered BossBar to show
* @param duration
* In milliseconds for how long should bossbar be displayed
* In milliseconds for how long should BossBar be displayed
* @throws IllegalArgumentException
* if no bossbar with specified name exists
* if no BossBar with specified name exists
* @see #sendBossBarTemporarily(TabPlayer, String, int)
* @see #getAnnouncedBossBars()
*/
public void announceBossBar(String bossbar, int duration);
void announceBossBar(String bossBar, int duration);
/**
* Returns list of bossbars which are currently being announced.
* @return list of currently active bossbar announcements
* Returns list of BossBars which are currently being announced.
* @return list of currently active BossBar announcements
* @see #sendBossBarTemporarily(TabPlayer, String, int)
* @see #announceBossBar(String, int)
*/
public List<BossBar> getAnnouncedBossBars();
List<BossBar> getAnnouncedBossBars();
}
@@ -1,31 +1,50 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.util.Preconditions;
/**
* Class for click event action in chat component
*/
public class ChatClickable {
private EnumClickAction action;
private String value;
/** 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 cannot be null");
Preconditions.checkNotNull(value, "hover value cannot be null");
this.action = action;
this.value = value;
}
/**
* Returns click action defined in constructor
* @return click action
*/
public EnumClickAction getAction() {
return action;
}
public void setAction(EnumClickAction action) {
this.action = action;
}
/**
* Returns click action defined in constructor
* @return click action
*/
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
/**
* Enum for all possible click actions
*/
@@ -35,6 +54,6 @@ public class ChatClickable {
RUN_COMMAND,
CHANGE_PAGE, //since 1.8
SUGGEST_COMMAND,
COPY_TO_CLIPBOARD; //since 1.15
COPY_TO_CLIPBOARD //since 1.15
}
}
@@ -1,31 +1,50 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.util.Preconditions;
/**
* Class for hover event action in chat component
*/
public class ChatHoverable {
private EnumHoverAction action;
private IChatBaseComponent value;
/** 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 cannot be null");
Preconditions.checkNotNull(value, "hover value cannot be null");
this.action = action;
this.value = value;
}
/**
* Returns hover action defined in constructor
* @return hover action
*/
public EnumHoverAction getAction() {
return action;
}
public void setAction(EnumHoverAction action) {
this.action = action;
}
/**
* Returns hover value defined in constructor
* @return hover value
*/
public IChatBaseComponent getValue() {
return value;
}
public void setValue(IChatBaseComponent value) {
this.value = value;
}
/**
* Enum for all possible hover actions
*/
@@ -33,6 +52,6 @@ public class ChatHoverable {
SHOW_TEXT,
SHOW_ITEM,
SHOW_ENTITY;
SHOW_ENTITY
}
}
}
@@ -39,6 +39,7 @@ public class ChatModifier {
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() {
@@ -156,7 +157,6 @@ public class ChatModifier {
/**
* Sets click action to RUN_COMMAND and command to given value
* @param command - command to perform, might be without / to send a chat message
* @return self
*/
public void onClickRunCommand(String command) {
clickEvent = new ChatClickable(EnumClickAction.RUN_COMMAND, command);
@@ -172,11 +172,19 @@ public class ChatModifier {
/**
* Sets click action to CHANGE_PAGE and page id to given value
* @param newpage - id of new page
* @param newPage - id of new page
*/
public void onClickChangePage(int newpage) {
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));
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) {
clickEvent = new ChatClickable(EnumClickAction.COPY_TO_CLIPBOARD, text);
}
public void onClick(EnumClickAction action, String value) {
@@ -3,81 +3,83 @@ package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
/**
* A class representing the n.m.s.EnumChatFormat class to make work with it much easier
* An enum containing all possible legacy color codes and magic codes. Also contains handy color-related methods.
*/
public enum EnumChatFormat {
BLACK(0, '0', "000000"),
DARK_BLUE(1, '1', "0000AA"),
DARK_GREEN(2, '2', "00AA00"),
DARK_AQUA(3, '3', "00AAAA"),
DARK_RED(4, '4', "AA0000"),
DARK_PURPLE(5, '5', "AA00AA"),
GOLD(6, '6', "FFAA00"),
GRAY(7, '7', "AAAAAA"),
DARK_GRAY(8, '8', "555555"),
BLUE(9, '9', "5555FF"),
GREEN(10, 'a', "55FF55"),
AQUA(11, 'b', "55FFFF"),
RED(12, 'c', "FF5555"),
LIGHT_PURPLE(13, 'd', "FF55FF"),
YELLOW(14, 'e', "FFFF55"),
WHITE(15, 'f', "FFFFFF"),
OBFUSCATED(16, 'k'),
BOLD(17, 'l'),
STRIKETHROUGH(18, 'm'),
UNDERLINE(19, 'n'),
ITALIC(20, 'o'),
RESET(21, 'r');
BLACK('0', "000000"),
DARK_BLUE('1', "0000AA"),
DARK_GREEN('2', "00AA00"),
DARK_AQUA('3', "00AAAA"),
DARK_RED('4', "AA0000"),
DARK_PURPLE('5', "AA00AA"),
GOLD('6', "FFAA00"),
GRAY('7', "AAAAAA"),
DARK_GRAY('8', "555555"),
BLUE('9', "5555FF"),
GREEN('a', "55FF55"),
AQUA('b', "55FFFF"),
RED('c', "FF5555"),
LIGHT_PURPLE('d', "FF55FF"),
YELLOW('e', "FFFF55"),
WHITE('f', "FFFFFF"),
OBFUSCATED('k'),
BOLD('l'),
STRIKETHROUGH('m'),
UNDERLINE('n'),
ITALIC('o'),
RESET('r');
/** Creating a constant to avoid memory allocations on each request */
private static final EnumChatFormat[] VALUES = values();
/** The symbol minecraft uses to colorize text */
public static final char COLOR_CHAR = 0x00a7;
/** The color symbol in form of a string */
public static final String COLOR_STRING = String.valueOf(COLOR_CHAR);
//network id of the color
private final int networkId;
//characer representing the color
/** Character representing the color or magic code */
private final char character;
//red value of this color
private final int red;
/** Red value of this constant, 0 for magic codes */
private final short red;
/** Green value of this constant, 0 for magic codes */
private final short green;
/** Blue value of this constant, 0 for magic codes */
private final short blue;
//green value of this color
private final int green;
//blue value of this color
private final int blue;
//hex code as string
/** Color as 6-digit hex code, null for magic codes */
private final String hexCode;
//color char followed by color's character
/** Color symbol followed by constant's character */
private final String chatFormat;
/**
* Constructs new instance with given parameters
* @param networkId - network id of the color
* @param character - character representing the color
* @param hexColor - hex code of the color
* Constructs new color instance with given character and hex code
* @param character
* character which the color goes by
* @param hexCode
* 6-digit hex code of the color
*/
private EnumChatFormat(int networkId, char character, String hexCode) {
this.networkId = networkId;
EnumChatFormat(char character, String hexCode) {
this.character = character;
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
this.hexCode = hexCode;
int hexColor = Integer.parseInt(hexCode, 16);
red = (hexColor >> 16) & 0xFF;
green = (hexColor >> 8) & 0xFF;
blue = hexColor & 0xFF;
red = (short) ((hexColor >> 16) & 0xFF);
green = (short) ((hexColor >> 8) & 0xFF);
blue = (short) (hexColor & 0xFF);
}
/**
* Constructs new instance with given parameters
* @param networkId - network id of the color
* @param character - character representing the color
* Constructs new magic code instance with given character
* @param character
* character representing the magic code
*/
private EnumChatFormat(int networkId, char character) {
this.networkId = networkId;
EnumChatFormat(char character) {
this.character = character;
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
red = 0;
@@ -85,63 +87,58 @@ public enum EnumChatFormat {
blue = 0;
hexCode = null;
}
/**
* Returns network id of this color
* @return network if of this color
* Returns red value of this color code
* @return red value
*/
public int getNetworkId() {
return networkId;
}
/**
* Returns red value of this color
* @return red value
*/
public int getRed() {
public short getRed() {
return red;
}
/**
* Returns green value of this color
* @return green value
* Returns green value of this color code
* @return green value
*/
public int getGreen() {
public short getGreen() {
return green;
}
/**
* Returns blue value of this color
* @return blue value
* Returns blue value of this color code
* @return blue value
*/
public int getBlue() {
public short getBlue() {
return blue;
}
/**
* Returns enum value based on inserted color or null if character is not valid
* @param c - color code character (0-9, a-f, k-o, r)
* @return instance from the character
* Returns enum value based on provided character or null if character is not valid
* @param c
* color code character (0-9, a-f, k-o, r)
* @return instance from the character or null if character is not valid
*/
public static EnumChatFormat getByChar(char c) {
for (EnumChatFormat format : values()) {
for (EnumChatFormat format : VALUES) {
if (format.character == c) return format;
}
return null;
}
/**
* Returns enum value of last colors used in given string
* @param string - string to check last colors of
* @return last used color code in given string
* Returns enum value of last colors used in given string.
* If it's null, empty or does not contain color codes, WHITE is returned.
* @param string
* string to check last colors of
* @return last used color code in given string or WHITE if nothing is found
*/
public static EnumChatFormat lastColorsOf(String string) {
if (string == null || string.length() == 0) return EnumChatFormat.WHITE;
String legacyText = RGBUtils.getInstance().convertRGBtoLegacy(string); //translating RGB into legacy for nametags
String legacyText = RGBUtils.getInstance().convertRGBtoLegacy(string);
String last = getLastColors(legacyText);
if (last.length() > 0) {
char c = last.toCharArray()[1];
for (EnumChatFormat e : values()) {
for (EnumChatFormat e : VALUES) {
if (e.character == c) return e;
}
}
@@ -173,20 +170,29 @@ public enum EnumChatFormat {
}
/**
* Returns enum value with exact red, green and blue values or null if no match
* @param red - exact red value
* @param green - exact green value
* @param blue - exact blue value
* @return enum value or null if no such combination exists
* Returns enum value with exact red, green and blue values or null if no match was found
* @param red
* exact red value
* @param green
* exact green value
* @param blue
* exact blue value
* @return enum value or null if no such combination exists
*/
public static EnumChatFormat fromRGBExact(int red, int green, int blue){
for (EnumChatFormat format : values()) {
for (EnumChatFormat format : VALUES) {
if (format.red == red && format.green == green && format.blue == blue) return format;
}
return null;
}
//code taken from bukkit, so it can work on bungee too
/**
* Color translation method taken from bukkit, which converts '&' symbol into
* the actual color character if followed by a valid color character.
* @param textToTranslate
* text to replace color symbol in
* @return colorized string from provided text
*/
public static String color(String textToTranslate){
if (textToTranslate == null) return null;
if (!textToTranslate.contains("&")) return textToTranslate;
@@ -199,13 +205,25 @@ public enum EnumChatFormat {
}
return new String(b);
}
/**
* Turns back the color symbol into '&' symbol in provided text.
* @param text
* text to revert colors in
* @return reverted text
*/
public static String decolor(String text) {
if (text == null) return null;
if (!text.contains(COLOR_STRING)) return text;
return text.replace(COLOR_CHAR, '&');
}
//code taken from bukkit, so it can work on bungee too
/**
* Code taken from bukkit, which returns last color codes used in provided text.
* @param input
* text to get last colors from
* @return last colors used in provided text or empty string if nothing was found
*/
public static String getLastColors(String input) {
StringBuilder result = new StringBuilder();
int length = input.length();
@@ -1,11 +1,8 @@
package me.neznamy.tab.api.chat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import me.neznamy.tab.api.util.Preconditions;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
@@ -14,6 +11,7 @@ import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.chat.ChatClickable.EnumClickAction;
import me.neznamy.tab.api.chat.ChatHoverable.EnumHoverAction;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import org.json.simple.parser.ParseException;
/**
* A class representing the n.m.s.IChatBaseComponent class to make work with it much easier
@@ -21,21 +19,21 @@ import me.neznamy.tab.api.chat.rgb.RGBUtils;
@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<>();
//constants
private static final String EMPTY_TRANSLATABLE = "{\"translate\":\"\"}";
private static final String EMPTY_TEXT = "{\"text\":\"\"}";
private static final List<IChatBaseComponent> EMPTY_LIST = new ArrayList<>(0);
//component text
/** Text of the component */
private String text;
/** Chat modifier containing color, magic codes, hover and click event */
private ChatModifier modifier = new ChatModifier();
//extra components
/** Extra components used in "extra" field */
private List<IChatBaseComponent> extra;
/**
@@ -46,7 +44,8 @@ public class IChatBaseComponent {
/**
* Constructs a new component which is a clone of provided component
* @param component - component to clone
* @param component
* component to clone
*/
public IChatBaseComponent(IChatBaseComponent component) {
this.text = component.text;
@@ -58,25 +57,29 @@ public class IChatBaseComponent {
/**
* Constructs new instance with given text
* @param text - text to display
* @param text
* text to display
*/
public IChatBaseComponent(String text) {
this.text = text;
}
/**
* Returns list of extra components or null if none are defined
* @return list of extras
* 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 EMPTY_LIST;
if (extra == null) return Collections.emptyList();
return extra;
}
/**
* Sets full list of extra components to given list
* @param components - components to use as extra
* @return self
* 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
@@ -86,105 +89,126 @@ public class IChatBaseComponent {
/**
* Appends provided component as extra component
* @param child - component to append
* @return self
* @param child
* component to append
*/
public IChatBaseComponent addExtra(IChatBaseComponent child) {
public void addExtra(IChatBaseComponent child) {
if (extra == null) extra = new ArrayList<>();
extra.add(child);
return this;
}
/**
* Returns text of this component
* @return 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;
}
/**
* Sets text of this component
* @param text - text to show
* @return self
* Changes text of this component
* @param text
* text to show
*/
public IChatBaseComponent setText(String text) {
public void setText(String text) {
this.text = text;
return this;
}
/**
* Deserializes string and returns created component
* @param json - serialized string
* @return Deserialized component
* 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;
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
return new IChatBaseComponent(json.substring(1, json.length()-1));
}
JSONObject jsonObject;
try {
if (json == null) return null;
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
return new IChatBaseComponent(json.substring(1, json.length()-1));
}
JSONObject jsonObject = (JSONObject) new JSONParser().parse(json);
IChatBaseComponent component;
if (jsonObject.containsKey("type")) {
return new ChatComponentEntity((String) jsonObject.get("type"), UUID.fromString((String) jsonObject.get("id")), IChatBaseComponent.deserialize(((JSONObject) jsonObject.get("name")).toString()).toFlatText());
}
component = new IChatBaseComponent();
component.setText((String) jsonObject.get("text"));
component.modifier.setBold(getBoolean(jsonObject, "bold"));
component.modifier.setItalic(getBoolean(jsonObject, "italic"));
component.modifier.setUnderlined(getBoolean(jsonObject, "underlined"));
component.modifier.setStrikethrough(getBoolean(jsonObject, "strikethrough"));
component.modifier.setObfuscated(getBoolean(jsonObject, "obfuscated"));
component.modifier.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();
component.modifier.onClick(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");
component.modifier.onHover(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 + "\"";
component.addExtra(deserialize(string));
}
}
return component;
} catch (Exception e) {
jsonObject = (JSONObject) new JSONParser().parse(json);
} catch (ParseException e) {
TabAPI.getInstance().logError("Failed to deserialize json component " + json, e);
return null;
}
IChatBaseComponent component;
if (jsonObject.containsKey("type")) {
return new ChatComponentEntity((String) jsonObject.get("type"), UUID.fromString((String) jsonObject.get("id")), IChatBaseComponent.deserialize(jsonObject.get("name").toString()).toFlatText());
}
component = new IChatBaseComponent();
component.setText((String) jsonObject.get("text"));
component.modifier.setBold(getBoolean(jsonObject, "bold"));
component.modifier.setItalic(getBoolean(jsonObject, "italic"));
component.modifier.setUnderlined(getBoolean(jsonObject, "underlined"));
component.modifier.setStrikethrough(getBoolean(jsonObject, "strikethrough"));
component.modifier.setObfuscated(getBoolean(jsonObject, "obfuscated"));
component.modifier.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();
component.modifier.onClick(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");
component.modifier.onHover(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 + "\"";
component.addExtra(deserialize(string));
}
}
return component;
}
/**
* Returns boolean value of requested key
* @param jsonObject - object to get value from
* @param key - name of key
* @return value from json object or null if not present
* 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) {
String value = String.valueOf(jsonObject.getOrDefault(key, null));
return "null".equals(value) ? null : Boolean.parseBoolean(value);
}
/**
* 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();
@@ -196,28 +220,33 @@ public class IChatBaseComponent {
}
/**
* Serializes this component with colors based on client version
* @param clientVersion - client version
* @return Serialized string
* Serializes this component with colors based on client version.
* If client version is <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
* @param clientVersion - client version
* @param sendTranslatableIfEmpty - if empty translatable should be sent if text is empty or not
* @return Serialized string
* Serializes this component with colors based on client version.
* If client version is <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 EMPTY_TRANSLATABLE;
return "{\"translate\":\"\"}";
} else {
return EMPTY_TEXT;
return "{\"text\":\"\"}";
}
}
}
@@ -242,8 +271,9 @@ public class IChatBaseComponent {
/**
* Returns organized component from colored text
* @param originalText - text to convert
* @return organized component from colored text
* @param originalText
* text to convert
* @return organized component from colored text
*/
public static IChatBaseComponent fromColoredText(String originalText){
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText), false);
@@ -328,9 +358,11 @@ public class IChatBaseComponent {
/**
* Returns true if text contains legacy color request at defined RGB index start
* @param text - text to check
* @param i - current index start
* @return true if legacy color is defined, false if not
* @param text
* text to check
* @param i
* current index start
* @return true if legacy color is defined, false if not
*/
private static boolean containsLegacyCode(String text, int i) {
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
@@ -338,8 +370,8 @@ public class IChatBaseComponent {
}
/**
* Converts this component into a simple text with legacy colors (closest match if color is set to RGB)
* @return The simple text format
* 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();
@@ -348,11 +380,13 @@ public class IChatBaseComponent {
}
/**
* Appends text to string builder, might also add color and magic codes if different
* than 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
* Appends text to string builder, might also add color and magic codes if they are different
* from previous component in chain.
* @param builder
* builder to append text to
* @param previousFormatting
* colors and magic codes in previous component
* @return wew formatting, might be identical to previous one
*/
private String append(StringBuilder builder, String previousFormatting) {
String formatting = previousFormatting;
@@ -372,7 +406,7 @@ public class IChatBaseComponent {
/**
* Returns colors and magic codes of this component
* @return used colors and magic codes
* @return used colors and magic codes
*/
private String getFormatting() {
StringBuilder builder = new StringBuilder();
@@ -390,7 +424,7 @@ public class IChatBaseComponent {
/**
* Returns raw text without colors, only works correctly when component is organized
* @return raw text in this component
* @return raw text in this component and all child components
*/
public String toRawText() {
StringBuilder builder = new StringBuilder();
@@ -403,11 +437,11 @@ public class IChatBaseComponent {
/**
* Converts the component into flat text with used colors (including rgb) and magic codes
* @return converted text
* @return converted text
*/
public String toFlatText() {
StringBuilder builder = new StringBuilder();
if (modifier.getColor() != null) builder.append("#" + modifier.getColor().getHexCode());
if (modifier.getColor() != null) builder.append("#").append(modifier.getColor().getHexCode());
builder.append(modifier.getMagicCodes());
if (text != null) builder.append(text);
for (IChatBaseComponent child : getExtra()) {
@@ -420,8 +454,9 @@ public class IChatBaseComponent {
* Returns the most optimized component based on text. Returns null if text is null,
* organized component if RGB colors are used or simple component with only text field
* containing the whole text when no RGB colors are used
* @param text - text to create component from
* @return The most performance-optimized component based on text
* @param text
* text to create component from
* @return The most performance-optimized component based on text
*/
public static IChatBaseComponent optimizedComponent(String text){
if (text == null) return null;
@@ -1,89 +1,117 @@
package me.neznamy.tab.api.chat;
import me.neznamy.tab.api.util.Preconditions;
/**
* A class used to represent any combination of RGB colors
* A class representing a component color, which can be either RGB or legacy code.
*/
public class TextColor {
//rgb values, only initializing when they are actually requested
private int red = -1;
private int green = -1;
private int blue = -1;
/**
* RGB values as a single number of 3 8-bit numbers (0-255).
* It is only initialized if colors are actually used to avoid
* unnecessary memory allocations with string operations.
*/
private int rgb = -1;
//closest legacy color
/** Closest legacy color to this color object. */
private EnumChatFormat legacyColor;
//6-digit combination of hexadecimal numbers
/** HEX code of this color as a 6-digit hexadecimal string without "#" prefix. */
private String hexCode;
//true if legacy color was forced via constructor, false if automatically
/**
* Boolean value whether the legacy color was forced with constructor or should be
* automatically assigned as closest color.
* This value is used in gradients when converting text for legacy players. */
private boolean legacyColorForced;
/**
* Constructs new instance as a clone of the provided color.
* @param color
* color to create a clone of
* @throws IllegalArgumentException
* if color is {@code null}
*/
public TextColor(TextColor color) {
red = color.red;
green = color.green;
blue = color.blue;
Preconditions.checkNotNull(color, "color cannot be null");
rgb = color.rgb;
legacyColor = color.legacyColor;
hexCode = color.hexCode;
legacyColorForced = color.legacyColorForced;
}
/**
* Constructs new instance based on hex code as string
* @param hexCode - a 6-digit combination of hex numbers
* Constructs new instance from provided 6-digit hex code string
* @param hexCode
* a 6-digit combination of hex numbers as a string
* @throws IllegalArgumentException
* if hexCode is {@code null}
*/
public TextColor(String hexCode) {
Preconditions.checkNotNull(hexCode, "hex code cannot be null");
this.hexCode = hexCode;
}
/**
* Constructs new instance from given 6-digit hex code and legacy color
* @param hexCode - 6-digit hex code
* @param legacyColor color to use for legacy clients
* Constructs new instance from provided 6-digit hex code and forced legacy color
* @param hexCode
* 6-digit combination of hex numbers as a string
* @param legacyColor
* color to use for legacy clients instead of using the closest legacy color
* @throws IllegalArgumentException
* if {@code hexCode} is {@code null} or {@code legacyColor} is {@code null}
*/
public TextColor(String hexCode, EnumChatFormat legacyColor) {
Preconditions.checkNotNull(hexCode, "hex code cannot be null");
Preconditions.checkNotNull(legacyColor, "legacy color cannot be null");
this.hexCode = hexCode;
this.legacyColorForced = true;
this.legacyColor = legacyColor;
}
/**
* Constructs new instance with given parameter
* @param legacyColor - legacy color
* Constructs new instance from provided legacy color
* @param legacyColor
* legacy color to construct the instance from
* @throws IllegalArgumentException
* if {@code legacyColor} is {@code null}
*/
public TextColor(EnumChatFormat legacyColor) {
this.red = legacyColor.getRed();
this.green = legacyColor.getGreen();
this.blue = legacyColor.getBlue();
Preconditions.checkNotNull(legacyColor, "legacy color cannot be null");
this.rgb = (legacyColor.getRed() << 16) + (legacyColor.getGreen() << 8) + legacyColor.getBlue();
this.hexCode = legacyColor.getHexCode();
}
/**
* Constructs new instance with given parameters
* @param red - red value
* @param green - green value
* @param blue - blue value
* Constructs new instance with red, green and blue values
* @param red
* red value
* @param green
* green value
* @param blue
* blue value
* @throws IllegalArgumentException
* if {@code red}, {@code green} or {@code blue} is out of range ({@code 0-255})
*/
public TextColor(int red, int green, int blue) {
this.red = red;
this.green = green;
this.blue = blue;
Preconditions.checkRange(red, 0, 255, "red color");
Preconditions.checkRange(green, 0, 255, "green color");
Preconditions.checkRange(blue, 0, 255, "blue color");
this.rgb = (red << 16) + (green << 8) + blue;
}
/**
* Gets closest legacy color based on rgb values
* Loads the closest legacy color based currently provided values
*/
private EnumChatFormat getClosestColor(int red, int green, int blue) {
private EnumChatFormat loadClosestColor() {
double minMaxDist = 9999;
double maxDist;
EnumChatFormat closestColor = EnumChatFormat.WHITE;
for (EnumChatFormat color : EnumChatFormat.values()) {
int rDiff = color.getRed() - red;
int gDiff = color.getGreen() - green;
int bDiff = color.getBlue() - blue;
if (rDiff < 0) rDiff = -rDiff;
if (gDiff < 0) gDiff = -gDiff;
if (bDiff < 0) bDiff = -bDiff;
int rDiff = Math.abs(color.getRed() - getRed());
int gDiff = Math.abs(color.getGreen() - getGreen());
int bDiff = Math.abs(color.getBlue() - getBlue());
maxDist = rDiff;
if (gDiff > maxDist) maxDist = gDiff;
if (bDiff > maxDist) maxDist = bDiff;
@@ -96,72 +124,56 @@ public class TextColor {
}
/**
* Returns amount of red
* @return amount of red
* Returns {@code red} value
* @return red value
*/
public int getRed() {
if (red == -1) {
int hexColor = Integer.parseInt(hexCode, 16);
red = (hexColor >> 16) & 0xFF;
green = (hexColor >> 8) & 0xFF;
blue = hexColor & 0xFF;
}
return red;
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
return (rgb >> 16) & 0xFF;
}
/**
* Returns amount of green
* @return amount of green
* Returns {@code green} value
* @return green value
*/
public int getGreen() {
if (green == -1) {
int hexColor = Integer.parseInt(hexCode, 16);
red = (hexColor >> 16) & 0xFF;
green = (hexColor >> 8) & 0xFF;
blue = hexColor & 0xFF;
}
return green;
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
return (rgb >> 8) & 0xFF;
}
/**
* Returns amount of blue
* @return amount of blue
* Returns {@code blue} value
* @return blue value
*/
public int getBlue() {
if (blue == -1) {
int hexColor = Integer.parseInt(hexCode, 16);
red = (hexColor >> 16) & 0xFF;
green = (hexColor >> 8) & 0xFF;
blue = hexColor & 0xFF;
}
return blue;
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
return rgb & 0xFF;
}
/**
* Returns defined legacy color
* @return defined legacy color
* Returns the closest legacy color of this color object.
* If the color was defined in constructor, it's returned.
* Otherwise, the closest color is calculated the then returned.
* @return closest legacy color
*/
public EnumChatFormat getLegacyColor() {
if (legacyColor == null) {
legacyColor = getClosestColor(getRed(), getGreen(), getBlue());
}
if (legacyColor == null) legacyColor = loadClosestColor();
return legacyColor;
}
/**
* Returns hex code of this color
* @return hex code of this color
* Returns the rgb combination as a 6-digit hex code string
* @return the rgb combination as a 6-digit hex code string
*/
public String getHexCode() {
if (hexCode == null) {
hexCode = String.format("%06X", (red << 16) + (green << 8) + blue);
}
if (hexCode == null) hexCode = String.format("%06X", rgb);
return hexCode;
}
/**
* Converts the color into a valid color value used in color field in chat component
* @return the color converted into string acceptable by client
* Converts the color into a valid color value used in color field in chat component.
* That is either 6-digit hex code prefixed with '#', or lowercase legacy color.
* @return the color serialized for use in chat component
*/
public String toString() {
EnumChatFormat legacyEquivalent = EnumChatFormat.fromRGBExact(getRed(), getGreen(), getBlue());
@@ -170,21 +182,24 @@ public class TextColor {
return legacyEquivalent.toString().toLowerCase();
}
return "#" + getHexCode();
}
/**
* Returns true if legacy color was forced with a constructor, false if automatically
* @return true if forced, false if not
* Returns true if legacy color was forced with a constructor, false if not
* @return true if forced, false if not
*/
public boolean isLegacyColorForced() {
return legacyColorForced;
}
/**
* Reads the string and turns into text color. String is either #RRGGBB or a lowercased legacy color
* @param string - string from color field in chat component
* @return An instance from specified string
* Reads the string as it appears in chat component and turns it into the color object.
* If the entered string is null, returns null.
* If it's prefixed with '#', it's considered as a hex code.
* Otherwise, it is considered being a lowercase legacy color.
* @param string
* string from color field in chat component
* @return An instance from specified string or null if string is null
*/
public static TextColor fromString(String string) {
if (string == null) return null;
@@ -4,12 +4,10 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.chat.TextColor;
import me.neznamy.tab.api.chat.rgb.format.BukkitFormat;
import me.neznamy.tab.api.chat.rgb.format.CMIFormat;
import me.neznamy.tab.api.chat.rgb.format.HtmlFormat;
import me.neznamy.tab.api.chat.rgb.format.HtmlFormat2;
import me.neznamy.tab.api.chat.rgb.format.RGBFormatter;
import me.neznamy.tab.api.chat.rgb.format.UnnamedFormat1;
import me.neznamy.tab.api.chat.rgb.gradient.CMIGradient;
@@ -22,26 +20,30 @@ import me.neznamy.tab.api.chat.rgb.gradient.KyoriGradient;
*/
public class RGBUtils {
//instance of class
private final static RGBUtils instance = new RGBUtils();
/** Instance of the class */
private static final RGBUtils instance = new RGBUtils();
//rgb formatters
/** Registered RGB formatters */
private final RGBFormatter[] formats;
//gradient patterns
/** Registered gradient patterns */
private final GradientPattern[] gradients;
//TAB's RGB format
/** 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() {
formats = new RGBFormatter[] {
new BukkitFormat(),
new CMIFormat(),
new UnnamedFormat1(),
new HtmlFormat(),
new HtmlFormat2()
new HtmlFormat()
};
gradients = new GradientPattern[] {
//{#RRGGBB>}text{#RRGGBB<}
@@ -60,16 +62,22 @@ public class RGBUtils {
/**
* Returns instance of this class
* @return instance
* @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
* Applies all RGB formats and gradients to text and returns it.
* This method is called on every property initialization to convert formats
* once instead of on every refresh, if possible. Because of that, parameter
* {@code ignorePlaceholders} exists to not break raw placeholder identifiers
* @param text
* original text
* @param ignorePlaceholders
* whether placeholders should be ignored or not
* @return text where everything is converted to #RRGGBB
*/
public String applyFormats(String text, boolean ignorePlaceholders) {
String replaced = text;
@@ -81,8 +89,18 @@ public class RGBUtils {
}
return replaced;
}
/**
* Converts TAB's RGB format (#RRGGBB) into bukkit one (&x&r&r&g&g&b&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
@@ -90,21 +108,22 @@ public class RGBUtils {
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));
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 IChatBaseComponent.fromColoredText(text).toLegacyText();
return convertRGBtoLegacy(text);
}
}
/**
* Converts all hex codes in given string to legacy codes
* @param text - text to translate
* @return - translated text
* @param text
* text to convert
* @return translated text
*/
public String convertRGBtoLegacy(String text) {
if (text == null) return null;
@@ -132,7 +151,14 @@ public class RGBUtils {
}
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) {
if (string.length() != 6) return false;
for (int i=0; i<6; i++) {
@@ -144,9 +170,11 @@ public class RGBUtils {
/**
* Returns true if text contains legacy color request at defined RGB index start
* @param text - text to check
* @param i - current index start
* @return true if legacy color is defined, false if not
* @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;
@@ -10,7 +10,7 @@ import me.neznamy.tab.api.chat.EnumChatFormat;
*/
public class BukkitFormat implements RGBFormatter {
private final Pattern pattern = Pattern.compile("[" + EnumChatFormat.COLOR_CHAR + "&]x[[" + EnumChatFormat.COLOR_CHAR + "&]\\p{XDigit}]{12}");
private final Pattern pattern = Pattern.compile("[" + EnumChatFormat.COLOR_CHAR + "&]x[" + EnumChatFormat.COLOR_CHAR + "&\\p{XDigit}]{12}");
@Override
public String reformat(String text) {
@@ -18,9 +18,9 @@ public class BukkitFormat implements RGBFormatter {
String replaced = text;
Matcher m = pattern.matcher(replaced);
while (m.find()) {
String hexcode = m.group();
String fixed = new String(new char[] {'#', hexcode.charAt(3), hexcode.charAt(5), hexcode.charAt(7), hexcode.charAt(9), hexcode.charAt(11), hexcode.charAt(13)});
replaced = replaced.replace(hexcode, fixed);
String hexCode = m.group();
String fixed = new String(new char[] {'#', hexCode.charAt(3), hexCode.charAt(5), hexCode.charAt(7), hexCode.charAt(9), hexCode.charAt(11), hexCode.charAt(13)});
replaced = replaced.replace(hexCode, fixed);
}
return replaced;
}
@@ -8,7 +8,7 @@ import java.util.regex.Pattern;
*/
public class CMIFormat implements RGBFormatter {
private final Pattern pattern = Pattern.compile("\\{#[0-9a-fA-F]{6}\\}");
private final Pattern pattern = Pattern.compile("\\{#[0-9a-fA-F]{6}}");
@Override
public String reformat(String text) {
@@ -16,9 +16,9 @@ public class CMIFormat implements RGBFormatter {
String replaced = text;
Matcher m = pattern.matcher(replaced);
while (m.find()) {
String hexcode = m.group();
String fixed = hexcode.substring(2, 8);
replaced = replaced.replace(hexcode, "#" + fixed);
String hexCode = m.group();
String fixed = hexCode.substring(2, 8);
replaced = replaced.replace(hexCode, "#" + fixed);
}
return replaced;
}
@@ -16,9 +16,9 @@ public class HtmlFormat implements RGBFormatter {
Matcher m = pattern.matcher(text);
String replaced = text;
while (m.find()) {
String hexcode = m.group();
String fixed = hexcode.substring(2, 8);
replaced = replaced.replace(hexcode, "#" + fixed);
String hexCode = m.group();
String fixed = hexCode.substring(2, 8);
replaced = replaced.replace(hexCode, "#" + fixed);
}
return replaced;
}
@@ -1,25 +0,0 @@
package me.neznamy.tab.api.chat.rgb.format;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Formatter for <#RRGGBB>
*/
public class HtmlFormat2 implements RGBFormatter {
private final Pattern pattern = Pattern.compile("<#[0-9a-fA-F]{6}>");
@Override
public String reformat(String text) {
if (!text.contains("<#")) return text;
Matcher m = pattern.matcher(text);
String replaced = text;
while (m.find()) {
String hexcode = m.group();
String fixed = hexcode.substring(2, 8);
replaced = replaced.replace(hexcode, "#" + fixed);
}
return replaced;
}
}
@@ -10,5 +10,5 @@ public interface RGBFormatter {
* @param text - text to format
* @return reformatted text
*/
public String reformat(String text);
String reformat(String text);
}
@@ -6,11 +6,11 @@ import java.util.regex.Pattern;
public class CMIGradient extends CommonGradient {
//pattern for {#RRGGBB<>}
private final Pattern shortcutPattern = Pattern.compile("\\{#[0-9a-fA-F]{6}<>\\}");
private final Pattern shortcutPattern = Pattern.compile("\\{#[0-9a-fA-F]{6}<>}");
public CMIGradient() {
super(Pattern.compile("\\{#[0-9a-fA-F]{6}>\\}[^\\{]*\\{#[0-9a-fA-F]{6}<\\}"),
Pattern.compile("\\{#[0-9a-fA-F]{6}\\|.>\\}[^\\{]*\\{#[0-9a-fA-F]{6}<\\}"),
super(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, 10, 8);
}
@@ -13,7 +13,7 @@ public interface GradientPattern {
* @param text - text to be reformatted
* @return reformatted text
*/
public String applyPattern(String text, boolean ignorePlaceholders);
String applyPattern(String text, boolean ignorePlaceholders);
/**
* Returns gradient text based on start color, text and end color
@@ -22,18 +22,18 @@ public interface GradientPattern {
* @param end - end color
* @return reformatted text
*/
public default String asGradient(TextColor start, String text, TextColor end) {
default String asGradient(TextColor start, String text, TextColor end) {
//lazy support for magic codes in gradients
String magicCodes = EnumChatFormat.getLastColors(text);
String decolorized = text.substring(magicCodes.length());
String deColorized = text.substring(magicCodes.length());
StringBuilder sb = new StringBuilder();
int length = decolorized.length();
int length = deColorized.length();
if (length == 1) {
sb.append("#");
sb.append(new TextColor(start.getRed(), start.getGreen(), start.getBlue()).getHexCode());
if (start.isLegacyColorForced()) sb.append("|" + start.getLegacyColor().getCharacter());
if (start.isLegacyColorForced()) sb.append("|").append(start.getLegacyColor().getCharacter());
sb.append(magicCodes);
sb.append(decolorized);
sb.append(deColorized);
return sb.toString();
}
for (int i=0; i<length; i++) {
@@ -42,9 +42,9 @@ public interface GradientPattern {
int blue = (int) (start.getBlue() + (float)(end.getBlue() - start.getBlue())/(length-1)*i);
sb.append("#");
sb.append(new TextColor(red, green, blue).getHexCode());
if (start.isLegacyColorForced()) sb.append("|" + start.getLegacyColor().getCharacter());
if (start.isLegacyColorForced()) sb.append("|").append(start.getLegacyColor().getCharacter());
sb.append(magicCodes);
sb.append(decolorized.charAt(i));
sb.append(deColorized.charAt(i));
}
return sb.toString();
}
@@ -51,23 +51,19 @@ public abstract class ConfigurationFile {
protected ConfigurationFile(InputStream source, File destination) throws IOException {
if (destination == null) throw new IllegalArgumentException("Destination cannot be null");
this.file = destination;
if (file.getParentFile() != null) file.getParentFile().mkdirs();
if (!file.exists()) {
if (source == null) throw new IllegalStateException("File does not exist and source is null");
if (file.getParentFile() != null) Files.createDirectories(file.getParentFile().toPath());
if (!file.exists() && source == null) throw new IllegalStateException("File does not exist and source is null");
if (file.createNewFile()) {
// Files.copy(source, file.toPath());
//avoiding MalformedInputException thrown on fabric due to file not having UTF8 encoding by default
//also automatically using the encoding so users don't have to worry about it anymore
file.createNewFile();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))){
String line;
while ((line = reader.readLine()) != null) {
writer.write(line + "\n");
}
reader.close();
writer.close();
}
}
detectHeader();
}
@@ -155,8 +151,8 @@ public abstract class ConfigurationFile {
* @param map
* map to get value from
* @param key
* case insensitive key name
* @return map value from case insensitive key
* case-insensitive key name
* @return map value from case-insensitive key
*/
private Object getIgnoreCase(Map<Object, Object> map, String key) {
for (Entry<Object, Object> entry : map.entrySet()) {
@@ -312,7 +308,6 @@ public abstract class ConfigurationFile {
public <K, V> Map<K, V> getConfigurationSection(String path) {
if (path == null || path.length() == 0) return (Map<K, V>) values;
Object value = getObject(path, null);
if (value == null) return new LinkedHashMap<>();
if (value instanceof Map) {
return (Map<K, V>) value;
} else {
@@ -360,11 +355,11 @@ public abstract class ConfigurationFile {
private Map<String, Object> set(Map<String, Object> map, String path, Object value) {
if (path.contains(".")) {
String keyWord = getRealKey(map, path.split("\\.")[0]);
Object submap = map.get(keyWord);
if (!(submap instanceof Map)) {
submap = new LinkedHashMap<>();
Object subMap = map.get(keyWord);
if (!(subMap instanceof Map)) {
subMap = new LinkedHashMap<>();
}
map.put(keyWord, set((Map<String, Object>) submap, path.substring(keyWord.length()+1, path.length()), value));
map.put(keyWord, set((Map<String, Object>) subMap, path.substring(keyWord.length()+1), value));
} else {
if (value == null) {
map.remove(getRealKey(map, path));
@@ -386,20 +381,19 @@ public abstract class ConfigurationFile {
* @return The real key name
*/
private String getRealKey(Map<?, ?> map, String key) {
for (Object mapkey : map.keySet()) {
if (mapkey.toString().equalsIgnoreCase(key)) return mapkey.toString();
for (Object mapKey : map.keySet()) {
if (mapKey.toString().equalsIgnoreCase(key)) return mapKey.toString();
}
return key;
}
/**
* Detects header of a file (first lines of file starting with #).
*
* @return list of comment lines on top
*
* @throws IOException
* if I/O operation fails
*/
private List<String> detectHeader() throws IOException {
private void detectHeader() throws IOException {
header = new ArrayList<>();
for (String line : Files.readAllLines(file.toPath())) {
if (line.startsWith("#")) {
@@ -408,7 +402,6 @@ public abstract class ConfigurationFile {
break;
}
}
return header;
}
/**
@@ -10,7 +10,6 @@ import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
@@ -27,9 +26,6 @@ public class YamlConfigurationFile extends ConfigurationFile {
/** SnakeYAML instance */
private static final Yaml yaml;
/**
* Initializes SnakeYAML instance with BLOCK flowstyle dumper option
*/
static {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
@@ -53,13 +49,12 @@ public class YamlConfigurationFile extends ConfigurationFile {
* @throws IOException
* if I/O operation with the file unexpectedly fails
*/
@SuppressWarnings("unchecked")
public YamlConfigurationFile(InputStream source, File destination) throws YAMLException, IOException {
super(source, destination);
FileInputStream input = null;
try {
input = new FileInputStream(file);
values = (Map<String, Object>) yaml.load(input);
values = yaml.load(input);
if (values == null) values = new LinkedHashMap<>();
input.close();
} catch (YAMLException e) {
@@ -0,0 +1,53 @@
package me.neznamy.tab.api.event;
/**
* The event bus. This is used to register event listeners/handlers for
* handling events that may be fired by TAB.
*/
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}
* must meet the following criteria:
* <ul>
* <li>It must be <strong>public</strong>.</li>
* <li>It must <strong>not</strong> be abstract.</li>
* <li>It must have exactly <strong>one</strong> parameter.</li>
* <li>Its single parameter must be <strong>an event</strong>.</li>
* </ul>
* If a method does not meet the above criteria, an exception <strong>will</strong>
* be thrown.
*
* @param listener the listener to register
* @throws RuntimeException if a method does not meet the criteria
*/
void register(Object listener);
/**
* Registers the given handler to this event bus.
*
* @param type the class type of the event
* @param handler the handler to register
* @param <E> the type of the event
*/
<E extends TabEvent> void register(Class<E> type, EventHandler<E> handler);
/**
* Scans the given listener for any method that is annotated with
* {@link Subscribe} and unregisters it from this event bus.
*
* @param listener the listener to unregister
*/
void unregister(Object listener);
/**
* Unregisters the given handler from this event bus.
*
* @param handler the handler to unregister
* @param <E> the type of the event
*/
<E extends TabEvent> void unregister(EventHandler<E> handler);
}
@@ -0,0 +1,18 @@
package me.neznamy.tab.api.event;
/**
* A handler for an event. This allows listening for events in a more
* functional style.
*
* @param <E> the type of event this handler handles
*/
@FunctionalInterface
public interface EventHandler<E> {
/**
* Called when the given event is fired from an event bus.
*
* @param event the event to handle
*/
void handle(E event);
}
@@ -0,0 +1,14 @@
package me.neznamy.tab.api.event;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Subscribes to a {@link TabEvent}.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
}
@@ -0,0 +1,10 @@
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
* interface.
*/
public interface TabEvent {
}
@@ -0,0 +1,24 @@
package me.neznamy.tab.api.event.player;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.event.TabEvent;
/**
* Called when the player has been fully loaded by TAB.
*/
public interface PlayerLoadEvent extends TabEvent {
/**
* Gets the player that was loaded.
*
* @return the player that was loaded
*/
TabPlayer getPlayer();
/**
* Returns true if player was loaded because they joined,
* false if the player was loaded because tab reload command was executed
* @return true if player joined, false if plugin was reloaded
*/
boolean isJoin();
}
@@ -0,0 +1,11 @@
package me.neznamy.tab.api.event.plugin;
import me.neznamy.tab.api.event.TabEvent;
import me.neznamy.tab.api.placeholder.Placeholder;
public interface PlaceholderRegisterEvent extends TabEvent {
String getIdentifier();
void setPlaceholder(Placeholder placeholder);
}
@@ -0,0 +1,9 @@
package me.neznamy.tab.api.event.plugin;
import me.neznamy.tab.api.event.TabEvent;
/**
* Called when TAB is fully loaded.
*/
public interface TabLoadEvent extends TabEvent {
}
@@ -2,15 +2,15 @@ package me.neznamy.tab.api.placeholder;
public interface Placeholder {
public int getRefresh();
int getRefresh();
public String getIdentifier();
String getIdentifier();
public boolean isTriggerMode();
boolean isTriggerMode();
public void enableTriggerMode();
void enableTriggerMode();
public void enableTriggerMode(Runnable onActivation, Runnable onDisable);
void enableTriggerMode(Runnable onActivation, Runnable onDisable);
public void unload();
void unload();
}
@@ -13,24 +13,24 @@ public interface PlaceholderManager {
/**
* Registers a server placeholder (placeholder with same output for all players)
*/
public ServerPlaceholder registerServerPlaceholder(String identifier, int refresh, Supplier<Object> supplier);
ServerPlaceholder registerServerPlaceholder(String identifier, int refresh, Supplier<Object> supplier);
/**
* Registers a player placeholder (placeholder with player-specific output)
*/
public PlayerPlaceholder registerPlayerPlaceholder(String identifier, int refresh, Function<TabPlayer, Object> function);
PlayerPlaceholder registerPlayerPlaceholder(String identifier, int refresh, Function<TabPlayer, Object> function);
/**
* Registers a relational placeholder (different output for each player pair)
*/
public RelationalPlaceholder registerRelationalPlaceholder(String identifier, int refresh, BiFunction<TabPlayer, TabPlayer, Object> function);
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
*/
public List<String> detectPlaceholders(String text);
List<String> detectPlaceholders(String text);
/**
* Adds placeholder to list of used placeholders and assigns this feature as using it,
@@ -38,7 +38,9 @@ public interface PlaceholderManager {
* @param identifier - placeholder identifier
* @param feature - feature using the placeholder
*/
public void addUsedPlaceholder(String identifier, TabFeature feature);
void addUsedPlaceholder(String identifier, TabFeature feature);
public String findReplacement(String placeholder, String output);
String findReplacement(String placeholder, String output);
List<String> getUsedPlaceholders();
}
@@ -4,7 +4,7 @@ import me.neznamy.tab.api.TabPlayer;
public interface PlayerPlaceholder extends Placeholder {
public void updateValue(TabPlayer player, Object value);
void updateValue(TabPlayer player, Object value);
public Object request(TabPlayer p);
Object request(TabPlayer p);
}
@@ -4,7 +4,7 @@ import me.neznamy.tab.api.TabPlayer;
public interface RelationalPlaceholder extends Placeholder {
public void updateValue(TabPlayer viewer, TabPlayer target, Object value);
void updateValue(TabPlayer viewer, TabPlayer target, Object value);
public Object request(TabPlayer viewer, TabPlayer target);
Object request(TabPlayer viewer, TabPlayer target);
}
@@ -2,7 +2,7 @@ package me.neznamy.tab.api.placeholder;
public interface ServerPlaceholder extends Placeholder {
public void updateValue(Object value);
void updateValue(Object value);
public Object request();
Object request();
}
@@ -10,7 +10,7 @@ import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.api.util.BiFunctionWithException;
/**
* An interface represending a platform-specific packet builder
* An interface representing a platform-specific packet builder
*/
public abstract class PacketBuilder {
@@ -180,7 +180,7 @@ public abstract class PacketBuilder {
/**
* If {@code clientVersion} is >= 1.13, creates a component from given text and returns
* it as a serialized component, which bungeecord uses.
* it as a serialized component, which BungeeCord uses.
* <p>
* If {@code clientVersion} is < 1.12, the text is cut to {@code length} characters if
* needed and returned.
@@ -202,13 +202,13 @@ public abstract class PacketBuilder {
/**
* Converts platform-specific instance of player info packet into
* {@link me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo} object.
* {@link PacketPlayOutPlayerInfo} object.
*
* @param packet
* platform-specific info packet
* @param clientVersion
* Version of client receiving the packet
* @return The packet converted into {@link me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo}
* @return The packet converted into {@link PacketPlayOutPlayerInfo}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@@ -216,29 +216,25 @@ public abstract class PacketBuilder {
/**
* Converts platform-specific instance of objective packet into
* {@link me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective} object.
* {@link PacketPlayOutScoreboardObjective} object.
*
* @param packet
* platform-specific objective packet
* @param clientVersion
* Version of client receiving the packet
* @return The packet converted into {@link me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective}
* @return The packet converted into {@link PacketPlayOutScoreboardObjective}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public abstract PacketPlayOutScoreboardObjective readObjective(Object packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
public abstract PacketPlayOutScoreboardObjective readObjective(Object packet) throws ReflectiveOperationException;
/**
* Converts platform-specific instance of display objective packet into
* {@link me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective} object.
* {@link PacketPlayOutScoreboardDisplayObjective} object.
*
* @param packet
* platform-specific display objective packet
* @param clientVersion
* Version of client receiving the packet
* @return The packet converted into {@link me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective}
* @return The packet converted into {@link PacketPlayOutScoreboardDisplayObjective}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public abstract PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object packet, ProtocolVersion clientVersion) throws ReflectiveOperationException;
public abstract PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object packet) throws ReflectiveOperationException;
}
@@ -10,22 +10,22 @@ import me.neznamy.tab.api.bossbar.BarStyle;
*/
public class PacketPlayOutBoss implements TabPacket {
/** UUID of the bossbar */
private UUID id;
/** UUID of the BossBar */
private final UUID id;
/** Action of this packet */
private Action operation;
private final Action operation;
/** Bossbar title */
/** BossBar title */
private String name;
/** Bossbar progress (0-1)*/
/** BossBar progress (0-1)*/
private float pct;
/** Bossbar color */
/** BossBar color */
private BarColor color;
/** Bossbar style */
/** BossBar style */
private BarStyle overlay;
/** Darken screen flag */
@@ -42,15 +42,15 @@ public class PacketPlayOutBoss implements TabPacket {
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.ADD action
*
* @param id
* bossbar uuid
* BossBar uuid
* @param name
* bossbar title
* BossBar title
* @param pct
* bossbar progress
* BossBar progress
* @param color
* bossbar color
* BossBar color
* @param overlay
* bossbar style
* BossBar style
*/
public PacketPlayOutBoss(UUID id, String name, float pct, BarColor color, BarStyle overlay) {
this.operation = Action.ADD;
@@ -66,7 +66,7 @@ public class PacketPlayOutBoss implements TabPacket {
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.REMOVE action
*
* @param id
* bossbar uuid
* BossBar uuid
*/
public PacketPlayOutBoss(UUID id) {
this.operation = Action.REMOVE;
@@ -78,9 +78,9 @@ public class PacketPlayOutBoss implements TabPacket {
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PCT action
*
* @param id
* bossbar uuid
* BossBar uuid
* @param pct
* bossbar progress
* BossBar progress
*/
public PacketPlayOutBoss(UUID id, float pct) {
this.operation = Action.UPDATE_PCT;
@@ -93,9 +93,9 @@ public class PacketPlayOutBoss implements TabPacket {
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_NAME action
*
* @param id
* bossbar uuid
* BossBar uuid
* @param name
* bossbar title
* BossBar title
*/
public PacketPlayOutBoss(UUID id, String name) {
this.operation = Action.UPDATE_NAME;
@@ -108,11 +108,11 @@ public class PacketPlayOutBoss implements TabPacket {
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_STYLE action
*
* @param id
* bossbar uuid
* @param name
* bossbar color
* BossBar uuid
* @param color
* BossBar color
* @param overlay
* bossbar style
* BossBar style
*/
public PacketPlayOutBoss(UUID id, BarColor color, BarStyle overlay) {
this.operation = Action.UPDATE_STYLE;
@@ -171,7 +171,7 @@ public class PacketPlayOutBoss implements TabPacket {
}
/**
* Returns {@link #style}
* Returns {@link #overlay}
* @return style
*/
public BarStyle getOverlay() {
@@ -275,6 +275,6 @@ public class PacketPlayOutBoss implements TabPacket {
UPDATE_PCT,
UPDATE_NAME,
UPDATE_STYLE,
UPDATE_PROPERTIES;
UPDATE_PROPERTIES
}
}
@@ -55,6 +55,6 @@ public class PacketPlayOutChat implements TabPacket {
CHAT,
SYSTEM,
GAME_INFO;
GAME_INFO
}
}
@@ -12,7 +12,7 @@ import me.neznamy.tab.api.chat.IChatBaseComponent;
public class PacketPlayOutPlayerInfo implements TabPacket {
/** Packet action */
private EnumPlayerInfoAction action;
private final EnumPlayerInfoAction action;
/** List of affected entries */
private final List<PlayerInfoData> entries;
@@ -72,12 +72,12 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/** Latency */
private int latency;
/** Gamemode */
private EnumGamemode gameMode = EnumGamemode.SURVIVAL; //protocollib causes NPE even when action does not use gamemode
/** 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.
* 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;
@@ -103,7 +103,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* @param latency
* Player's ping
* @param gameMode
* Player's gamemode
* Player's GameMode
* @param displayName
* Player's display name
*/
@@ -123,7 +123,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* @param uniqueId
* Player's uuid
* @param gameMode
* Player's gamemode
* Player's GameMode
*/
public PlayerInfoData(UUID uniqueId, EnumGamemode gameMode) {
this.uniqueId = uniqueId;
@@ -254,7 +254,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Sets {@link #gameMode} to specified value
* @param gameMode
* Gamemode to use
* GameMode to use
*/
public void setGameMode(EnumGamemode gameMode) {
this.gameMode = gameMode;
@@ -288,11 +288,11 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
UPDATE_GAME_MODE,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
REMOVE_PLAYER;
REMOVE_PLAYER
}
/**
* An enum representing gamemode
* An enum representing GameMode
*/
public enum EnumGamemode {
@@ -300,6 +300,6 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
SURVIVAL,
CREATIVE,
ADVENTURE,
SPECTATOR;
SPECTATOR
}
}
@@ -7,10 +7,10 @@ import me.neznamy.tab.api.chat.IChatBaseComponent;
*/
public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
/** Tablist header */
/** TabList header */
private final IChatBaseComponent header;
/** Tablist footer */
/** TabList footer */
private final IChatBaseComponent footer;
/**
@@ -18,9 +18,9 @@ public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
* using {@link IChatBaseComponent#optimizedComponent(String)} method.
*
* @param header
* Tablist header
* TabList header
* @param footer
* Tablist footer
* TabList footer
*/
public PacketPlayOutPlayerListHeaderFooter(String header, String footer) {
this.header = IChatBaseComponent.optimizedComponent(header);
@@ -31,9 +31,9 @@ public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
* Constructs new instance with given parameters.
*
* @param header
* Tablist header
* TabList header
* @param footer
* Tablist footer
* TabList footer
*/
public PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent header, IChatBaseComponent footer) {
this.header = header;
@@ -7,9 +7,9 @@ public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
/**
* Display slot.
* 0 = playerlist,
* 1 = sidebar,
* 2 = belowname.
* 0 = PlayerList,
* 1 = SideBar,
* 2 = BelowName.
*/
private final int slot;
@@ -11,7 +11,7 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
/** Display name */
private final String displayName;
/** Display type, only takes effect in playertlist */
/** Display type, only takes effect in PlayerList */
private final EnumScoreboardHealthDisplay renderType;
/**
@@ -100,10 +100,10 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
/**
* An enum representing available display types.
* They only take effect in playerlist position.
* They only take effect in PlayerList position.
*/
public enum EnumScoreboardHealthDisplay {
INTEGER, HEARTS;
INTEGER, HEARTS
}
}
@@ -84,6 +84,6 @@ public class PacketPlayOutScoreboardScore implements TabPacket {
public enum Action {
CHANGE,
REMOVE;
REMOVE
}
}
@@ -12,12 +12,12 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Up to 16 characters long team name.
* It is used to sort players alphabetically in tablist by team names.
* Teamless players appear on top, sorted by usernames.
* 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 String name;
private final String name;
/** Team prefix, limited to 16 characters, on <1.13 */
private String playerPrefix;
@@ -26,10 +26,10 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
private String playerSuffix;
/**
* Nametag visibility rule since 1.8. Possible options are:
* NameTag visibility rule since 1.8. Possible options are:
* {@code always}, {@code hideForOtherTeams}, {@code hideForOwnTeam}, {@code never}
*/
private String nametagVisibility;
private String nameTagVisibility;
/**
* Collision rule added in 1.9. Possible options are:
@@ -44,7 +44,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
private EnumChatFormat color;
/**
* Affected entities. For players it is their name, for other entities their UUID
* Affected entities. For players, it is their name, for other entities their UUID
* Player name length is limited to 16 characters for <1.8, 40 characters since 1.8.
* Entity UUID option was added in 1.8.
*/
@@ -58,7 +58,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
* 3 = add entries,
* 4 = remove entries
*/
private int method;
private final int method;
/**
* Bit mask.
@@ -96,7 +96,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
* @param suffix
* Suffix of players in team
* @param visibility
* Nametag visibility rule
* NameTag visibility rule
* @param collision
* Collision rule
* @param players
@@ -110,7 +110,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
this(0, team);
this.playerPrefix = prefix;
this.playerSuffix = suffix;
this.nametagVisibility = visibility;
this.nameTagVisibility = visibility;
this.collisionRule = collision;
this.players = players;
this.options = options;
@@ -138,7 +138,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
* @param suffix
* Suffix of players in team
* @param visibility
* Nametag visibility rule
* NameTag visibility rule
* @param collision
* Collision rule
* @param options
@@ -150,7 +150,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
this(2, team);
this.playerPrefix = prefix;
this.playerSuffix = suffix;
this.nametagVisibility = visibility;
this.nameTagVisibility = visibility;
this.collisionRule = collision;
this.options = options;
}
@@ -175,9 +175,9 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
@Override
public String toString() {
return String.format("PacketPlayOutScoreboardTeam{name=%s,playerPrefix=%s,playerSuffix=%s,nametagVisibility=%s,"
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, method, options);
name, playerPrefix, playerSuffix, nameTagVisibility, collisionRule, color, players, method, options);
}
/**
@@ -205,11 +205,11 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
}
/**
* Returns {@link #nametagVisibility}
* @return nametagVisibility
* Returns {@link #nameTagVisibility}
* @return nameTagVisibility
*/
public String getNametagVisibility() {
return nametagVisibility;
public String getNameTagVisibility() {
return nameTagVisibility;
}
/**
@@ -12,5 +12,5 @@ public interface TabPacket {
* Returns user-friendly string representation of the packet
* @return user-friendly string representation of the packet
*/
public String toString();
String toString();
}
@@ -9,11 +9,11 @@ public interface Line {
/**
* Returns configured raw text of this line. Placeholders
* remain unparsed.
* remain in raw format.
*
* @return raw text of this line
*/
public String getText();
String getText();
/**
* Changes text to new value. Supports placeholders, which are
@@ -25,5 +25,5 @@ public interface Line {
* @param text
* Raw text to display in the line
*/
public void setText(String text);
void setText(String text);
}
@@ -3,7 +3,7 @@ package me.neznamy.tab.api.scoreboard;
import java.util.List;
/**
* An interface allowing to work with scoreboard, such as adding plyaers,
* An interface allowing to work with scoreboard, such as adding players,
* removing players and changing lines. New instance can be created using
* {@link ScoreboardManager#createScoreboard(String, String, List)},
* for scoreboards from config {@link ScoreboardManager#getRegisteredScoreboards()}.
@@ -17,16 +17,16 @@ public interface Scoreboard {
*
* @return custom name of this scoreboard
*/
public String getName();
String getName();
/**
* Returns raw title of this scoreboard. Placeholders stay in their
* raw, unparsed format.
* raw format.
*
* @return scoreboard title
* @see #setTitle(String)
*/
public String getTitle();
String getTitle();
/**
* Sets title to specified value. Placeholders are refreshed automatically
@@ -43,7 +43,7 @@ public interface Scoreboard {
* New title to use with placeholder support
* @see #getTitle()
*/
public void setTitle(String title);
void setTitle(String title);
/**
* Returns list of lines in this scoreboard in the order they appear
@@ -54,7 +54,7 @@ public interface Scoreboard {
* @see #addLine(String)
* @see #removeLine(int)
*/
public List<Line> getLines();
List<Line> getLines();
/**
* Adds line with specified text on the bottom of scoreboard. Supports
@@ -65,7 +65,7 @@ public interface Scoreboard {
* @see #getLines()
* @see #removeLine(int)
*/
public void addLine(String text);
void addLine(String text);
/**
* Removes line with specified index. Index starts at {@code 0} and ends at
@@ -78,12 +78,10 @@ public interface Scoreboard {
* @see #getLines()
* @see #addLine(String)
*/
public void removeLine(int index);
void removeLine(int index);
/**
* Unregisters this scoreboard from all players who can see it.
* Calling this method is equal to iterating over {@link #getPlayers()}
* and calling {@link #removePlayer(TabPlayer)} on each player.
*/
public void unregister();
void unregister();
}
@@ -23,7 +23,7 @@ public interface ScoreboardManager {
* Scoreboard {@code title} is limited to 32 characters on <1.13. If needed,
* it will be cut to 32 character for those players (after replacing
* placeholders). RGB is supported using any of the supported formats.
* Placholders are supported.
* Placeholders are supported.
* <p>
* {@code lines} will display in the same order as contained in the list.
* Client can only see up to 15 lines at a time, however you can define
@@ -39,7 +39,7 @@ public interface ScoreboardManager {
* @throws NullPointerException
* if {@code name} or {@code lines} is {@code null}
*/
public Scoreboard createScoreboard(String name, String title, List<String> lines);
Scoreboard createScoreboard(String name, String title, List<String> lines);
/**
* Returns map of registered scoreboards via config and API.
@@ -49,7 +49,7 @@ public interface ScoreboardManager {
*
* @return map of registered scoreboards
*/
public Map<String, Scoreboard> getRegisteredScoreboards();
Map<String, Scoreboard> getRegisteredScoreboards();
/**
* Displays scoreboard to defined player. This will disable all display
@@ -66,7 +66,7 @@ public interface ScoreboardManager {
* @see #hasCustomScoreboard(TabPlayer)
* @see #resetScoreboard(TabPlayer)
*/
public void showScoreboard(TabPlayer player, Scoreboard scoreboard);
void showScoreboard(TabPlayer player, Scoreboard scoreboard);
/**
* Returns {@code true} if player has custom scoreboard set using
@@ -79,7 +79,7 @@ public interface ScoreboardManager {
* @see #showScoreboard(TabPlayer, Scoreboard)
* @see #resetScoreboard(TabPlayer)
*/
public boolean hasCustomScoreboard(TabPlayer player);
boolean hasCustomScoreboard(TabPlayer player);
/**
* Hides custom scoreboard sent using {@link #showScoreboard(TabPlayer, Scoreboard)}
@@ -91,7 +91,7 @@ public interface ScoreboardManager {
* @see #showScoreboard(TabPlayer, Scoreboard)
* @see #hasCustomScoreboard(TabPlayer)
*/
public void resetScoreboard(TabPlayer player);
void resetScoreboard(TabPlayer player);
/**
* Returns {@code true} if player has scoreboard enabled, {@code false}
@@ -105,7 +105,7 @@ public interface ScoreboardManager {
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
* @see #toggleScoreboard(TabPlayer, boolean)
*/
public boolean hasScoreboardVisible(TabPlayer player);
boolean hasScoreboardVisible(TabPlayer player);
/**
* Sets scoreboard visibility of player to defined value. If visibility status
@@ -122,7 +122,7 @@ public interface ScoreboardManager {
* @see #toggleScoreboard(TabPlayer, boolean)
* @see #hasScoreboardVisible(TabPlayer)
*/
public void setScoreboardVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
void setScoreboardVisible(TabPlayer player, boolean visible, boolean sendToggleMessage);
/**
* Toggles scoreboard for specified player. If player had scoreboard visible,
@@ -136,7 +136,7 @@ public interface ScoreboardManager {
* @see #hasScoreboardVisible(TabPlayer)
* @see #setScoreboardVisible(TabPlayer, boolean, boolean)
*/
public void toggleScoreboard(TabPlayer player, boolean sendToggleMessage);
void toggleScoreboard(TabPlayer player, boolean sendToggleMessage);
/**
* Temporarily displays scoreboard to all players for specified amount of
@@ -151,5 +151,15 @@ public interface ScoreboardManager {
* if no scoreboard was found with such name or {@code duration}
* is < 0.
*/
public void announceScoreboard(String scoreboard, int duration);
void announceScoreboard(String scoreboard, int duration);
/**
* Returns player's currently displayed scoreboard. This can be either with
* configuration, overridden with commands or the API. Will return {@code null}
* if player does not see any scoreboard due to not meeting any display condition.
* @param player
* player to get active scoreboard of
* @return player's active scoreboard or {@code null} if player has no scoreboard
*/
Scoreboard getActiveScoreboard(TabPlayer player);
}
@@ -13,14 +13,23 @@ public interface ThreadManager {
* @param type - usage type to add cpu usage to
* @param task - the task
*/
public Future<Void> runMeasuredTask(String errorDescription, TabFeature feature, String type, Runnable task);
Future<Void> runMeasuredTask(String errorDescription, TabFeature feature, String type, Runnable task);
/**
* Starts a task in new thread and measures how long it took to process
* @param errorDescription - description to use if this task throws an error
* @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 errorDescription, String feature, String type, Runnable task);
/**
* Runs task in a new thread
* @param errorDescription - description to use if this task throws an error
* @param task - the task
*/
public Future<Void> runTask(String errorDescription, Runnable task);
Future<Void> runTask(String errorDescription, Runnable task);
/**
* Starts a new task with defined repeat interval that measures cpu usage
@@ -30,7 +39,7 @@ public interface ThreadManager {
* @param type - usage type to add cpu usage to
* @param task - the task
*/
public RepeatingTask startRepeatingMeasuredTask(int intervalMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task);
RepeatingTask startRepeatingMeasuredTask(int intervalMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task);
/**
* Runs task with a delay and measures how long it took to process
@@ -39,18 +48,9 @@ public interface ThreadManager {
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
* @return future allowing to cancel the task
*/
public Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, TabFeature feature, String type, 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 errorDescription - description to use if this task throws an error
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
*/
public Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, String feature, String type, Runnable task);
public Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, Runnable task);
Future<?> runTaskLater(int delayMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task);
Future<?> runTaskLater(int delayMilliseconds, String errorDescription, Runnable task);
}
@@ -5,109 +5,109 @@ import me.neznamy.tab.api.TabPlayer;
public interface TeamManager {
/**
* Makes player's nametag globally invisible
* @see showNametag
* @see hasHiddenNametag
* Makes player's NameTag globally invisible
* @see #showNametag(TabPlayer)
* @see #hasHiddenNametag(TabPlayer)
*/
public void hideNametag(TabPlayer player);
void hideNametag(TabPlayer player);
/**
* Hides player's nametag for specified player until it's shown again
* @param viewer - player to hide nametag for
* Hides player's NameTag for specified player until it's shown again
* @param viewer - player to hide NameTag for
*/
public void hideNametag(TabPlayer player, TabPlayer viewer);
void hideNametag(TabPlayer player, TabPlayer viewer);
/**
* Makes player's nametag visible again
* @see hideNametag
* @see hasHiddenNametag
* Makes player's NameTag visible again
* @see #hideNametag(TabPlayer)
* @see #hasHiddenNametag(TabPlayer)
*/
public void showNametag(TabPlayer player);
void showNametag(TabPlayer player);
/**
* Shows player's nametag for specified viewer if it was hidden before
* @param viewer - player to show nametag back for
* Shows player's NameTag for specified viewer if it was hidden before
* @param viewer - player to show NameTag back for
*/
public void showNametag(TabPlayer player, TabPlayer viewer);
void showNametag(TabPlayer player, TabPlayer viewer);
/**
* Return whether player has hidden nametag or not
* @return Whether player has hidden nametag or not
* @see hideNametag
* @see showNametag
* Return whether player has hidden NameTag or not
* @return Whether player has hidden NameTag or not
* @see #hideNametag(TabPlayer)
* @see #showNametag(TabPlayer)
*/
public boolean hasHiddenNametag(TabPlayer player);
boolean hasHiddenNametag(TabPlayer player);
/**
* Returns true if nametag is hidden for specified viewer, false if not
* Returns true if NameTag is hidden for specified viewer, false if not
* @param viewer - player to check visibility status for
* @return true if hidden, false if not
*/
public boolean hasHiddenNametag(TabPlayer player, TabPlayer viewer);
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.
*/
public void pauseTeamHandling(TabPlayer player);
void pauseTeamHandling(TabPlayer player);
/**
* Resumes team handling if it was before paused using pauseTeamHandling(), if not, nothing happens
*/
public void resumeTeamHandling(TabPlayer player);
void resumeTeamHandling(TabPlayer player);
/**
* Returns true if team handling is paused for this player using pauseTeamHandling(), false if not or
* Returns true if team handling is paused for this player using pauseTeamHandling(), false if not, or
* it was resumed already using resumeTeamHandling
* @return true if paused, false if not
*/
public boolean hasTeamHandlingPaused(TabPlayer player);
boolean hasTeamHandlingPaused(TabPlayer player);
/**
* Forces new team name for the player until this method is called again with null argument and
* performs all actions to change player's team name
* @param teamName - forced team name
*/
public void forceTeamName(TabPlayer player, String teamName);
void forceTeamName(TabPlayer player, String teamName);
/**
* Returns forced team name of player or null if not forced
* @return forced team name of player or null if not forced
*/
public String getForcedTeamName(TabPlayer player);
String getForcedTeamName(TabPlayer player);
/**
* Forces collision rule for the player. Setting it to null will remove forced value
* @param collision - forced collision rule
*/
public void setCollisionRule(TabPlayer player, Boolean collision);
void setCollisionRule(TabPlayer player, Boolean collision);
/**
* Returns forced collision rule or null if collision is not forced using setCollisionRule
* @return forced value or null if not forced
*/
public Boolean getCollisionRule(TabPlayer player);
Boolean getCollisionRule(TabPlayer player);
/**
* Sends update team properties packet of player's team to everyone
* Sends update team's properties packet of player's team to everyone
* @param player - player to update team data of
*/
public void updateTeamData(TabPlayer player);
void updateTeamData(TabPlayer player);
public void setPrefix(TabPlayer player, String prefix);
void setPrefix(TabPlayer player, String prefix);
public void setSuffix(TabPlayer player, String suffix);
void setSuffix(TabPlayer player, String suffix);
public void resetPrefix(TabPlayer player);
void resetPrefix(TabPlayer player);
public void resetSuffix(TabPlayer player);
void resetSuffix(TabPlayer player);
public String getCustomPrefix(TabPlayer player);
String getCustomPrefix(TabPlayer player);
public String getCustomSuffix(TabPlayer player);
String getCustomSuffix(TabPlayer player);
public String getOriginalPrefix(TabPlayer player);
String getOriginalPrefix(TabPlayer player);
public String getOriginalSuffix(TabPlayer player);
String getOriginalSuffix(TabPlayer player);
}
@@ -6,27 +6,27 @@ import me.neznamy.tab.api.TabPlayer;
public interface UnlimitedNametagManager extends TeamManager {
public void disableArmorStands(TabPlayer player);
void disableArmorStands(TabPlayer player);
public void enableArmorStands(TabPlayer player);
void enableArmorStands(TabPlayer player);
public boolean hasDisabledArmorStands(TabPlayer player);
boolean hasDisabledArmorStands(TabPlayer player);
public void setName(TabPlayer player, String customname);
void setName(TabPlayer player, String customName);
public void setLine(TabPlayer player, String line, String value);
void setLine(TabPlayer player, String line, String value);
public void resetName(TabPlayer player);
void resetName(TabPlayer player);
public void resetLine(TabPlayer player, String line);
void resetLine(TabPlayer player, String line);
public String getCustomName(TabPlayer player);
String getCustomName(TabPlayer player);
public String getCustomLineValue(TabPlayer player, String line);
String getCustomLineValue(TabPlayer player, String line);
public String getOriginalName(TabPlayer player);
String getOriginalName(TabPlayer player);
public String getOriginalLineValue(TabPlayer player, String line);
String getOriginalLineValue(TabPlayer player, String line);
public List<String> getDefinedLines();
List<String> getDefinedLines();
}
@@ -0,0 +1,13 @@
package me.neznamy.tab.api.util;
public class Preconditions {
public static void checkNotNull(Object obj, String exceptionMessage){
if (obj == null) throw new IllegalArgumentException(exceptionMessage);
}
public static void checkRange(Number number, Number min, Number max, String variable){
if (number.doubleValue() < min.doubleValue() || number.doubleValue() > max.doubleValue())
throw new IllegalArgumentException("Number index out of range (" + min + " - " + max + ") for " + variable);
}
}
@@ -1,5 +1,5 @@
plugins {
id("tab.base-conventions")
id("tab.standard-conventions")
id("com.github.johnrengelman.shadow")
}
@@ -8,5 +8,6 @@ tasks {
archiveFileName.set("TAB-${project.name}-${project.version}.jar")
relocate("org.bstats", "me.neznamy.tab.libs.org.bstats")
relocate("org.json.simple", "me.neznamy.tab.libs.org.json.simple")
relocate("net.kyori.event", "me.neznamy.tab.libs.net.kyori.event")
}
}
@@ -1,18 +0,0 @@
plugins {
id("tab.base-conventions")
`maven-publish`
}
java {
withSourcesJar()
withJavadocJar()
}
publishing {
publications.create<MavenPublication>("mavenJava") {
groupId = rootProject.group as String
artifactId = project.name
version = rootProject.version as String
from(components["java"])
}
}
@@ -0,0 +1,31 @@
import gradle.kotlin.dsl.accessors._4cbb0020bd75d48acc42cabded5fdbb7.publishing
import org.gradle.api.artifacts.repositories.PasswordCredentials
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.repositories
plugins {
id("tab.base-conventions")
`maven-publish`
}
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"])
}
}
+6
View File
@@ -15,9 +15,15 @@ val platforms = setOf(
projects.velocity
).map { it.dependencyProject }
val special = setOf(
projects.api,
projects.shared
).map { it.dependencyProject }
subprojects {
when (this) {
in platforms -> plugins.apply("tab.platform-conventions")
in special -> plugins.apply("tab.standard-conventions")
else -> plugins.apply("tab.base-conventions")
}
}
@@ -36,7 +36,6 @@ import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerListHeaderFooter;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardScore;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
@@ -95,7 +94,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
@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
//1.9+ server or client, handled by bukkit api or ViaVersion
return packet;
}
//<1.9 client and server
@@ -209,9 +208,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
nms.ScoreboardTeam_setAllowFriendlyFire.invoke(team, (packet.getOptions() & 0x1) > 0);
nms.ScoreboardTeam_setCanSeeFriendlyInvisibles.invoke(team, (packet.getOptions() & 0x2) > 0);
if (nms.getMinorVersion() >= 13) {
team = createTeamModern(packet, clientVersion, team, prefix, suffix);
createTeamModern(packet, clientVersion, team, prefix, suffix);
} else {
team = createTeamLegacy(packet, team, prefix, suffix);
createTeamLegacy(packet, team, prefix, suffix);
}
if (nms.getMinorVersion() >= 17) {
switch (packet.getMethod()) {
@@ -234,13 +233,13 @@ public class BukkitPacketBuilder extends PacketBuilder {
/**
* Builds entity destroy packet with given parameter
* @param id - entity id to destroy
* @return destroy packet
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityDestroy packet) throws ReflectiveOperationException {
try {
return nms.newPacketPlayOutEntityDestroy.newInstance(packet.getEntities());
return nms.newPacketPlayOutEntityDestroy.newInstance(new Object[]{packet.getEntities()});
} catch (IllegalArgumentException e) {
//1.17.0
return nms.newPacketPlayOutEntityDestroy.newInstance(packet.getEntities()[0]);
@@ -253,13 +252,10 @@ public class BukkitPacketBuilder extends PacketBuilder {
/**
* Builds entity spawn packet with given parameters
* @param entityId - entity id
* @param uuid - entity uuid
* @param entityType - entity type
* @param loc - location to spawn at
* @param dataWatcher - datawatcher
*
* @return entity spawn packet
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutSpawnEntityLiving packet) throws ReflectiveOperationException {
Object nmsPacket;
@@ -290,10 +286,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
/**
* Builds entity teleport packet with given parameters
* @param entityId - entity id
* @param location - location to teleport to
* @return entity teleport packet
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityTeleport packet) throws ReflectiveOperationException {
Object nmsPacket;
@@ -317,22 +312,20 @@ public class BukkitPacketBuilder extends PacketBuilder {
return nmsPacket;
}
private Object createTeamModern(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion, Object team, String prefix, String suffix) throws ReflectiveOperationException {
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, nms.EnumChatFormat_values[format.ordinal()]);
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNametagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNameTagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
return team;
}
private Object createTeamLegacy(PacketPlayOutScoreboardTeam packet, Object team, String prefix, String suffix) throws ReflectiveOperationException {
private void createTeamLegacy(PacketPlayOutScoreboardTeam packet, Object team, String prefix, String suffix) throws ReflectiveOperationException {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, prefix);
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, suffix);
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNametagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNameTagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
if (nms.getMinorVersion() >= 9) nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
return team;
}
@Override
@@ -341,42 +334,27 @@ public class BukkitPacketBuilder extends PacketBuilder {
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());
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 : fromNMSComponent(nmsComponent);
PropertyMap map = new PropertyMap();
map.putAll(profile.getProperties());
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), map, (int) nms.PlayerInfoData_getLatency.invoke(nmsData), gamemode, listName));
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), map, (int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName));
}
return new PacketPlayOutPlayerInfo(action, listData);
}
@Override
public PacketPlayOutScoreboardObjective readObjective(Object nmsPacket, ProtocolVersion clientVersion) throws ReflectiveOperationException {
String objective = (String) nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME.get(nmsPacket);
String displayName;
Object component = nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.get(nmsPacket);
if (nms.getMinorVersion() >= 13) {
IChatBaseComponent c = component == null ? null : fromNMSComponent(component);
displayName = c == null ? null : c.toLegacyText();
} else {
displayName = (String) component;
}
EnumScoreboardHealthDisplay renderType = null;
if (nms.getMinorVersion() >= 8) {
Object nmsRender = nms.PacketPlayOutScoreboardObjective_RENDERTYPE.get(nmsPacket);
if (nmsRender != null) {
renderType = EnumScoreboardHealthDisplay.valueOf(nmsRender.toString());
}
}
int method = nms.PacketPlayOutScoreboardObjective_METHOD.getInt(nmsPacket);
return new PacketPlayOutScoreboardObjective(method, objective, displayName, renderType);
public PacketPlayOutScoreboardObjective readObjective(Object nmsPacket) throws ReflectiveOperationException {
return new PacketPlayOutScoreboardObjective(nms.PacketPlayOutScoreboardObjective_METHOD.getInt(nmsPacket),
(String) nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME.get(nmsPacket), null, null
);
}
@Override
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object nmsPacket, ProtocolVersion clientVersion) throws ReflectiveOperationException {
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object nmsPacket) throws ReflectiveOperationException {
return new PacketPlayOutScoreboardDisplayObjective(
nms.PacketPlayOutScoreboardDisplayObjective_POSITION.getInt(nmsPacket),
(String) nms.PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME.get(nmsPacket)
@@ -384,11 +362,12 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Builds entity bossbar packet
* Builds entity BossBar packet
* @param packet - packet to build
* @param clientVersion - client version
* @return entity bossbar packet
* @throws ReflectiveOperationException
* @return entity BossBar packet
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private Object buildBossPacketEntity(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (packet.getOperation() == Action.UPDATE_STYLE) return null; //nothing to do here
@@ -415,7 +394,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* A method yoinked from minecraft code used to convert double to int
* A method taken from minecraft code used to convert double to int
* @param paramDouble double value
* @return int value
*/
@@ -425,10 +404,11 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Converts minecraft IChatBaseComponent into TAB's component class. Currently does not support show_item hover event on 1.16+.
* Converts minecraft IChatBaseComponent into TAB's component class. Currently, does not support show_item hover event on 1.16+.
* @param component - component to convert
* @return converted component
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
private IChatBaseComponent fromNMSComponent(Object component) throws ReflectiveOperationException {
if (!nms.ChatComponentText.isInstance(component)) return null; //paper
@@ -484,11 +464,12 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Converts TAB's IChatBaseComponent into minecraft's component. Currently does not support hover event.
* Converts TAB's IChatBaseComponent into minecraft's component. Currently, does not support hover event.
* @param component - component to convert
* @param clientVersion - client version used to decide RGB conversion
* @return converted component
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object toNMSComponent(IChatBaseComponent component, ProtocolVersion clientVersion) throws ReflectiveOperationException {
Object obj;
@@ -13,6 +13,7 @@ import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.NickCompatibility;
import me.neznamy.tab.shared.features.PipelineInjector;
import org.jetbrains.annotations.NotNull;
public class BukkitPipelineInjector extends PipelineInjector {
@@ -33,7 +34,7 @@ public class BukkitPipelineInjector extends PipelineInjector {
public class BukkitChannelDuplexHandler extends ChannelDuplexHandler {
//injected player
private TabPlayer player;
private final TabPlayer player;
/**
* Constructs new instance with given player
@@ -44,7 +45,7 @@ public class BukkitPipelineInjector extends PipelineInjector {
}
@Override
public void channelRead(ChannelHandlerContext context, Object packet) {
public void channelRead(@NotNull ChannelHandlerContext context, @NotNull Object packet) {
try {
if (TAB.getInstance().getFeatureManager().onPacketReceive(player, packet)) return;
super.channelRead(context, packet);
@@ -65,8 +66,8 @@ public class BukkitPipelineInjector extends PipelineInjector {
super.write(context, packet, channelPromise);
return;
}
if (nms.PacketPlayOutScoreboardDisplayObjective.isInstance(packet) && TAB.getInstance().getFeatureManager().onDisplayObjective(player, packet)) {
return;
if (nms.PacketPlayOutScoreboardDisplayObjective.isInstance(packet)){
TAB.getInstance().getFeatureManager().onDisplayObjective(player, packet);
}
if (nms.PacketPlayOutScoreboardObjective.isInstance(packet)) {
TAB.getInstance().getFeatureManager().onObjective(player, packet);
@@ -85,7 +86,8 @@ public class BukkitPipelineInjector extends PipelineInjector {
/**
* Removes all real players from team if packet does not come from TAB and reports this to override log
* @param packetPlayOutScoreboardTeam - team packet
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* nmsGameMode
*/
@SuppressWarnings("unchecked")
private void modifyPlayers(Object packetPlayOutScoreboardTeam) throws ReflectiveOperationException {
@@ -109,7 +111,7 @@ public class BukkitPipelineInjector extends PipelineInjector {
}
}
nms.setField(packetPlayOutScoreboardTeam, nms.PacketPlayOutScoreboardTeam_PLAYERS, newList);
TAB.getInstance().getCPUManager().addTime("Nametags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
private TabPlayer getPlayer(String name) {
@@ -119,9 +121,8 @@ public class BukkitPipelineInjector extends PipelineInjector {
return null;
}
private String getName(TabPlayer p) {
return ((NickCompatibility) TAB.getInstance().getFeatureManager().getFeature("nick")).getNickname(p);
return ((NickCompatibility) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.NICK_COMPATIBILITY)).getNickname(p);
}
}
}
@@ -1,6 +1,8 @@
package me.neznamy.tab.platforms.bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;
@@ -45,6 +47,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
private boolean paperTps;
private boolean paperMspt;
private boolean purpur;
private Method playerIsAfk;
private Listener healthListener = null;
@@ -82,7 +85,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
//not paper
}
try {
Player.class.getMethod("isAfk");
playerIsAfk = Player.class.getMethod("isAfk");
purpur = true;
} catch (NoSuchMethodException e) {
//not purpur
@@ -92,11 +95,10 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
@SuppressWarnings("deprecation")
@Override
public void registerPlaceholders(PlaceholderManager manager) {
NumberFormat roundDown = NumberFormat.getInstance();
NumberFormat roundDown = NumberFormat.getNumberInstance(Locale.ENGLISH);
roundDown.setRoundingMode(RoundingMode.DOWN);
roundDown.setMaximumFractionDigits(2);
manager.registerPlayerPlaceholder("%displayname%", 500, p -> ((Player) p.getPlayer()).getDisplayName());
manager.registerPlayerPlaceholder("%vanished%", 1000, TabPlayer::isVanished);
if (paperTps) {
manager.registerServerPlaceholder("%tps%", 1000, () -> formatTPS(Bukkit.getTPS()[0]));
} else if (recentTps != null) {
@@ -115,7 +117,19 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
}
manager.registerPlayerPlaceholder("%afk%", 500, p -> {
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
return purpur && ((Player)p.getPlayer()).isAfk();
if (!purpur || playerIsAfk == null) return false;
try {
return playerIsAfk.invoke(p.getPlayer());
} catch (final IllegalAccessException | InvocationTargetException exception) {
TAB.getInstance().getErrorManager().printError("Failed to invoke isAfk!", exception);
}
return false;
});
manager.registerPlayerPlaceholder("%essentialsnick%", 1000, p -> {
String nickname = null;
if (essentials != null)
nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
return nickname == null || nickname.length() == 0 ? p.getName() : nickname;
});
if (chat != null) {
manager.registerPlayerPlaceholder("%vault-prefix%", 500, p -> ((Chat) chat).getPlayerPrefix((Player) p.getPlayer()));
@@ -133,13 +147,13 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
}
private void registerOnlinePlaceholders(PlaceholderManager manager) {
manager.registerPlayerPlaceholder("%online%", -1, p -> {
manager.registerPlayerPlaceholder("%online%", 1000, p -> {
int count = 0;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()){
if (((Player) p.getPlayer()).canSee((Player) all.getPlayer())) count++;
}
return count;
}).enableTriggerMode();
});
manager.registerPlayerPlaceholder("%staffonline%", 2000, p -> {
int count = 0;
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()){
@@ -185,13 +199,11 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onRespawn(PlayerRespawnEvent e) {
health.updateValue(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getPlayer().getMaxHealth());
health.updateValue(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), (int) e.getPlayer().getMaxHealth());
}
};
Bukkit.getPluginManager().registerEvents(healthListener, plugin);
}, () -> {
HandlerList.unregisterAll(healthListener);
});
}, () -> HandlerList.unregisterAll(healthListener));
}
}
}
}
@@ -4,6 +4,7 @@ import java.io.File;
import java.util.Collection;
import java.util.List;
import me.neznamy.tab.shared.TabConstants;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@@ -18,7 +19,7 @@ import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.protocol.PacketBuilder;
import me.neznamy.tab.platforms.bukkit.event.TabLoadEvent;
import me.neznamy.tab.platforms.bukkit.event.TabPlayerLoadEvent;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerlist;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.platforms.bukkit.features.PetFix;
import me.neznamy.tab.platforms.bukkit.features.TabExpansion;
import me.neznamy.tab.platforms.bukkit.features.WitherBossBar;
@@ -36,9 +37,10 @@ import me.neznamy.tab.shared.permission.UltraPermissions;
import me.neznamy.tab.shared.placeholders.PlayerPlaceholderImpl;
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.plugin.RegisteredServiceProvider;
/**
* Bukkit implementation of Platform
* Bukkit's implementation of Platform interface
*/
public class BukkitPlatform implements Platform {
@@ -49,9 +51,7 @@ public class BukkitPlatform implements Platform {
//booleans to check plugin presence
private boolean placeholderAPI;
private boolean viaversion;
private boolean idisguise;
private boolean libsdisguises;
private boolean libsDisguises;
private Plugin essentials;
/**
@@ -69,7 +69,9 @@ public class BukkitPlatform implements Platform {
} else if (Bukkit.getPluginManager().isPluginEnabled("UltraPermissions")) {
return new UltraPermissions(getPluginVersion("UltraPermissions"));
} else if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
return new Vault(Bukkit.getServicesManager().getRegistration(Permission.class).getProvider(), getPluginVersion("Vault"));
RegisteredServiceProvider<Permission> provider = Bukkit.getServicesManager().getRegistration(Permission.class);
if (provider == null) return new None();
return new Vault(provider.getProvider(), getPluginVersion("Vault"));
} else {
return new None();
}
@@ -80,7 +82,6 @@ public class BukkitPlatform implements Platform {
if (Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
try {
Class.forName("com.viaversion.viaversion.api.Via");
viaversion = true;
} catch (ClassNotFoundException e) {
TAB.getInstance().sendConsoleMessage("&c[TAB] An outdated version of ViaVersion (" + getPluginVersion("ViaVersion") + ") was detected.", true);
TAB.getInstance().sendConsoleMessage("&c[TAB] TAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
@@ -91,24 +92,26 @@ public class BukkitPlatform implements Platform {
TAB.getInstance().sendConsoleMessage("&c[TAB] Detected plugin \"Tablisknu\", which causes TAB to not work properly. Consider removing the plugin.", true);
}
placeholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
idisguise = Bukkit.getPluginManager().isPluginEnabled("iDisguise");
libsdisguises = Bukkit.getPluginManager().isPluginEnabled("LibsDisguises");
libsDisguises = Bukkit.getPluginManager().isPluginEnabled("LibsDisguises");
essentials = Bukkit.getPluginManager().getPlugin("Essentials");
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection()) tab.getFeatureManager().registerFeature("injection", new BukkitPipelineInjector());
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
new UniversalPlaceholderRegistry().registerPlaceholders(tab.getPlaceholderManager());
loadNametagFeature(tab);
loadNameTagFeature(tab);
tab.loadUniversalFeatures();
if (tab.getConfiguration().getConfig().getBoolean("bossbar.enabled", false)) {
if (tab.getServerVersion().getMinorVersion() < 9) {
tab.getFeatureManager().registerFeature("bossbar", new WitherBossBar(plugin));
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new WitherBossBar(plugin));
} else {
tab.getFeatureManager().registerFeature("bossbar", new BossBarManagerImpl());
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("petfix", new PetFix());
if (tab.getConfiguration().getConfig().getBoolean("per-world-playerlist.enabled", false)) tab.getFeatureManager().registerFeature("pwp", new PerWorldPlayerlist(plugin));
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) {
new TabExpansion(plugin);
}
@@ -118,19 +121,20 @@ public class BukkitPlatform implements Platform {
}
private String getPluginVersion(String plugin) {
return Bukkit.getPluginManager().getPlugin(plugin).getDescription().getVersion();
Plugin pl = Bukkit.getPluginManager().getPlugin(plugin);
return pl == null ? "" : pl.getDescription().getVersion();
}
/**
* Loads nametag feature from config
* Loads NameTag feature from config
* @param tab - tab instance
*/
private void loadNametagFeature(TAB tab) {
private void loadNameTagFeature(TAB tab) {
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && tab.getServerVersion().getMinorVersion() >= 8) {
tab.getFeatureManager().registerFeature("nametagx", new NameTagX(plugin));
tab.getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, new NameTagX(plugin));
} else {
tab.getFeatureManager().registerFeature("nametag16", new NameTag());
tab.getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS, new NameTag());
}
}
}
@@ -182,11 +186,12 @@ public class BukkitPlatform implements Platform {
long time = System.nanoTime();
String syncedPlaceholder = identifier.substring(6, identifier.length()-1);
String value = placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), "%" + syncedPlaceholder + "%") : identifier;
getLastValues().put(p.getName(), value);
if (!getForceUpdate().contains(p.getName())) getForceUpdate().add(p.getName());
getLastValues().put(p, value);
getForceUpdate().add(p);
TAB.getInstance().getCPUManager().addPlaceholderTime(getIdentifier(), System.nanoTime()-time);
});
return getLastValues().get(p.getName());
String value = getLastValues().get(p);
return value == null ? identifier : value;
}
});
return;
@@ -227,20 +232,12 @@ public class BukkitPlatform implements Platform {
return Bukkit.getMaxPlayers();
}
public boolean isViaversionEnabled() {
return viaversion;
}
public boolean isLibsdisguisesEnabled() {
return libsdisguises;
public boolean isLibsDisguisesEnabled() {
return libsDisguises;
}
public void setLibsdisguisesEnabled(boolean enabled) {
libsdisguises = enabled;
}
public boolean isIdisguiseEnabled() {
return idisguise;
public void setLibsDisguisesEnabled(boolean enabled) {
libsDisguises = enabled;
}
public Essentials getEssentials() {
@@ -42,8 +42,8 @@ public class BukkitTabPlayer extends ITabPlayer {
private Object playerConnection;
//player's visible boss bars
private final Map<UUID, BossBar> bossbars = new HashMap<>();
private final Map<UUID, com.viaversion.viaversion.api.legacy.bossbar.BossBar> viaBossbars = new HashMap<>();
private final Map<UUID, BossBar> bossBars = new HashMap<>();
private final Map<UUID, com.viaversion.viaversion.api.legacy.bossbar.BossBar> viaBossBars = new HashMap<>();
/**
* Constructs new instance with given parameter
@@ -102,36 +102,38 @@ public class BukkitTabPlayer extends ITabPlayer {
}
private void handle(PacketPlayOutBoss packet) {
BossBar bar;
switch (packet.getOperation()) {
case 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()),
BossBar bar = bossBars.get(packet.getId());
if (packet.getOperation() == 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);
bossBars.put(packet.getId(), bar);
bar.addPlayer(getPlayer());
break;
return;
}
if (bar == null) return; //no idea how
switch (packet.getOperation()) {
case REMOVE:
bossbars.get(packet.getId()).removePlayer(getPlayer());
bossbars.remove(packet.getId());
bar.removePlayer(getPlayer());
bossBars.remove(packet.getId());
break;
case UPDATE_PCT:
bossbars.get(packet.getId()).setProgress(packet.getPct());
bar.setProgress(packet.getPct());
break;
case UPDATE_NAME:
bossbars.get(packet.getId()).setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16));
bar.setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16));
break;
case UPDATE_STYLE:
bossbars.get(packet.getId()).setColor(BarColor.valueOf(packet.getColor().name()));
bossbars.get(packet.getId()).setStyle(BarStyle.valueOf(packet.getOverlay().getBukkitName()));
bar.setColor(BarColor.valueOf(packet.getColor().name()));
bar.setStyle(BarStyle.valueOf(packet.getOverlay().getBukkitName()));
break;
case UPDATE_PROPERTIES:
bar = bossbars.get(packet.getId());
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);
@@ -145,7 +147,7 @@ public class BukkitTabPlayer extends ITabPlayer {
com.viaversion.viaversion.api.legacy.bossbar.BossBar bar;
switch (packet.getOperation()) {
case ADD:
if (viaBossbars.containsKey(packet.getId())) return;
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()),
@@ -153,25 +155,25 @@ public class BukkitTabPlayer extends ITabPlayer {
//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);
viaBossBars.put(packet.getId(), bar);
bar.addPlayer(getPlayer().getUniqueId());
break;
case REMOVE:
viaBossbars.get(packet.getId()).removePlayer(getPlayer().getUniqueId());
viaBossbars.remove(packet.getId());
viaBossBars.get(packet.getId()).removePlayer(getPlayer().getUniqueId());
viaBossBars.remove(packet.getId());
break;
case UPDATE_PCT:
viaBossbars.get(packet.getId()).setHealth(packet.getPct());
viaBossBars.get(packet.getId()).setHealth(packet.getPct());
break;
case UPDATE_NAME:
viaBossbars.get(packet.getId()).setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16));
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()));
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());
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);
@@ -213,12 +215,12 @@ public class BukkitTabPlayer extends ITabPlayer {
@Override
public boolean isDisguised() {
try {
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).isLibsdisguisesEnabled()) return false;
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).isLibsDisguisesEnabled()) return false;
return DisguiseAPI.isDisguised(getPlayer());
} catch (NoClassDefFoundError | ExceptionInInitializerError e) {
} catch (LinkageError 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);
((BukkitPlatform)TAB.getInstance().getPlatform()).setLibsDisguisesEnabled(false);
return false;
}
}
@@ -256,4 +258,4 @@ public class BukkitTabPlayer extends ITabPlayer {
public int getGamemode() {
return getPlayer().getGameMode().getValue();
}
}
}
@@ -7,11 +7,9 @@ import java.util.List;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import com.viaversion.viaversion.api.Via;
@@ -22,14 +20,15 @@ import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import org.jetbrains.annotations.NotNull;
/**
* Main class for Bukkit platform
*/
public class Main extends JavaPlugin {
private boolean viaversion;
private boolean protocolsupport;
private boolean viaVersion;
private boolean protocolSupport;
@Override
public void onEnable(){
@@ -38,22 +37,24 @@ public class Main extends JavaPlugin {
Bukkit.getPluginManager().disablePlugin(this);
return;
}
viaversion = Bukkit.getPluginManager().isPluginEnabled("ViaVersion");
protocolsupport = Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport");
viaVersion = Bukkit.getPluginManager().isPluginEnabled("ViaVersion");
protocolSupport = Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport");
TAB.setInstance(new TAB(new BukkitPlatform(this), ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0])));
if (TAB.getInstance().getServerVersion() == ProtocolVersion.UNKNOWN) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Unknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
}
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(this), this);
TABCommand command = new TABCommand();
Bukkit.getPluginCommand("tab").setExecutor(command);
Bukkit.getPluginCommand("tab").setTabCompleter(command);
TAB.getInstance().load();
Metrics metrics = new Metrics(this, 5304);
metrics.addCustomChart(new SimplePie("unlimited_nametag_mode_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx") ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("unlimited_nametag_mode_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("placeholderapi", () -> Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI") ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("permission_system", () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie("server_version", () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
PluginCommand cmd = Bukkit.getPluginCommand("tab");
if (cmd == null) return;
TABCommand command = new TABCommand();
cmd.setExecutor(command);
cmd.setTabCompleter(command);
}
@Override
@@ -101,12 +102,12 @@ public class Main extends JavaPlugin {
* @return protocol version of this player
*/
public int getProtocolVersion(Player player) {
if (protocolsupport){
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) {
if (viaVersion) {
return getProtocolVersionVia(player, 0);
}
return TAB.getInstance().getServerVersion().getNetworkId();
@@ -149,16 +150,17 @@ public class Main extends JavaPlugin {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
} catch (Exception | NoClassDefFoundError e) {
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ViaVersion v%s", player.getName(), Bukkit.getPluginManager().getPlugin("ViaVersion").getDescription().getVersion()), e);
} catch (Exception | LinkageError e) {
Plugin via = Bukkit.getPluginManager().getPlugin("ViaVersion");
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ViaVersion v%s", player.getName(), via == null ? "" : via.getDescription().getVersion()), e);
return TAB.getInstance().getServerVersion().getNetworkId();
}
}
public class TABCommand implements CommandExecutor, TabCompleter {
public static class TABCommand implements CommandExecutor, TabCompleter {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (TAB.getInstance().isDisabled()) {
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));
@@ -175,7 +177,7 @@ public class Main extends JavaPlugin {
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
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());
@@ -2,21 +2,25 @@ package me.neznamy.tab.platforms.bukkit.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* Bukkit event that is called each time TAB fully loads. This includes server startup, reload,
* /tab reload and /plugman reload tab
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.plugin.TabLoadEvent}
*/
@Deprecated
public class TabLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@Override
public HandlerList getHandlers(){
public @NotNull HandlerList getHandlers(){
return getHandlerList();
}
public static HandlerList getHandlerList() {
return handlers;
}
}
}
@@ -4,10 +4,14 @@ import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import me.neznamy.tab.api.TabPlayer;
import org.jetbrains.annotations.NotNull;
/**
* Bukkit event that is called when player is successfully loaded after joining. This also includes plugin reloading.
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.player.PlayerLoadEvent}
*/
@Deprecated
public class TabPlayerLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@@ -19,7 +23,7 @@ public class TabPlayerLoadEvent extends Event {
}
@Override
public HandlerList getHandlers(){
public @NotNull HandlerList getHandlers(){
return getHandlerList();
}
@@ -30,4 +34,4 @@ public class TabPlayerLoadEvent extends Event {
public TabPlayer getPlayer() {
return player;
}
}
}
@@ -19,10 +19,10 @@ import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
/**
* Per-world-playerlist feature handler
* Per-world-PlayerList feature handler
*/
@SuppressWarnings("deprecation")
public class PerWorldPlayerlist extends TabFeature implements Listener {
public class PerWorldPlayerList extends TabFeature implements Listener {
//plugin instance
private final JavaPlugin plugin;
@@ -36,10 +36,10 @@ public class PerWorldPlayerlist extends TabFeature implements Listener {
* Constructs new instance with given parameters and loads config options
* @param plugin - plugin instance
*/
public PerWorldPlayerlist(JavaPlugin plugin) {
super("Per world playerlist", null);
public PerWorldPlayerList(JavaPlugin plugin) {
super("Per world PlayerList", null);
this.plugin = plugin;
TAB.getInstance().debug(String.format("Loaded PerWorldPlayerlist feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
TAB.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@@ -1,8 +1,6 @@
package me.neznamy.tab.platforms.bukkit.features;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
@@ -12,8 +10,8 @@ import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
import me.neznamy.tab.shared.TAB;
/**
* 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 (<1.15) / entity metadata packets and removing owner field from the datawatcher list
* 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 (<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 {
@@ -21,11 +19,11 @@ public class PetFix extends TabFeature {
//nms storage
private final NMSStorage nms = NMSStorage.getInstance();
//datawatcher position of pet owner field
//DataWatcher position of pet owner field
private final int petOwnerPosition = getPetOwnerPosition();
//logger of last interacts to prevent feature not working on 1.16
private final Map<String, Long> lastInteractFix = new HashMap<>();
private final WeakHashMap<TabPlayer, Long> lastInteractFix = new WeakHashMap<>();
/**
* Constructs new instance with given parameter
@@ -60,18 +58,19 @@ public class PetFix extends TabFeature {
/**
* Cancels a packet if previous one arrived with no delay to prevent double toggle on 1.16
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@Override
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
if (nms.PacketPlayInUseEntity.isInstance(packet)) {
if (lastInteractFix.containsKey(sender.getName()) && (System.currentTimeMillis() - lastInteractFix.get(sender.getName()) < 5)) {
if (lastInteractFix.containsKey(sender) && (System.currentTimeMillis() - lastInteractFix.get(sender) < 5)) {
//last interact packet was sent right now, cancelling to prevent double-toggle due to this feature enabled
return true;
}
if (isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
//this is the first packet, saving player so the next packet can be cancelled
lastInteractFix.put(sender.getName(), System.currentTimeMillis());
lastInteractFix.put(sender, System.currentTimeMillis());
}
}
return false;
@@ -86,8 +85,9 @@ public class PetFix extends TabFeature {
}
/**
* Removes pet owner field from datawatcher
* @throws ReflectiveOperationException
* Removes pet owner field from DataWatcher
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@SuppressWarnings("unchecked")
@Override
@@ -96,14 +96,19 @@ public class PetFix extends TabFeature {
Object removedEntry = null;
List<Object> items = (List<Object>) nms.PacketPlayOutEntityMetadata_LIST.get(packet);
if (items == null) return;
for (Object item : items) {
if (item == null) continue;
if (nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item)) == petOwnerPosition) {
Object value = nms.DataWatcherItem_VALUE.get(item);
if (value instanceof java.util.Optional || value instanceof com.google.common.base.Optional) {
removedEntry = item;
try {
for (Object item : items) {
if (item == null) continue;
if (nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item)) == petOwnerPosition) {
Object value = nms.DataWatcherItem_VALUE.get(item);
if (value instanceof java.util.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) {
@@ -116,12 +121,4 @@ public class PetFix extends TabFeature {
}
}
}
/**
* Removing player data to not have a memory leak
*/
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
lastInteractFix.remove(disconnectedPlayer.getName());
}
}
@@ -1,5 +1,6 @@
package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.shared.TabConstants;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
@@ -12,7 +13,7 @@ import me.neznamy.tab.api.bossbar.BossBarManager;
import me.neznamy.tab.api.scoreboard.Scoreboard;
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
import org.jetbrains.annotations.NotNull;
/**
* TAB's expansion for PlaceholderAPI
@@ -42,22 +43,22 @@ public class TabExpansion extends PlaceholderExpansion {
}
@Override
public String getAuthor(){
public @NotNull String getAuthor(){
return plugin.getDescription().getAuthors().toString();
}
@Override
public String getIdentifier(){
public @NotNull String getIdentifier(){
return "tab";
}
@Override
public String getVersion(){
public @NotNull String getVersion(){
return plugin.getDescription().getVersion();
}
@Override
public String onPlaceholderRequest(Player player, String identifier){
public String onPlaceholderRequest(Player player, @NotNull String identifier){
if (player == null) return "";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if ("scoreboard_visible".equals(identifier)) {
@@ -82,21 +83,21 @@ public class TabExpansion extends PlaceholderExpansion {
}
private boolean hasBossBarVisible(TabPlayer p) {
BossBarManager boss = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature("bossbar");
BossBarManager boss = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.BOSS_BAR);
if (boss == null) return false;
return boss.hasBossBarVisible(p);
}
private boolean hasScoreboardVisible(TabPlayer p) {
ScoreboardManager scoreboard = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
ScoreboardManager scoreboard = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SCOREBOARD);
if (scoreboard == null) return false;
return scoreboard.hasScoreboardVisible(p);
}
private String getActiveScoreboard(TabPlayer p) {
ScoreboardManagerImpl sb = (ScoreboardManagerImpl) TabAPI.getInstance().getScoreboardManager();
ScoreboardManager sb = TabAPI.getInstance().getScoreboardManager();
if (sb == null) return "";
Scoreboard active = sb.getActiveScoreboards().get(p);
Scoreboard active = sb.getActiveScoreboard(p);
if (active == null) return "";
return active.getName();
}
@@ -1,6 +1,6 @@
package me.neznamy.tab.platforms.bukkit.features;
import java.util.Arrays;
import java.util.Collections;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -32,10 +32,10 @@ public class WitherBossBar extends BossBarManagerImpl implements Listener {
*/
public WitherBossBar(JavaPlugin plugin) {
Bukkit.getPluginManager().registerEvents(this, plugin);
//when MC is on fullscreen, bossbar disappears after 1 second of not being seen
//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().getPlaceholderManager().registerPlayerPlaceholder("%location%", 100, p -> ((Player)p.getPlayer()).getLocation());
addUsedPlaceholders(Arrays.asList("%location%"));
addUsedPlaceholders(Collections.singletonList("%location%"));
}
@Override
@@ -27,12 +27,12 @@ import me.neznamy.tab.shared.TAB;
*/
public class BukkitArmorStand implements ArmorStand {
private static boolean alwaysVisible = TAB.getInstance().getConfiguration().isArmorStandsAlwaysVisible();
private final boolean alwaysVisible = TAB.getInstance().getConfiguration().isArmorStandsAlwaysVisible();
//entity id counter to pick unique entity IDs
private static int idCounter = 2000000000;
//nametag feature
private final NameTagX manager = (NameTagX) TAB.getInstance().getFeatureManager().getFeature("nametagx");
//NameTag feature
private final NameTagX manager = (NameTagX) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
//owner of the armor stand
private final TabPlayer owner;
@@ -59,7 +59,7 @@ public class BukkitArmorStand implements ArmorStand {
private final Property property;
//if offset is static or dynamic based on other armor stands
private boolean staticOffset;
private final boolean staticOffset;
//entity destroy packet
private final PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(entityId);
@@ -119,7 +119,7 @@ public class BukkitArmorStand implements ArmorStand {
@Override
public void destroy() {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) all.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
for (TabPlayer all : owner.getArmorStandManager().getNearbyPlayers()) all.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
}
@Override
@@ -149,7 +149,7 @@ public class BukkitArmorStand implements ArmorStand {
this.sneaking = sneaking;
for (TabPlayer viewer : owner.getArmorStandManager().getNearbyPlayers()) {
if (viewer.getVersion().getMinorVersion() == 14 && !alwaysVisible) {
//1.14.x client sided bug, despawning completely
//1.14.x client sided bug, de-spawning completely
if (sneaking) {
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SNEAK);
} else {
@@ -196,7 +196,7 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns general visibility rule for everyone with limited info
* @return true if should be visible, false if not
* @return true if armor stand should be visible, false if not
*/
public boolean getVisibility() {
if (owner.isDisguised() || manager.getVehicleManager().isOnBoat(owner)) return false;
@@ -261,7 +261,7 @@ public class BukkitArmorStand implements ArmorStand {
* Creates data watcher with specified display name for viewer
* @param displayName - armor stand name
* @param viewer - player to apply checks against
* @return datawatcher for viewer
* @return DataWatcher for viewer
*/
public DataWatcher createDataWatcher(String displayName, TabPlayer viewer) {
DataWatcher datawatcher = new DataWatcher();
@@ -299,7 +299,6 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns list of packets to send to make armor stand spawn with metadata
* @param viewer - viewer to apply relational placeholders for
* @param addToRegistered - if player should be added to registered or not
* @return List of packets that spawn the armor stand
*/
public TabPacket[] getSpawnPackets(TabPlayer viewer) {
@@ -16,25 +16,25 @@ import me.neznamy.tab.shared.TAB;
*/
public class EventListener implements Listener {
//the nametag feature handler
//the NameTag feature handler
private final NameTagX feature;
/**
* Constructs new instance with given parameters
* @param feature - nametagx feature handler
* @param feature - NameTag feature handler
*/
public EventListener(NameTagX feature) {
this.feature = feature;
}
/**
* Sneak event listener to despawn & spawn armor stands to skip animation
* Sneak event listener to de-spawn & spawn armor stands to skip animation
* @param e - sneak event
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSneak(PlayerToggleSneakEvent e) {
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
if (p == null || !p.isLoaded() || feature.isPlayerDisabled(p)) return;
if (p == null || feature.isPlayerDisabled(p)) return;
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerToggleSneakEvent", feature, TabConstants.CpuUsageCategory.PLAYER_SNEAK, () -> {
if (p.getArmorStandManager() != null) p.getArmorStandManager().sneak(e.isSneaking());
});
@@ -56,12 +56,11 @@ public class EventListener implements Listener {
@EventHandler
public void onWorldChange(PlayerChangedWorldEvent e) {
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
if (p == null) return;
if (p == null || !p.isLoaded()) return;
long time = System.nanoTime();
String to = e.getPlayer().getWorld().getName();
if (feature.isDisabled(to)) {
if (!feature.getPlayersInDisabledUnlimitedWorlds().contains(p))
feature.getPlayersInDisabledUnlimitedWorlds().add(p);
feature.getPlayersInDisabledUnlimitedWorlds().add(p);
} else {
feature.getPlayersInDisabledUnlimitedWorlds().remove(p);
}
@@ -1,38 +0,0 @@
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import java.util.Arrays;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
public class LocationRefresher extends TabFeature {
private final NameTagX feature;
public LocationRefresher(NameTagX feature) {
super(feature.getFeatureName(), "Processing passengers / preview");
this.feature = feature;
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%location0%", 50, p -> {
if (!feature.getVehicleManager().getPlayersInVehicle().containsKey(p) && !p.isPreviewingNametag()) return null;
Location l = ((Player)p.getPlayer()).getLocation();
return l.getX()+l.getY()+l.getZ(); //less cpu usage than literal Location#toString() and then .equals() when comparing last value
});
addUsedPlaceholders(Arrays.asList("%location0%")); //making sure it's not the same one as in WiherBossBar
}
@Override
public void refresh(TabPlayer p, boolean force) {
if (feature.getVehicleManager().getPlayersInVehicle().containsKey(p)) {
feature.getVehicleManager().processPassengers((Entity) p.getPlayer());
}
if (p.isPreviewingNametag()) {
p.getArmorStandManager().teleport(p);
}
}
}
@@ -1,10 +1,6 @@
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
@@ -24,7 +20,7 @@ import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.nametags.NameTag;
/**
* The core class for unlimited nametag mode
* The core class for unlimited NameTag mode
*/
public class NameTagX extends NameTag implements UnlimitedNametagManager {
@@ -42,26 +38,24 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
//bukkit event listener
private final EventListener eventListener = new EventListener(this);
private final List<TabPlayer> playersInDisabledUnlimitedWorlds = new ArrayList<>();
private final Set<TabPlayer> playersInDisabledUnlimitedWorlds = Collections.newSetFromMap(new WeakHashMap<>());
private final String[] disabledUnlimitedWorldsArray = disabledUnlimitedWorlds.toArray(new String[0]);
private final boolean unlimitedWorldWhitelistMode = disabledUnlimitedWorlds.contains("WHITELIST");
private final List<TabPlayer> playersDisabledWithAPI = new ArrayList<>();
private final Set<TabPlayer> playersDisabledWithAPI = Collections.newSetFromMap(new WeakHashMap<>());
private final VehicleRefresher vehicleManager = new VehicleRefresher(this);
/**
* Constructs new instance with given parameters and loads config options
* @param plugin - plugin instance
* @param nms - nms storage
*/
public NameTagX(JavaPlugin plugin) {
Collections.reverse(dynamicLines);
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
TAB.getInstance().getFeatureManager().registerFeature("nametagx-packet", new PacketListener(this));
TAB.getInstance().getFeatureManager().registerFeature("nametagx-vehicle", vehicleManager);
TAB.getInstance().getFeatureManager().registerFeature("nametagx-location", new LocationRefresher(this));
TAB.getInstance().debug(String.format("Loaded Unlimited nametag feature with parameters markerFor18x=%s, disableOnBoats=%s, spaceBetweenLines=%s, disabledUnlimitedWorlds=%s",
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, new PacketListener(this));
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
TAB.getInstance().debug(String.format("Loaded Unlimited NameTag feature with parameters markerFor18x=%s, disableOnBoats=%s, spaceBetweenLines=%s, disabledUnlimitedWorlds=%s",
markerFor18x, disableOnBoats, spaceBetweenLines, disabledUnlimitedWorlds));
}
@@ -85,10 +79,10 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
}
private void startVisibilityRefreshTask() {
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(500, "refreshing nametag visibility", this, TabConstants.CpuUsageCategory.REFRESHING_NAMETAG_VISIBILITY, () -> {
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(500, "refreshing NameTag visibility", this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (!p.isLoaded() || isPlayerDisabled(p)) continue;
if (isPlayerDisabled(p)) continue;
p.getArmorStandManager().updateVisibility(false);
}
});
@@ -106,7 +100,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
@Override
public void onJoin(TabPlayer connectedPlayer) {
if (isDisabled(connectedPlayer.getWorld()) && !playersInDisabledUnlimitedWorlds.contains(connectedPlayer))
if (isDisabled(connectedPlayer.getWorld()))
playersInDisabledUnlimitedWorlds.add(connectedPlayer);
super.onJoin(connectedPlayer);
getEntityIdMap().put(((Player) connectedPlayer.getPlayer()).getEntityId(), connectedPlayer);
@@ -136,9 +130,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.getArmorStandManager() != null) all.getArmorStandManager().unregisterPlayer(disconnectedPlayer);
}
getEntityIdMap().remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
playersInDisabledUnlimitedWorlds.remove(disconnectedPlayer);
playersDisabledWithAPI.remove(disconnectedPlayer);
entityIdMap.remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
if (disconnectedPlayer.getArmorStandManager() != null) { //player was not loaded yet
disconnectedPlayer.getArmorStandManager().destroy();
TAB.getInstance().getCPUManager().runTaskLater(500, "processing onQuit", this, TabConstants.CpuUsageCategory.PLAYER_QUIT, () -> disconnectedPlayer.getArmorStandManager().destroy());
@@ -152,7 +144,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
* @param sendMutually - if packets should be sent both ways
*/
public void spawnArmorStands(TabPlayer owner, TabPlayer viewer, boolean sendMutually) {
if (owner == viewer) return; //not displaying own armorstands
if (owner == viewer) return; //not displaying own armor stands
if (((Player) viewer.getPlayer()).getWorld() != ((Player) owner.getPlayer()).getWorld()) return; //different world
if (isPlayerDisabled(owner)) return;
if (getDistance(viewer, owner) <= 48) {
@@ -232,26 +224,29 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
* @param p - player to update
*/
@Override
public void updateProperties(TabPlayer p) {
super.updateProperties(p);
p.loadPropertyFromConfig(this, TabConstants.Property.CUSTOMTAGNAME, p.getName());
p.setProperty(this, TabConstants.Property.NAMETAG, p.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue() + p.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue() + p.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
public boolean updateProperties(TabPlayer p) {
boolean changed = super.updateProperties(p);
if (p.loadPropertyFromConfig(this, TabConstants.Property.CUSTOMTAGNAME, p.getName())) changed = true;
if (p.setProperty(this, TabConstants.Property.NAMETAG, p.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue() +
p.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue() + p.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue())) changed = true;
for (String property : dynamicLines) {
if (!property.equals(TabConstants.Property.NAMETAG)) p.loadPropertyFromConfig(this, property);
if (!property.equals(TabConstants.Property.NAMETAG) && p.loadPropertyFromConfig(this, property)) changed = true;
}
for (String property : staticLines.keySet()) {
if (!property.equals(TabConstants.Property.NAMETAG)) p.loadPropertyFromConfig(this, property);
if (!property.equals(TabConstants.Property.NAMETAG) && p.loadPropertyFromConfig(this, property)) changed = true;
}
return changed;
}
@Override
public String getFeatureName() {
return "Unlimited Nametags";
return "Unlimited NameTags";
}
@Override
public boolean getTeamVisibility(TabPlayer p, TabPlayer viewer) {
if (p.hasInvisibilityPotion()) return false; //1.8.x client sided bug
if (vehicleManager == null) return false; //class is still being initialized and this was called from super constructor
return vehicleManager.isOnBoat(p) || isPlayerDisabled(p);
}
@@ -273,7 +268,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
return markerFor18x;
}
public List<TabPlayer> getPlayersInDisabledUnlimitedWorlds() {
public Set<TabPlayer> getPlayersInDisabledUnlimitedWorlds() {
return playersInDisabledUnlimitedWorlds;
}
@@ -329,35 +324,35 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
@Override
public void setPrefix(TabPlayer player, String prefix) {
player.getProperty(TabConstants.Property.TAGPREFIX).setTemporaryValue(prefix);
rebuildNametagLine(player);
rebuildNameTagLine(player);
player.forceRefresh();
}
@Override
public void setSuffix(TabPlayer player, String suffix) {
player.getProperty(TabConstants.Property.TAGSUFFIX).setTemporaryValue(suffix);
rebuildNametagLine(player);
rebuildNameTagLine(player);
player.forceRefresh();
}
@Override
public void resetPrefix(TabPlayer player) {
player.getProperty(TabConstants.Property.TAGPREFIX).setTemporaryValue(null);
rebuildNametagLine(player);
rebuildNameTagLine(player);
player.forceRefresh();
}
@Override
public void resetSuffix(TabPlayer player) {
player.getProperty(TabConstants.Property.TAGSUFFIX).setTemporaryValue(null);
rebuildNametagLine(player);
rebuildNameTagLine(player);
player.forceRefresh();
}
@Override
public void setName(TabPlayer player, String customname) {
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).setTemporaryValue(customname);
rebuildNametagLine(player);
public void setName(TabPlayer player, String customName) {
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).setTemporaryValue(customName);
rebuildNameTagLine(player);
player.forceRefresh();
}
@@ -370,7 +365,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
@Override
public void resetName(TabPlayer player) {
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).setTemporaryValue(null);
rebuildNametagLine(player);
rebuildNameTagLine(player);
player.forceRefresh();
}
@@ -400,7 +395,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
return player.getProperty(line).getOriginalRawValue();
}
private void rebuildNametagLine(TabPlayer player) {
private void rebuildNameTagLine(TabPlayer player) {
player.setProperty(this, TabConstants.Property.NAMETAG, player.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue() +
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue() + player.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
}
@@ -13,7 +13,7 @@ import me.neznamy.tab.shared.TAB;
/**
* The packet listening part for securing proper functionality of armor stands
* Bukkit events are too unreliable and delayed/ahead which causes desync
* Bukkit events are too unreliable and delayed/ahead which causes de-sync
*/
public class PacketListener extends TabFeature {
@@ -119,8 +119,8 @@ public class PacketListener extends TabFeature {
}
private void onEntityDestroy(TabPlayer receiver, int entity) {
TabPlayer despawnedPlayer = nameTagX.getEntityIdMap().get(entity);
if (despawnedPlayer != null && despawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(despawnedPlayer))
TAB.getInstance().getCPUManager().runMeasuredTask("processing EntityDestroy", nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY, () -> despawnedPlayer.getArmorStandManager().destroy(receiver));
TabPlayer deSpawnedPlayer = nameTagX.getEntityIdMap().get(entity);
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(deSpawnedPlayer))
TAB.getInstance().getCPUManager().runMeasuredTask("processing EntityDestroy", nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY, () -> deSpawnedPlayer.getArmorStandManager().destroy(receiver));
}
}
@@ -1,11 +1,9 @@
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import me.neznamy.tab.shared.TabConstants;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
@@ -16,24 +14,50 @@ import me.neznamy.tab.shared.TAB;
public class VehicleRefresher extends TabFeature {
//list of players currently in a vehicle
private final Map<TabPlayer, Entity> playersInVehicle = new ConcurrentHashMap<>();
//map of players currently in a vehicle
private final WeakHashMap<TabPlayer, Entity> playersInVehicle = new WeakHashMap<>();
//map of vehicles carrying players
private final Map<Integer, List<Entity>> vehicles = new ConcurrentHashMap<>();
//list of players currently on boats
private final List<TabPlayer> playersOnBoats = new ArrayList<>();
//set of players currently on boats
private final Set<TabPlayer> playersOnBoats = Collections.newSetFromMap(new WeakHashMap<>());
private final NameTagX feature;
public VehicleRefresher(NameTagX feature) {
super(feature.getFeatureName(), "Refreshing vehicles");
this.feature = feature;
addUsedPlaceholders(Arrays.asList("%vehicle%"));
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%vehicle%", 100, p -> ((Player)p.getPlayer()).getVehicle());
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(50, "processing player movement",
this, TabConstants.CpuUsageCategory.PROCESSING_PLAYER_MOVEMENT, () -> {
for (TabPlayer inVehicle : playersInVehicle.keySet()) {
inVehicle.getArmorStandManager().teleport();
// feature.getVehicleManager().processPassengers((Entity) inVehicle.getPlayer());
}
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (p.isPreviewingNametag()) {
p.getArmorStandManager().teleport(p);
}
}
});
addUsedPlaceholders(Collections.singletonList("%vehicle%"));
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%vehicle%", 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
}
@Override
public void load() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
Entity vehicle = ((Player)p.getPlayer()).getVehicle();
if (vehicle != null) {
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
playersInVehicle.put(p, vehicle);
if (feature.isDisableOnBoats() && vehicle.getType() == EntityType.BOAT) {
playersOnBoats.add(p);
}
}
}
}
@Override
public void refresh(TabPlayer p, boolean force) {
if (feature.isPlayerDisabled(p)) return;
@@ -67,8 +91,6 @@ public class VehicleRefresher extends TabFeature {
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
if (playersInVehicle.containsKey(disconnectedPlayer)) vehicles.remove(playersInVehicle.get(disconnectedPlayer).getEntityId());
playersInVehicle.remove(disconnectedPlayer);
playersOnBoats.remove(disconnectedPlayer);
}
public Map<Integer, List<Entity>> getVehicles() {
@@ -86,7 +108,7 @@ public class VehicleRefresher extends TabFeature {
return vehicle.getPassengers();
} else {
if (vehicle.getPassenger() != null) {
return Arrays.asList(vehicle.getPassenger());
return Collections.singletonList(vehicle.getPassenger());
} else {
return new ArrayList<>();
}
@@ -98,8 +120,8 @@ public class VehicleRefresher extends TabFeature {
* @param p - player to load passengers of
*/
public void loadPassengers(TabPlayer p) {
if (((Entity) p.getPlayer()).getVehicle() == null) return;
Entity vehicle = ((Entity) p.getPlayer()).getVehicle();
if (vehicle == null) return;
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
}
@@ -115,8 +137,4 @@ public class VehicleRefresher extends TabFeature {
processPassengers(passenger);
}
}
public Map<TabPlayer, Entity> getPlayersInVehicle() {
return playersInVehicle;
}
}
@@ -38,14 +38,13 @@ public final class NMSStorage {
//another field due to fabric which uses 1.16 package on 1.17 server
private final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]);
//base
private final Class<?> Packet = getNMSClass("net.minecraft.network.protocol.Packet", "Packet");
public final Class<?> EnumChatFormat = getNMSClass("net.minecraft.EnumChatFormat", "EnumChatFormat");
public final Class<?> EntityPlayer = getNMSClass("net.minecraft.server.level.EntityPlayer", "EntityPlayer");
private final Class<?> Entity = getNMSClass("net.minecraft.world.entity.Entity", "Entity");
private final Class<?> EntityLiving = getNMSClass("net.minecraft.world.entity.EntityLiving", "EntityLiving");
private final Class<?> NetworkManager = getNMSClass("net.minecraft.network.NetworkManager", "NetworkManager");
private final Class<?> PlayerConnection = getNMSClass("net.minecraft.server.network.PlayerConnection", "PlayerConnection");
public Constructor<?> newEntityArmorStand;
public final Field PING = getField(EntityPlayer, "ping", "latency", "field_71138_i", "field_13967", "e");
@@ -224,9 +223,12 @@ public final class NMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* If any class, field or method fails to load
*/
public NMSStorage() throws ReflectiveOperationException {
ProtocolVersion.UNKNOWN.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
Class<?> NetworkManager = getNMSClass("net.minecraft.network.NetworkManager", "NetworkManager");
if (minorVersion >= 7) {
NETWORK_MANAGER = getFields(PlayerConnection, NetworkManager).get(0);
}
@@ -255,7 +257,7 @@ public final class NMSStorage {
* Sets new instance
* @param instance - new instance
*/
public final static void setInstance(NMSStorage instance) {
public static void setInstance(NMSStorage instance) {
NMSStorage.instance = instance;
}
@@ -263,11 +265,11 @@ public final class NMSStorage {
* Returns instance
* @return instance
*/
public final static NMSStorage getInstance() {
public static NMSStorage getInstance() {
return instance;
}
private final void initializeChatComponents() throws ReflectiveOperationException {
private void initializeChatComponents() throws ReflectiveOperationException {
if (minorVersion < 7) return;
Class<?> ChatBaseComponent = getNMSClass("net.minecraft.network.chat.ChatBaseComponent", "ChatBaseComponent");
Class<?> ChatClickable = getNMSClass("net.minecraft.network.chat.ChatClickable", "ChatClickable");
@@ -318,7 +320,7 @@ public final class NMSStorage {
}
}
private final void initializeChatPacket() throws ReflectiveOperationException {
private void initializeChatPacket() throws ReflectiveOperationException {
Class<?> PacketPlayOutChat = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutChat", "PacketPlayOutChat", "Packet3Chat");
if (minorVersion >= 12) {
ChatMessageType = getNMSClass("net.minecraft.network.chat.ChatMessageType", "ChatMessageType");
@@ -335,7 +337,7 @@ public final class NMSStorage {
}
}
private final void initializeDataWatcher() throws ReflectiveOperationException {
private void initializeDataWatcher() throws ReflectiveOperationException {
if (minorVersion >= 9) {
Class<?> DataWatcherObject = getNMSClass("net.minecraft.network.syncher.DataWatcherObject", "DataWatcherObject");
DataWatcherRegistry = getNMSClass("net.minecraft.network.syncher.DataWatcherRegistry", "DataWatcherRegistry");
@@ -352,7 +354,7 @@ public final class NMSStorage {
registry = new DataWatcherRegistry(this);
}
private final void initializeEntityPackets() throws ReflectiveOperationException {
private void initializeEntityPackets() throws ReflectiveOperationException {
if (minorVersion >= 17 || serverVersion.getMinorVersion() >= 17) {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor(Entity);
@@ -395,7 +397,7 @@ public final class NMSStorage {
}
}
private final void initializeHeaderFooterPacket() throws ReflectiveOperationException {
private void initializeHeaderFooterPacket() throws ReflectiveOperationException {
if (minorVersion < 8) return;
Class<?> PacketPlayOutPlayerListHeaderFooter = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter", "PacketPlayOutPlayerListHeaderFooter");
PacketPlayOutPlayerListHeaderFooter_HEADER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(0);
@@ -407,7 +409,7 @@ public final class NMSStorage {
}
}
private final void initializePlayerInfoPacket() throws ReflectiveOperationException {
private void initializePlayerInfoPacket() throws ReflectiveOperationException {
if (minorVersion < 8) return;
PacketPlayOutPlayerInfo = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo", "PacketPlayOutPlayerInfo");
Class<?> EnumPlayerInfoAction = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction", "net.minecraft.class_2703$class_5893");
@@ -425,7 +427,7 @@ public final class NMSStorage {
EnumGamemode_values = getEnumValues(EnumGamemode);
}
private final void initializeScoreboardPackets() throws ReflectiveOperationException {
private void initializeScoreboardPackets() throws ReflectiveOperationException {
List<Field> list = getFields(PacketPlayOutScoreboardObjective, int.class);
PacketPlayOutScoreboardObjective_METHOD = list.get(list.size()-1);
Class<?> EnumScoreboardAction = null;
@@ -452,15 +454,15 @@ public final class NMSStorage {
}
}
private final void initializeTeamPackets() throws ReflectiveOperationException {
private void initializeTeamPackets() throws ReflectiveOperationException {
PacketPlayOutScoreboardTeam = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam", "PacketPlayOutScoreboardTeam", "Packet209SetScoreboardTeam");
Class<?> ScoreboardTeam = getNMSClass("net.minecraft.world.scores.ScoreboardTeam", "ScoreboardTeam");
newScoreboardTeam = ScoreboardTeam.getConstructor(Scoreboard, String.class);
PacketPlayOutScoreboardTeam_NAME = getFields(PacketPlayOutScoreboardTeam, String.class).get(0);
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
ScoreboardTeam_getPlayerNameSet = getMethods(ScoreboardTeam, Collection.class).get(0);
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a"}, boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[]{"setCanSeeFriendlyInvisibles", "b"}, boolean.class);
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a", "func_96660_a"}, boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[]{"setCanSeeFriendlyInvisibles", "b", "func_98300_b"}, boolean.class);
if (minorVersion >= 8) {
Class<?> EnumNameTagVisibility = getNMSClass("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility", "ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
EnumNameTagVisibility_values = getEnumValues(EnumNameTagVisibility);
@@ -519,14 +521,14 @@ public final class NMSStorage {
private Class<?> getLegacyClass(String name) throws ClassNotFoundException {
try {
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
} catch (ClassNotFoundException e) {
} catch (ClassNotFoundException | NullPointerException e) {
try {
//modded server?
Class<?> clazz = Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
if (clazz != null) return clazz;
throw new ClassNotFoundException(name);
} catch (ClassNotFoundException e1) {
//fabric
} catch (ClassNotFoundException | NullPointerException e1) {
//maybe fabric?
return Class.forName(name);
}
}
@@ -548,7 +550,21 @@ public final class NMSStorage {
//not the first method in array
}
}
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(names) + " with parameters " + Arrays.toString(parameterTypes) + " in class " + clazz.getName());
List<String> list = new ArrayList<>();
for (Method m : clazz.getMethods()) {
if (m.getParameterCount() != parameterTypes.length) continue;
Class<?>[] types = m.getParameterTypes();
boolean valid = true;
for (int i=0; i<types.length; i++) {
if (types[i] != parameterTypes[i]) {
valid = false;
break;
}
}
if (valid) list.add(m.getName());
}
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(names) + " with parameters " +
Arrays.toString(parameterTypes) + " in class " + clazz.getName() + ". Methods with matching parameters: " + list);
}
private Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException {
@@ -558,7 +574,10 @@ public final class NMSStorage {
Class<?>[] types = m.getParameterTypes();
boolean valid = true;
for (int i=0; i<types.length; i++) {
if (types[i] != parameterTypes[i]) valid = false;
if (types[i] != parameterTypes[i]) {
valid = false;
break;
}
}
if (valid) list.add(m);
}
@@ -573,7 +592,10 @@ public final class NMSStorage {
Class<?>[] types = m.getParameterTypes();
boolean valid = true;
for (int i=0; i<types.length; i++) {
if (types[i] != parameterTypes[i]) valid = false;
if (types[i] != parameterTypes[i]) {
valid = false;
break;
}
}
if (valid) list.add(m);
}
@@ -11,7 +11,7 @@ import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
*/
public class DataWatcher {
//datawatcher data
//DataWatcher data
private final Map<Integer, DataWatcherItem> dataValues = new HashMap<>();
//a helper for easier data write
@@ -54,7 +54,8 @@ public class DataWatcher {
/**
* Converts the class into an instance of NMS.DataWatcher
* @return an instance of NMS.DataWatcher with same data
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public Object toNMS() throws ReflectiveOperationException {
NMSStorage nms = NMSStorage.getInstance();
@@ -79,9 +80,10 @@ public class DataWatcher {
/**
* Reads NMS data watcher and returns and instance of this class with same data
* @param nmsWatcher - NMS datawatcher to read
* @param nmsWatcher - NMS DataWatcher to read
* @return an instance of this class with same values
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@SuppressWarnings("unchecked")
public static DataWatcher fromNMS(Object nmsWatcher) throws ReflectiveOperationException {
@@ -9,14 +9,14 @@ import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
/**
* A class to help assigning DataWatcher items as positions often change per-version
* A class to help to assign DataWatcher items as positions often change per-version
*/
public class DataWatcherHelper {
//position of armor stand flags
private final int armorStandFlagsPosition = getArmorStandFlagsPosition();
//original datawatcher to write to
//original DataWatcher to write to
private final DataWatcher data;
//data watcher registry
@@ -25,7 +25,8 @@ public class DataWatcherItem {
* Returns and instance of this class from given NMS item
* @param nmsItem - NMS item
* @return instance of this class with same data
* @throws ReflectiveOperationException
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
public static DataWatcherItem fromNMS(Object nmsItem) throws ReflectiveOperationException {
NMSStorage nms = NMSStorage.getInstance();
@@ -5,7 +5,7 @@ package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
*/
public class DataWatcherObject {
//position in datawatcher
//position in DataWatcher
private final int position;
//value class type used since 1.9
@@ -13,7 +13,7 @@ public class DataWatcherObject {
/**
* Constructs a new instance of this class with given parameters
* @param position - position in datawatcher
* @param position - position in DataWatcher
* @param classType - value class type
*/
public DataWatcherObject(int position, Object classType){
+3 -6
View File
@@ -131,7 +131,7 @@ scoreboard:
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- ""
- " "
- "&6Server Info:"
- "* &bTPS&7: %tps%"
- "* &bUptime&7: &f%server_uptime%"
@@ -145,9 +145,9 @@ scoreboard:
- "* &eKills&7: &f%statistic_player_kills%"
- "* &eDeaths&7: &f%statistic_deaths%"
- "* &eHealth&7: &f%health%"
- ""
- " "
- "&6Personal Info:"
- "* &bRank&7: &f%rank%"
- "* &bRank&7: &f%group%"
- "* &bPing&7: &f%ping%&7ms"
- "* &bWorld&7: &f%world%"
- "%animation:MyAnimation1%"
@@ -163,9 +163,6 @@ placeholders:
date-format: "dd.MM.yyyy"
time-format: "[HH:mm:ss / h:mm a]"
time-offset: 0
remove-strings:
- "[]"
- "< >"
placeholder-output-replacements:
"%essentials_vanished%":
+3 -2
View File
@@ -1,10 +1,11 @@
name: @name@
name: TAB
version: @version@
author: NEZNAMY
main: me.neznamy.tab.platforms.bukkit.Main
api-version: 1.13
softdepend: [PlaceholderAPI, Vault, UltraPermissions, LuckPerms, Essentials, LibsDisguises, ProtocolSupport, ViaVersion]
softdepend: [PlaceholderAPI, Vault, UltraPermissions, LuckPerms, Essentials, LibsDisguises, ProtocolSupport, ViaVersion, floodgate]
website: "https://github.com/NEZNAMY/TAB"
description: @description@
commands:
tab:
description: Plugin's main command
@@ -1,53 +1,50 @@
package me.neznamy.tab.platforms.bungeecord;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PluginMessageHandler;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ChatEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.ServerSwitchEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
/**
* The core for bungeecord forwarding events into all enabled features
* The core for BungeeCord forwarding events into all enabled features
*/
public class BungeeEventListener implements Listener {
private final PluginMessageHandler plm;
public BungeeEventListener(PluginMessageHandler plm) {
this.plm = plm;
}
/**
* Disconnect event listener to forward the event to all features
* @param e - disconnect event
*/
@EventHandler(priority = EventPriority.LOWEST)
@EventHandler
public void onQuit(PlayerDisconnectEvent e){
if (TAB.getInstance().isDisabled()) return;
TAB.getInstance().getFeatureManager().onQuit(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()));
TAB.getInstance().getCPUManager().runTask("processing PlayerDisconnectEvent", () ->
TAB.getInstance().getFeatureManager().onQuit(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
}
/**
* Listener to join / server switch to forward the event to all features
* @param e
* @param e
* switch event
*/
@EventHandler(priority = EventPriority.LOW)
@EventHandler
public void onSwitch(ServerSwitchEvent e){
if (TAB.getInstance().isDisabled()) return;
if (TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()) == null) {
TAB.getInstance().getFeatureManager().onJoin(new BungeeTabPlayer(e.getPlayer(), plm));
} else {
TAB.getInstance().getFeatureManager().onServerChange(e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
}
TAB.getInstance().getCPUManager().runTask("processing ServerSwitchEvent", () -> {
if (TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()) == null) {
TAB.getInstance().getFeatureManager().onJoin(new BungeeTabPlayer(e.getPlayer()));
} else {
TAB.getInstance().getFeatureManager().onServerChange(e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
}
});
}
/**
* Listener to chat packets to forward the event to all features
* @param e
* @param e
* chat event
*/
@EventHandler
public void onChat(ChatEvent e) {
@@ -18,7 +18,6 @@ import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
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.api.protocol.PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay;
import net.md_5.bungee.protocol.packet.BossBar;
import net.md_5.bungee.protocol.packet.Chat;
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
@@ -107,10 +106,10 @@ public class BungeePacketBuilder extends PacketBuilder {
public Object build(PacketPlayOutScoreboardTeam packet, ProtocolVersion clientVersion) {
int color = 0;
if (clientVersion.getMinorVersion() >= 13) {
color = (packet.getColor() != null ? packet.getColor() : EnumChatFormat.lastColorsOf(packet.getPlayerPrefix())).getNetworkId();
color = (packet.getColor() != null ? packet.getColor() : EnumChatFormat.lastColorsOf(packet.getPlayerPrefix())).ordinal();
}
return new Team(packet.getName(), (byte)packet.getMethod(), jsonOrCut(packet.getName(), clientVersion, 16), jsonOrCut(packet.getPlayerPrefix(), clientVersion, 16), jsonOrCut(packet.getPlayerSuffix(), clientVersion, 16),
packet.getNametagVisibility(), packet.getCollisionRule(), color, (byte)packet.getOptions(), packet.getPlayers().toArray(new String[0]));
packet.getNameTagVisibility(), packet.getCollisionRule(), color, (byte)packet.getOptions(), packet.getPlayers().toArray(new String[0]));
}
@Override
@@ -124,20 +123,12 @@ public class BungeePacketBuilder extends PacketBuilder {
}
@Override
public PacketPlayOutScoreboardObjective readObjective(Object bungeePacket, ProtocolVersion clientVersion) {
ScoreboardObjective packet = (ScoreboardObjective) bungeePacket;
String title;
if (clientVersion.getMinorVersion() >= 13) {
title = packet.getValue() == null ? null : IChatBaseComponent.deserialize(packet.getValue()).toLegacyText();
} else {
title = packet.getValue();
}
EnumScoreboardHealthDisplay renderType = (packet.getType() == null ? null : EnumScoreboardHealthDisplay.valueOf(packet.getType().toString().toUpperCase()));
return new PacketPlayOutScoreboardObjective(packet.getAction(), packet.getName(), title, renderType);
public PacketPlayOutScoreboardObjective readObjective(Object bungeePacket) {
return new PacketPlayOutScoreboardObjective(((ScoreboardObjective) bungeePacket).getAction(), ((ScoreboardObjective) bungeePacket).getName(), null, null);
}
@Override
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object bungeePacket, ProtocolVersion clientVersion){
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object bungeePacket){
return new PacketPlayOutScoreboardDisplayObjective(((ScoreboardDisplay) bungeePacket).getPosition(), ((ScoreboardDisplay) bungeePacket).getName());
}
}
@@ -28,7 +28,7 @@ import net.md_5.bungee.protocol.packet.Team;
public class BungeePipelineInjector extends PipelineInjector {
//packets that must be deserialized and bungeecord does not do it automatically
//packets that must be deserialized and BungeeCord does not do it automatically
private final Map<Class<? extends DefinedPacket>, Supplier<DefinedPacket>> extraPackets = new HashMap<>();
/**
@@ -48,7 +48,7 @@ public class BungeePipelineInjector extends PipelineInjector {
public class BungeeChannelDuplexHandler extends ChannelDuplexHandler {
//injected player
private TabPlayer player;
private final TabPlayer player;
/**
* Constructs new instance with given player
@@ -61,8 +61,8 @@ public class BungeePipelineInjector extends PipelineInjector {
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
long time = System.nanoTime();
Object modifiedPacket = packet instanceof ByteBuf && bytebufDeserialization ? deserialize((ByteBuf) packet) : packet;
TAB.getInstance().getCPUManager().addTime("Packet deserializing", TabConstants.CpuUsageCategory.BYTEBUF, System.nanoTime()-time);
Object modifiedPacket = packet instanceof ByteBuf && byteBufDeserialization ? deserialize((ByteBuf) packet) : packet;
TAB.getInstance().getCPUManager().addTime("Packet deserializing", TabConstants.CpuUsageCategory.BYTE_BUF, System.nanoTime()-time);
try {
switch(modifiedPacket.getClass().getSimpleName()) {
case "PlayerListItem":
@@ -74,9 +74,7 @@ public class BungeePipelineInjector extends PipelineInjector {
}
break;
case "ScoreboardDisplay":
if (TAB.getInstance().getFeatureManager().onDisplayObjective(player, modifiedPacket)) {
return;
}
TAB.getInstance().getFeatureManager().onDisplayObjective(player, modifiedPacket);
break;
case "ScoreboardObjective":
TAB.getInstance().getFeatureManager().onObjective(player, modifiedPacket);
@@ -114,7 +112,7 @@ public class BungeePipelineInjector extends PipelineInjector {
col.remove(getName(p));
}
}
RedisBungeeSupport redis = (RedisBungeeSupport) TAB.getInstance().getFeatureManager().getFeature("redisbungee");
RedisBungeeSupport redis = (RedisBungeeSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
if (redis != null) {
for (RedisPlayer p : redis.getRedisPlayers().values()) {
if (col.contains(p.getName()) && !packet.getName().equals(p.getTeamName())) {
@@ -124,11 +122,11 @@ public class BungeePipelineInjector extends PipelineInjector {
}
}
packet.setPlayers(col.toArray(new String[0]));
TAB.getInstance().getCPUManager().addTime("Nametags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
}
private String getName(TabPlayer p) {
NickCompatibility nick = (NickCompatibility) TAB.getInstance().getFeatureManager().getFeature("nick");
NickCompatibility nick = (NickCompatibility) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.NICK_COMPATIBILITY);
if (nick != null) {
return nick.getNickname(p);
}
@@ -136,10 +134,10 @@ public class BungeePipelineInjector extends PipelineInjector {
}
/**
* Deserializes bytebuf in case it is one of the tracked packets coming from backend server and returns it.
* Deserializes byte buf in case it is one of the tracked packets coming from backend server and returns it.
* If the packet is not one of them, returns input
* @param buf - bytebuf to deserialize
* @return deserialized packet or input bytebuf if packet is not tracked
* @param buf - byte buf to deserialize
* @return deserialized packet or input byte buf if packet is not tracked
*/
private Object deserialize(ByteBuf buf) {
int marker = buf.readerIndex();
@@ -11,7 +11,7 @@ import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* Bungeecord registry to register bungeecord-only placeholders
* BungeeCord registry to register BungeeCord-only placeholders
*/
public class BungeePlaceholderRegistry extends ProxyPlaceholderRegistry {
@@ -3,6 +3,7 @@ package me.neznamy.tab.platforms.bungeecord;
import java.io.File;
import java.util.List;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.protocol.PacketBuilder;
@@ -10,6 +11,7 @@ import me.neznamy.tab.platforms.bungeecord.event.TabLoadEvent;
import me.neznamy.tab.platforms.bungeecord.event.TabPlayerLoadEvent;
import me.neznamy.tab.platforms.bungeecord.redisbungee.RedisBungeeSupport;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.PluginMessageHandler;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.PermissionPlugin;
@@ -22,7 +24,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
/**
* Bungeecord implementation of Platform
* BungeeCord implementation of Platform
*/
public class BungeePlatform extends ProxyPlatform {
@@ -56,14 +58,19 @@ public class BungeePlatform extends ProxyPlatform {
@Override
public void loadFeatures() {
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection()) tab.getFeatureManager().registerFeature("injection", new BungeePipelineInjector());
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BungeePipelineInjector());
new BungeePlaceholderRegistry().registerPlaceholders(tab.getPlaceholderManager());
super.loadFeatures();
if (ProxyServer.getInstance().getPluginManager().getPlugin("RedisBungee") != null) {
tab.getFeatureManager().registerFeature("redisbungee", new RedisBungeeSupport(plugin));
if (RedisBungeeAPI.getRedisBungeeApi() != null) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, new RedisBungeeSupport(plugin));
} else {
TAB.getInstance().getErrorManager().criticalError("RedisBungee plugin was detected, but it returned null API instance. Disabling hook.", null);
}
}
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
tab.addPlayer(new BungeeTabPlayer(p, plm));
tab.addPlayer(new BungeeTabPlayer(p));
}
}
@@ -33,14 +33,15 @@ public class BungeePluginMessageHandler extends PluginMessageHandler implements
*/
@EventHandler
public void on(PluginMessageEvent event){
if (!event.getTag().equalsIgnoreCase(channelName)) return;
if (!event.getTag().equals(channelName)) return;
if (event.getReceiver() instanceof ProxiedPlayer) {
long time = System.nanoTime();
BungeeTabPlayer receiver = (BungeeTabPlayer) TAB.getInstance().getPlayer(((ProxiedPlayer) event.getReceiver()).getUniqueId());
if (receiver == null) return;
onPluginMessage(receiver, ByteStreams.newDataInput(event.getData()));
event.setCancelled(true);
TAB.getInstance().getCPUManager().addTime("Plugin message handling", TabConstants.CpuUsageCategory.PLUGIN_MESSAGE, System.nanoTime()-time);
TAB.getInstance().getCPUManager().runMeasuredTask("processing PluginMessageEvent", "Plugin message handling",
TabConstants.CpuUsageCategory.PLUGIN_MESSAGE, () -> {
BungeeTabPlayer receiver = (BungeeTabPlayer) TAB.getInstance().getPlayer(((ProxiedPlayer) event.getReceiver()).getUniqueId());
if (receiver == null) return;
onPluginMessage(receiver, ByteStreams.newDataInput(event.getData()));
});
}
}
@@ -48,6 +49,6 @@ public class BungeePluginMessageHandler extends PluginMessageHandler implements
public void sendPluginMessage(TabPlayer player, byte[] message) {
if (((ProxiedPlayer) player.getPlayer()).getServer() == null) return; //not connected to any server yet
((ProxiedPlayer) player.getPlayer()).getServer().sendData(channelName, message);
TAB.getInstance().getCPUManager().packetSent("Plugin Message");
TAB.getInstance().getCPUManager().packetSent("Plugin Message (" + new String(message) + ")");
}
}
@@ -6,7 +6,6 @@ import java.lang.reflect.Method;
import de.myzelyam.api.vanish.BungeeVanishAPI;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PluginMessageHandler;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.api.ProxyServer;
@@ -27,9 +26,6 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
private static Object directionData;
private static Method getId;
/**
* Initializes fields to get player channel later
*/
static {
try {
Field f = Protocol.class.getDeclaredField("TO_CLIENT");
@@ -46,10 +42,10 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
/**
* Constructs new instance for given player
* @param p - bungeecord player
* @param p - BungeeCord player
*/
public BungeeTabPlayer(ProxiedPlayer p, PluginMessageHandler plm) {
super(plm, p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", "N/A");
public BungeeTabPlayer(ProxiedPlayer p) {
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-");
try {
channel = ((ChannelWrapper) wrapperField.get(getPlayer().getPendingConnection())).getHandle();
} catch (IllegalAccessException e) {
Loaded 100 of 242 files, more files were not shown because too many files have changed in this diff. Show more