Compare commits

..
280 Commits
Author SHA1 Message Date
NEZNAMY 09dc8cd437 version bump 2021-06-16 13:52:27 +02:00
NEZNAMY c613ba401b remove unnecessary this. 2021-06-16 13:03:52 +02:00
NEZNAMY 866df0b5cf remove duplicate content 2021-06-16 11:12:43 +02:00
NEZNAMY 8f2b56ba05 2.9.0 2021-06-16 10:09:20 +02:00
NEZNAMY 2049885f11 more accurate error message 2021-06-16 09:56:15 +02:00
NEZNAMY 4caef337d5 ANOTHER BOSSBAR API BUG IN ANOTHER PLATFORM HAHA LOL 2021-06-15 23:37:02 +02:00
NEZNAMY a644792bf6 simplify 2021-06-15 22:50:16 +02:00
NEZNAMY 54c7f8fc6a drop the rest of title packet 2021-06-15 22:49:23 +02:00
NEZNAMY 2e59b87bbe use getters instead of fields 2021-06-15 22:46:42 +02:00
NEZNAMY f89242ad8f add and use magic code Boolean getters 2021-06-15 22:40:02 +02:00
NEZNAMY 845903788c remove unused code 2021-06-15 22:39:32 +02:00
NEZNAMY 53a9047c20 fix conflict between hidden-by-default and remember-toggle-choice 2021-06-15 22:36:55 +02:00
NEZNAMY a2b74bbc30 correctly display used color codes in parse error messages 2021-06-15 22:36:31 +02:00
NEZNAMY 50ef93cc45 fix stack overflow and stretch with relational placeholders 2021-06-15 22:36:00 +02:00
NEZNAMY e2a5da9c14 deserialize other scoreboard packets as well 2021-06-15 22:35:24 +02:00
NEZNAMY bf31116d0d split method 2021-06-15 22:34:42 +02:00
NEZNAMY d79fa8c4f3 drop title packet 2021-06-15 22:34:31 +02:00
NEZNAMY 87e0a7bea3 remove unused fields 2021-06-15 22:33:33 +02:00
NEZNAMY b34cabd55c optimize check 2021-06-15 22:32:50 +02:00
NEZNAMY 704bd36c77 do not try to use rgb on <1.16 servers 2021-06-14 09:35:23 +02:00
NEZNAMY 5c2c4d0f4b improved error message 2021-06-14 08:43:36 +02:00
NEZNAMY 80cca5cb34 fix #329 2021-06-14 08:22:37 +02:00
NEZNAMY dcd603f2f5 null check to prevent errors 2021-06-13 22:10:30 +02:00
NEZNAMY 1bee3b49bf sending bossbar using bukkit api 2021-06-13 20:46:16 +02:00
NEZNAMY 11bcccb8f9 fix reload from previous commit 2021-06-13 20:24:39 +02:00
NEZNAMY 7425493706 initialization cleanup 2021-06-13 19:09:34 +02:00
NEZNAMY dca0cfd424 removed old value from config 2021-06-13 17:09:03 +02:00
NEZNAMY 21314def89 fix #328 2021-06-13 15:57:06 +02:00
NEZNAMY 90e544d279 option to take groups from backend servers 2021-06-13 11:54:03 +02:00
NEZNAMY 89e21558a1 2.8.11-pre19 2021-06-12 23:23:15 +02:00
NEZNAMY b28cc51457 spigot one dot god damn seventeen 2021-06-12 23:19:09 +02:00
NEZNAMY 2d0a203b5f increased initial delay 2021-06-12 14:28:44 +02:00
NEZNAMY 2948666c1d 2.8.11-pre18 2021-06-11 19:14:47 +02:00
NEZNAMY b2a2659546 added little comment 2021-06-11 17:14:03 +02:00
NEZNAMY e2eb7102c9 removed unnecessary 'throws' from methods 2021-06-10 21:22:15 +02:00
NEZNAMY 56301e653b splitting method into multiple 2021-06-10 21:13:40 +02:00
NEZNAMY 1d825e08d2 using correct uuid all the time 2021-06-10 19:38:46 +02:00
NEZNAMY c81a27709c 1.17 bungeecord 2021-06-10 17:22:58 +02:00
NEZNAMY 2b143172f8 spacing 2021-06-10 17:19:55 +02:00
NEZNAMY 84330ec6eb Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-06-10 17:19:25 +02:00
NEZNAMY 0bd703de7f Merge pull request #325 from Camotoy/viaversion4.0.0
Update to ViaVersion 4.0.0
2021-06-10 17:17:55 +02:00
Camotoy e6a55b7d5c Change packet ID if using 1.17 2021-06-10 11:13:45 -04:00
Camotoy b6ec7631d2 Update to ViaVersion 4.0.0
Tested on 1.17 and the bossbar showed up with no error in console.
2021-06-10 10:53:36 -04:00
NEZNAMY 5abbd00d63 fixed unloading when entering disabled world 2021-06-10 11:22:07 +02:00
NEZNAMY 515c94976e removed internal methods from TabPlayer API interface 2021-06-09 21:06:01 +02:00
NEZNAMY 912adad8f6 player 2021-06-09 20:08:36 +02:00
NEZNAMY 70fdd193a3 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-06-09 19:43:08 +02:00
NEZNAMY 9d83e968fc Merge pull request #323 from Score2/bukkit-permission
Support to obtain bukkit permissions from bungee.
2021-06-09 19:42:35 +02:00
83669 04329da133 resolve. 2021-06-10 01:41:32 +08:00
83669 19ccfbb1a6 Merge branch 'master' of https://github.com/NEZNAMY/TAB into bukkit-permission
 Conflicts:
	velocity-1.1.0/src/main/resources/velocityconfig.yml
2021-06-10 01:29:02 +08:00
83669 c53ab09abc Support to obtain bukkit permissions from bungee. 2021-06-10 01:24:23 +08:00
NEZNAMY a4d8daac1d fixed wrong logic causing name formatting to not work 2021-06-09 16:12:33 +02:00
NEZNAMY 99d566d521 wrong action order 2021-06-09 16:05:08 +02:00
NEZNAMY aa37f73161 fixed wrong config option name 2021-06-09 15:54:12 +02:00
NEZNAMY 087518378d pom.xml cleanup 2021-06-09 15:39:57 +02:00
NEZNAMY 913d27929b shortcut and no longer requiring guava 2021-06-09 15:24:04 +02:00
NEZNAMY 530d39bec5 added property name into exception message 2021-06-09 10:47:42 +02:00
NEZNAMY cf70dc3845 fixed priorities (caused <= and >= to not work) 2021-06-08 23:17:31 +02:00
NEZNAMY d1421fee3f new colors 2021-06-08 21:58:18 +02:00
NEZNAMY 3ec490b36e only the the API now 2021-06-08 09:58:46 +02:00
NEZNAMY ee42685179 Added API to disable team handling of a player 2021-06-07 13:31:44 +02:00
NEZNAMY e02d687771 PingSpoof and SpectatorFix now properly load/unload 2021-06-06 14:55:47 +02:00
NEZNAMY dddb592253 update to default config comments 2021-06-05 20:22:20 +02:00
NEZNAMY d0ad88d317 fixed potential security issue when player is not loaded due to an error 2021-06-05 20:20:02 +02:00
NEZNAMY 3d3ce0151f added gamemode getter to TabPlayer 2021-06-05 19:56:37 +02:00
NEZNAMY f017b0e562 %canseeonline% now uses vanish detection from previous commits instead of PV API on bungeecord 2021-06-05 19:47:03 +02:00
NEZNAMY 4ba2bb669b added back 1.5 and 1.6 support 2021-06-05 16:26:32 +02:00
NEZNAMY 9fc2424736 Create FUNDING.yml 2021-06-03 16:55:26 +02:00
NEZNAMY 980c01ea70 this never happened 2021-06-01 13:25:01 +02:00
NEZNAMY 758edcaf5c fixed NPE in API usage 2021-05-30 20:48:49 +02:00
NEZNAMY 950f825e31 added unnecessary softdependency due to md5's troll console message 2021-05-30 20:47:48 +02:00
NEZNAMY 0a985f5c78 using method from component to save color codes due to limits 2021-05-28 11:43:17 +02:00
NEZNAMY 58353f4e6d wrong variable 2021-05-28 11:41:22 +02:00
NEZNAMY c3d2b5c4b0 fixed error in initialization 2021-05-28 11:19:45 +02:00
NEZNAMY c2764b4b8a removed redundant ; 2021-05-28 11:19:31 +02:00
NEZNAMY 9bc8ad0d23 typo 2021-05-28 09:47:02 +02:00
NEZNAMY 488c3540a8 using more reliable check 2021-05-27 19:58:17 +02:00
NEZNAMY 1d03950a64 removed technically useless check 2021-05-27 19:51:21 +02:00
NEZNAMY e24ea7df2e added null check, continuation of previous commit 2021-05-27 18:06:09 +02:00
NEZNAMY fe5e3d6405 better compatibility with forks that use "Unknown" server version
by using a minor version check which is more reliable
2021-05-27 16:20:08 +02:00
NEZNAMY db8584e40b correctly translating colors 2021-05-27 16:18:43 +02:00
NEZNAMY 2e4db38d1a properly compensating for the bug on all horse types 2021-05-25 14:31:44 +02:00
NEZNAMY f82470c1b1 2.8.11-pre17 2021-05-24 22:37:10 +02:00
NEZNAMY 650cc93fc0 fixed placeholders not refreshing when updating properties with API 2021-05-24 22:35:02 +02:00
NEZNAMY e61070307a fixed rare NPE 2021-05-23 23:27:56 +02:00
NEZNAMY 2fa46867e5 moved repeating task to separate method 2021-05-23 18:51:39 +02:00
NEZNAMY 636b2e760a fixed wrong check 2021-05-23 18:46:10 +02:00
NEZNAMY 8cecf46310 NPE fix 2021-05-23 00:38:16 +02:00
NEZNAMY a7bcda7eba fixed NPEs when running commands from console 2021-05-22 20:48:09 +02:00
NEZNAMY a610dd2c70 cleanup 2021-05-22 16:49:30 +02:00
NEZNAMY df98c77a48 java 8 shortcut 2021-05-22 16:48:01 +02:00
NEZNAMY 331dc33c86 removed redundant method 2021-05-22 16:46:56 +02:00
NEZNAMY 8493b37171 recoded feature to fix issues, worked 2021-05-22 16:41:56 +02:00
NEZNAMY 2449c46165 using default locale
https://support-system.xyz/redirect?link=https://cdn.discordapp.com/attachments/690574378454483034/845273706287988807/ticket-844852135032127489.html
2021-05-21 14:16:55 +02:00
NEZNAMY a642c2d344 moved code to new method to avoid duplication 2021-05-20 16:59:48 +02:00
NEZNAMY 88c7b988e7 not displaying the error message anymore 2021-05-19 20:40:07 +02:00
NEZNAMY 36651260c6 added debug messages 2021-05-19 18:10:54 +02:00
NEZNAMY 6c0072dc54 using new method (also supports essentials vanish now) 2021-05-18 21:12:54 +02:00
NEZNAMY e38f5a77c0 added support for bukkit vanish plugins into global playerlist via bridge 2021-05-18 21:11:41 +02:00
NEZNAMY 6443926a79 added a reminder since even I forgot 2021-05-18 19:55:36 +02:00
NEZNAMY 489deb9ee6 2.8.11-pre16 2021-05-16 22:06:22 +02:00
NEZNAMY a2039110e0 improved compatibility with vanish plugins 2021-05-16 22:01:25 +02:00
NEZNAMY f1666b64ee moved shared module to top to avoid compilation error on first compilation 2021-05-16 21:11:45 +02:00
NEZNAMY d7e419c88b added getters to bossbar interface 2021-05-16 21:10:01 +02:00
NEZNAMY de8f8053be recalculating more often, seems to have fixed the issue 2021-05-16 20:56:15 +02:00
NEZNAMY 7aadef5c21 resending packet with a delay when anti-override is disabled 2021-05-16 20:55:26 +02:00
NEZNAMY f8384dfbdf removed character ID from widths for lower file size & counting them internally while loading 2021-05-16 20:52:38 +02:00
NEZNAMY b91cbf827c removed redundant check 2021-05-16 20:51:46 +02:00
NEZNAMY 096619d9f7 moved teleport event back to packets to avoid various issues 2021-05-16 13:00:28 +02:00
NEZNAMY 7b8006406c should fix rare issue caused by multi-thread access 2021-05-16 12:58:48 +02:00
NEZNAMY 90483d549a removed width command & loading since the file is now complete 2021-05-15 21:26:16 +02:00
NEZNAMY 5473055c1c found a way to measure & added arabic characters 2021-05-15 21:23:41 +02:00
NEZNAMY f3b082b8e1 removed unnecessary cast 2021-05-15 19:57:04 +02:00
NEZNAMY 52fe99a080 fixed stack overflow on sync placeholders 2021-05-15 13:33:57 +02:00
NEZNAMY 68b1e941d1 correctly formatting tablist name in packets sent on world change 2021-05-13 18:51:15 +02:00
NEZNAMY 109fce9b1a updated line number reference 2021-05-13 18:50:45 +02:00
NEZNAMY 014d73dd3f ignoring info packet on <1.8 clients so nametag feature can display prefixes 2021-05-13 18:50:08 +02:00
NEZNAMY ad82744e91 added configuration simplification console message 2021-05-09 16:12:41 +02:00
NEZNAMY 638f66ade4 added catch for ArrayIndexOutOfBoundsException 2021-05-09 16:11:22 +02:00
NEZNAMY c190efa7c4 removed config conversion
users had enough time to update to 2.8.10 which converts everything, only keeping 2.8.10 ->2.9.0 changes
2021-05-09 12:35:17 +02:00
NEZNAMY b071eb3366 added purpur afk api support for %afk% 2021-05-08 18:03:57 +02:00
NEZNAMY a7c7a86a90 removed feature interface not used by the main plugin 2021-05-08 17:18:57 +02:00
NEZNAMY 0c21519cf6 listening to respawn event directly in features instead of globally and being unused for most servers 2021-05-08 00:45:33 +02:00
NEZNAMY d7f805b26e goodbye 2021-05-07 21:14:11 +02:00
NEZNAMY 4f702feab3 random feature request in my private messages 2021-05-07 20:24:52 +02:00
NEZNAMY 46175ceb1f fixed error on that platform made by a group of friendly and amazing people 2021-05-07 20:24:07 +02:00
NEZNAMY c206860736 removed redundant code 2021-05-02 22:20:09 +02:00
NEZNAMY b97c222490 remove modify-npc-names config option
all NPC plugins offer configuration of NPC names, modification can be performed by competent server owners there instead
2021-05-02 17:32:38 +02:00
NEZNAMY 53ad5e3cec added debug messages 2021-05-02 13:15:04 +02:00
NEZNAMY e67ce8dbf4 possible issue fix 2021-05-01 23:39:58 +02:00
NEZNAMY ac661be5c4 manually adding yes/no keys to replacements to compensate for snakeyaml conversion 2021-05-01 22:23:26 +02:00
NEZNAMY ab4e386491 small visual code update, functionality remains same 2021-05-01 22:11:32 +02:00
NEZNAMY aa125b535e Update README.md 2021-05-01 00:49:46 +02:00
NEZNAMY 29df3bb5e2 version bump 2021-04-30 15:36:14 +02:00
NEZNAMY 80ee30a532 map must be linked, found a way to do both synchronized and linked 2021-04-28 20:47:08 +02:00
NEZNAMY a20b1bcd89 added hidden config option to disable pipeline injection 2021-04-28 10:33:39 +02:00
NEZNAMY 4269abd2ca 2.8.11-pre15 2021-04-27 20:51:35 +02:00
NEZNAMY a73397065c added fix for rare NPE due to multi-thread access 2021-04-27 20:47:58 +02:00
NEZNAMY 5805a6c5a3 fixed rare concurrent modification 2021-04-27 17:43:30 +02:00
NEZNAMY e813077c92 check is no longer in constructor, must be added elsewhere 2021-04-27 17:38:27 +02:00
NEZNAMY b1ae68e8c2 not spamming class initializations and getting field values directly 2021-04-26 14:45:52 +02:00
NEZNAMY b6162b2d4a not refreshing header/footer on world switch if values did not change 2021-04-26 14:07:45 +02:00
NEZNAMY 0da7854f7f using concurrent hashmap to not need to copy value collection each time it's iterated 2021-04-26 14:00:41 +02:00
NEZNAMY a073747e66 added comment 2021-04-26 13:25:00 +02:00
NEZNAMY 0975e31371 better instance check 2021-04-26 13:23:25 +02:00
NEZNAMY a1231f1708 anti-override was removed already 2021-04-26 09:14:41 +02:00
NEZNAMY 3581da0fe7 only running toString once instead of every loop 2021-04-26 08:43:51 +02:00
NEZNAMY 531810e633 remake using switch 2021-04-25 13:53:16 +02:00
NEZNAMY 36a1db78e2 separate method to reduce complexity 2021-04-25 13:50:50 +02:00
NEZNAMY 0e58009ced starting tasks in separate methods 2021-04-25 13:48:18 +02:00
NEZNAMY ecdcc6894b fixed wrong cpu calculation 2021-04-25 00:16:19 +02:00
NEZNAMY 677a2f9396 reduced memory allocations 2021-04-25 00:14:53 +02:00
NEZNAMY b14f30d4d1 using isInstance instead of equals for better performance 2021-04-23 20:30:17 +02:00
NEZNAMY 4396df55ae updated dependency 2021-04-22 22:07:00 +02:00
NEZNAMY de6ce93a31 partially reverted pre6 recode + fixed disabled worlds not working
also deleted SneakEventListener class and replaced it with direct event lisener
2021-04-22 16:31:01 +02:00
NEZNAMY b449b7cdfc fixed errors on <1.16 2021-04-22 14:52:15 +02:00
NEZNAMY de4412fcff 2.8.11-pre14 2021-04-21 18:02:09 +02:00
NEZNAMY 8e9b34125d added placeholder value printing into team name note in debug 2021-04-21 17:45:57 +02:00
NEZNAMY 024b3175fa updated link 2021-04-20 19:48:33 +02:00
NEZNAMY 9fadc6df3b removed links 2021-04-20 19:47:48 +02:00
NEZNAMY f51f8f0f2d updated info 2021-04-20 15:51:48 +02:00
NEZNAMY d805df4887 no longer required 2021-04-20 15:50:41 +02:00
NEZNAMY d161c6c074 no need for reflection since jar is manual
also fixed title packet
2021-04-20 15:40:24 +02:00
NEZNAMY 6603c9e682 removed redundant file 2021-04-20 15:23:58 +02:00
NEZNAMY 157360c26c added dependency exclusions 2021-04-20 14:11:03 +02:00
NEZNAMY e16199b360 defined encoding to avoid warn in compilation 2021-04-20 11:58:13 +02:00
NEZNAMY df42424970 more cleanup 2021-04-20 11:42:30 +02:00
NEZNAMY 22ce6ce75e more cleanup 2021-04-20 11:36:55 +02:00
NEZNAMY e8dde142d2 another savestate before I break something 2021-04-20 11:25:05 +02:00
NEZNAMY 44fe272ce5 making another savestate of a working project 2021-04-20 11:23:41 +02:00
NEZNAMY 23a8f32910 pom.xml cleanup and resource fix 2021-04-20 11:12:04 +02:00
NEZNAMY d523c0408e Merge pull request #300 from A248/fix-modularization
Fix modularization
2021-04-20 01:51:37 +02:00
A248 0c2edba316 Add all resources and delete target folders 2021-04-19 19:49:47 -04:00
A248 7cfc643522 Add more to .gitignore 2021-04-19 19:42:32 -04:00
A248 98b446efd2 Shade all the modules into single jar + Fix filename of velocity 2
Changed velocity/2.0.0/libs/Velocity 2.0.0jar to Velocity-2.0.0.jar
2021-04-19 19:40:24 -04:00
NEZNAMY bbf9aaed64 initial modulatization 2021-04-20 01:11:57 +02:00
NEZNAMY 13d36457af repackage 2021-04-19 15:12:39 +02:00
NEZNAMY 4f850b0f2e it's shutdown 2021-04-18 21:45:42 +02:00
NEZNAMY 7118e0d9d3 fixed magic code error in gradients due to recent gradient apply changes 2021-04-18 20:59:35 +02:00
NEZNAMY 900edef407 fixed NPE when player is not loaded 2021-04-18 15:13:33 +02:00
NEZNAMY a7e4b99ce6 2.8.11-pre13 2021-04-18 12:27:12 +02:00
NEZNAMY 0463667270 Update README.md 2021-04-16 21:03:23 +02:00
NEZNAMY d179bc20ae now with link 2021-04-16 20:07:31 +02:00
NEZNAMY bc658a9223 Update README.md 2021-04-16 20:03:30 +02:00
NEZNAMY e32e3baf3a added anti-smartass message 2021-04-16 14:52:57 +02:00
NEZNAMY 4436f4cf42 new sorting type 2021-04-15 01:28:15 +02:00
NEZNAMY 1dc7b40ee9 found a workaround, works fine 2021-04-15 00:36:34 +02:00
NEZNAMY fb2221d281 removed unnecessary local variables 2021-04-15 00:36:09 +02:00
NEZNAMY b5a295970b correctly moving to the bottom even when more than 9 groups are defined 2021-04-15 00:02:38 +02:00
NEZNAMY 485a453b27 added misconfiguration check 2021-04-14 19:36:39 +02:00
NEZNAMY 03d9ba9579 removed debug message 2021-04-14 19:29:08 +02:00
NEZNAMY ead7adea31 increased delay because requested 2021-04-14 19:28:17 +02:00
NEZNAMY 9793fdfc06 added debug messages 2021-04-14 19:27:50 +02:00
NEZNAMY 60b73e5c0d fixed NPE when modifier is null 2021-04-13 17:37:01 +02:00
NEZNAMY ed374c83d1 moving to API methods where possible 2021-04-13 15:24:56 +02:00
NEZNAMY f53828e01a no longer needed after updating setBossbarVisible method 2021-04-13 14:04:55 +02:00
NEZNAMY 91ab50fa6a removed unused method 2021-04-13 13:22:24 +02:00
NEZNAMY bee0d9d086 fixed issue from previous commits 2021-04-13 01:36:15 +02:00
NEZNAMY b38f09f118 moved symbol into constructor 2021-04-13 01:17:30 +02:00
NEZNAMY 65f57e8bf1 wrote the method from scratch for better performance & memory 2021-04-13 01:13:02 +02:00
NEZNAMY e424e42e05 matching change made to TextColor constructor 2021-04-13 00:48:11 +02:00
NEZNAMY a22cd5338d initializing value only once instead of every time it's used 2021-04-13 00:35:54 +02:00
NEZNAMY f921737d1c converting color object instead of calling fromString(.toString()) 2021-04-13 00:28:37 +02:00
NEZNAMY 2f760afc70 the constructor expects # 2021-04-13 00:06:59 +02:00
NEZNAMY 4ce56433f9 fowarding StringBuilder 2021-04-12 16:57:22 +02:00
NEZNAMY f8ace5fbb5 merged ifs 2021-04-12 16:51:26 +02:00
NEZNAMY f89778e969 not initializing map when unused 2021-04-12 16:35:56 +02:00
NEZNAMY 99f74d3824 using String.valueOf for less allocations 2021-04-12 16:28:13 +02:00
NEZNAMY dec9c42edc shorter alternative 2021-04-12 16:05:06 +02:00
NEZNAMY 34365c13cb calling int method directly if possible for better performance & memory 2021-04-12 15:20:39 +02:00
NEZNAMY 8a4c780fb1 parsing gradients only once instead of every refresh 2021-04-12 14:39:53 +02:00
NEZNAMY bed49408da only checking plugin presence on startup 2021-04-12 13:12:24 +02:00
NEZNAMY 5dd400bdc5 only initializing once 2021-04-12 13:01:04 +02:00
NEZNAMY 07d5a03a95 using AtomicInteger to avoid internal Integer.valueOf(int) call 2021-04-12 12:44:19 +02:00
NEZNAMY d53e7665d7 only initializing hexcodes once instead of every time 2021-04-12 12:17:33 +02:00
NEZNAMY 4021d7cbac fix from previous commit 2021-04-12 12:16:44 +02:00
NEZNAMY 2634ca5f28 not initializing list if not needed 2021-04-11 23:24:02 +02:00
NEZNAMY aa887fe27b 2.8.11-pre12 2021-04-10 21:39:40 +02:00
NEZNAMY c9077e1254 removed header/footer anti-override entirely
it was rather useless and very resource intensive
2021-04-10 20:22:42 +02:00
NEZNAMY fcf5636d9a small optimization with StringBuilder and String.format 2021-04-10 20:16:33 +02:00
NEZNAMY 1c4713ef1a not initializing maps when not needed 2021-04-10 18:13:15 +02:00
NEZNAMY d48da82f55 removed unnecessary local variable 2021-04-10 18:12:54 +02:00
NEZNAMY 7128ab26d8 using AtomicLong to prevent Long.valueOf(long) spamming allocations 2021-04-10 15:31:05 +02:00
NEZNAMY 58155041d1 loading placeholder usage map only once (per hard refresh) instead of every loop 2021-04-10 15:05:49 +02:00
NEZNAMY e96453eac9 optimized check 2021-04-09 22:22:37 +02:00
NEZNAMY a1d98c54ad why is it /btab not /vtab? now it's both 2021-04-09 21:35:15 +02:00
NEZNAMY fd423481cb small memory optimization using StringBuilder 2021-04-09 20:14:00 +02:00
NEZNAMY 48da7d0c2e added cpu measuring of some methods 2021-04-09 17:49:03 +02:00
NEZNAMY 949577fb07 properly using color field to prevent white text in next line 2021-04-09 17:48:09 +02:00
NEZNAMY dc876efff8 changed placeholder display to top 5 2021-04-09 16:35:55 +02:00
NEZNAMY c39b650e9b found a way to keep code in the same thread without double cpu measuring 2021-04-09 15:56:22 +02:00
NEZNAMY 12c1cb871d various minor memory optimizations 2021-04-09 15:39:57 +02:00
NEZNAMY c75e86812d created Component convert methods for faster code & less memory allocations 2021-04-09 13:56:25 +02:00
NEZNAMY cc1fffbe8b missing changes from previous commit 2021-04-09 13:27:19 +02:00
NEZNAMY e6313a5e52 showing correct threshold 2021-04-09 01:25:08 +02:00
NEZNAMY 2a278dcff9 added custom line properties support to commands 2021-04-09 01:16:47 +02:00
NEZNAMY 8539529061 updated line numbers 2021-04-09 00:00:16 +02:00
NEZNAMY 5a4b7d93c0 reduced display threshold 2021-04-08 16:01:07 +02:00
NEZNAMY 5e459effc0 fixed &# RGB format issue from previous commits 2021-04-08 14:24:17 +02:00
NEZNAMY 5f0cf2ecb5 Update TAB.java 2021-04-08 01:04:40 +02:00
NEZNAMY 44c133a68c added requested method 2021-04-07 23:19:39 +02:00
NEZNAMY f842f15cd4 wrong color symbol 2021-04-07 11:17:47 +02:00
NEZNAMY 04c78a37d7 various random small memory and cpu improvements 2021-04-06 21:33:01 +02:00
NEZNAMY cba4dbdf84 2.8.11-pre11 2021-04-05 20:40:55 +02:00
NEZNAMY 2fbb162bbf fixed NPE when joining into disabled world with aligned tabsuffix enabled 2021-04-05 20:36:16 +02:00
NEZNAMY a1be962997 disabled by default 2021-04-05 20:34:04 +02:00
NEZNAMY a3c3f5d990 reverted broken commit 2021-04-05 19:20:55 +02:00
NEZNAMY c785d4fdca fixed #282 2021-04-05 16:21:33 +02:00
NEZNAMY 6b78707bd8 not overriding other plugins with delayed scoreboard 2021-04-05 00:23:57 +02:00
NEZNAMY a1565ed481 2.8.11-pre10 2021-04-04 15:09:19 +02:00
NEZNAMY ae056b312f put 0.5 for characters that wide and added some missing values 2021-04-04 13:39:52 +02:00
NEZNAMY b20c1158b0 allow decimal values (0.5) 2021-04-04 13:39:26 +02:00
NEZNAMY 210f567b74 added debug messages to loading widths 2021-04-04 13:12:09 +02:00
NEZNAMY 82b91c7bd9 added empty lines for missing chars 2021-04-04 12:55:21 +02:00
NEZNAMY 5168a99c3b Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-04-04 12:49:55 +02:00
NEZNAMY e437917841 added permission required to use tabcomplete #290 2021-04-04 12:49:47 +02:00
MrGraycat 6543440926 added 55001-60000
added 55001-60000
2021-04-04 09:03:14 +02:00
NEZNAMY 30c53c6048 fixed disabling in worlds not really working 2021-04-04 01:32:42 +02:00
NEZNAMY 87412eb4ff another attempt to avoid concurrent modification 2021-04-04 00:24:43 +02:00
NEZNAMY f1ad5fc57d restructure, cleanup 2021-04-03 22:50:37 +02:00
NEZNAMY dd3a457ec0 fixed tablist entries not getting shorter after maxPlayer quits 2021-04-03 22:50:14 +02:00
NEZNAMY 069f2aba8b updated info 2021-04-03 21:40:29 +02:00
NEZNAMY 9e19553150 added option to disable unlimited nametags in worlds 2021-04-03 17:26:24 +02:00
NEZNAMY 2d7b009442 no longer refreshing if value did not change 2021-04-03 16:42:59 +02:00
NEZNAMY 68817e3595 post-recode fix 2021-04-03 16:42:36 +02:00
NEZNAMY 3e229d6b82 do not refresh unused placeholders 2021-04-03 12:15:29 +02:00
NEZNAMY 5fce471577 fixed empty packets being sent causing white names with anti-override disabled 2021-04-01 22:58:55 +02:00
NEZNAMY 68b1e7cb16 nametag classes refactor, NameTag is base class which NameTagX extends if enabled
also made invisible-nametags public
2021-04-01 22:15:18 +02:00
NEZNAMY 761e02ff6e fixed unsupported operation from previous commit 2021-04-01 16:59:08 +02:00
NEZNAMY 5fe4408631 using in-built java function instead of google one 2021-04-01 16:05:27 +02:00
NEZNAMY bae0685ac1 added check for team name length 2021-04-01 11:32:31 +02:00
NEZNAMY 6322a0c045 fixed concurrent modification 2021-04-01 11:32:07 +02:00
NEZNAMY 0b89de905e added null check 2021-03-31 21:05:32 +02:00
NEZNAMY 49c4453678 removed unnecessary full path 2021-03-31 08:25:49 +02:00
NEZNAMY c056cafda6 fixed error when left side of condition was empty 2021-03-30 22:02:40 +02:00
NEZNAMY b375c24340 added API methods to force team name 2021-03-30 15:16:26 +02:00
NEZNAMY 5a76131f24 setBossbarVisible method now performs all actions 2021-03-30 15:12:27 +02:00
NEZNAMY 764769a6ca fixed startup error caused by misconfiguration 2021-03-30 11:20:02 +02:00
NEZNAMY b0226df3ab updated comments 2021-03-29 22:39:25 +02:00
NEZNAMY 8c86120b33 removed unused method 2021-03-29 22:01:58 +02:00
233 changed files with 71154 additions and 65525 deletions

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://paypal.me/neznamy1'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+4 -5
View File
@@ -1,8 +1,7 @@
# Compiled class file
*.class
/bin/
/target/
/src/test/
bin/
target/
# Log file
*.log
@@ -31,8 +30,8 @@ hs_err_pid*
.project
# intellij
/.idea/
.idea/
# other
.gitattributes
*.iml
*.iml
+5 -13
View File
@@ -1,14 +1,11 @@
## Introduction
Here it is. After countless requests to go opensource, I did it to make those people quiet. It worked.
It's already been a year and I still can't see any positives of it. This way I just made it easier for everyone to make a plugin as good, so people can stop using it and switch to something better.
It's already been a year and I still can't see any positives of it. This way I just made it easier for everyone to make a plugin as good, so people can stop using it and switch to something better. Or just get premium version for free.
## Compiling
Plugin can be compiled using maven.
## Premium code
The source contains content of premium version as well. However, this must be activated by setting [this line to return true](https://github.com/NEZNAMY/TAB/blob/master/src/main/java/me/neznamy/tab/shared/TAB.java#L79).
Plugin can be compiled using maven. Once you've purchased the premium version, you can enable premium features by setting [this line to return true](https://github.com/NEZNAMY/TAB/blob/master/shared/src/main/java/me/neznamy/tab/shared/TAB.java#L78) and apply your code improvements. Enabling it without purchasing the plugin may result in the features not working correctly.
**No support is provided for self-compiled jars, only for official compilations.** If you compiled it yourself, it is highly likely you modified something, which might be the reason why is the plugin not working. If you didn't modify anything, you can just get the plugin from official download places.
## Links
@@ -22,13 +19,8 @@ Premium version
Bukkit bridge https://www.spigotmc.org/resources/83966/
Wiki https://github.com/NEZNAMY/TAB/wiki
Discord invite link: https://discord.gg/EaSvdk6
## Downloads
Official release: https://www.spigotmc.org/resources/57806/
Pre-releases: https://github.com/NEZNAMY/TAB/releases
## Maven Repository
```
<repositories>
+137
View File
@@ -0,0 +1,137 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>me.neznamy</groupId>
<artifactId>TAB</artifactId>
<version>2.9.0</version>
</parent>
<artifactId>bukkit</artifactId>
<repositories>
<!-- Spigot API -->
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<!-- PlaceholderAPI -->
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<!-- Vault -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<!-- ViaVersion -->
<repository>
<id>viaversion</id>
<url>https://repo.viaversion.com/</url>
</repository>
<repository>
<id>aikar</id>
<url>https://repo.aikar.co/content/groups/aikar/</url>
</repository>
</repositories>
<dependencies>
<!-- TAB Core -->
<dependency>
<groupId>me.neznamy</groupId>
<artifactId>shared</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.github.NEZNAMY</groupId>
<artifactId>YamlAssist</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- PlaceholderAPI -->
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.10.9</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Vault -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- ViaVersion -->
<dependency>
<groupId>com.viaversion</groupId>
<artifactId>viaversion-api</artifactId>
<version>4.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>1.11</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -3,13 +3,10 @@ package me.neznamy.tab.platforms.bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import me.neznamy.tab.shared.TAB;
@@ -57,34 +54,4 @@ public class BukkitEventListener implements Listener {
if (TAB.getInstance().isDisabled()) return;
if (TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
}
/**
* Listener to AsyncPlayerChatEvent to forward the event to features
* @param e chat event
*/
@EventHandler
public void onChat(AsyncPlayerChatEvent e) {
if (TAB.getInstance().isDisabled()) return;
if (TAB.getInstance().getFeatureManager().onChat(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage(), e.isCancelled())) e.setCancelled(true);
}
/**
* Listener to PlayerRespawnEvent to forward the event to features
* @param e respawn event
*/
@EventHandler
public void onRespawn(PlayerRespawnEvent e) {
if (TAB.getInstance().isDisabled()) return;
TAB.getInstance().getCPUManager().runTask("processing PlayerRespawnEvent", () -> TAB.getInstance().getFeatureManager().onRespawn(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
}
/**
* Listener to PlayerToggleSneakEvent to forward the event to features
* @param e sneak event
*/
@EventHandler
public void onSneak(PlayerToggleSneakEvent e) {
if (TAB.getInstance().isDisabled()) return;
TAB.getInstance().getCPUManager().runTask("processing PlayerToggleSneakEvent", () -> TAB.getInstance().getFeatureManager().onSneak(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.isSneaking()));
}
}
@@ -16,7 +16,7 @@ import org.bukkit.plugin.ServicePriority;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.Metrics;
@@ -97,7 +97,7 @@ public class BukkitMetrics extends Metrics {
}));
addCustomChart(new SimplePie("server_version", new Callable<String>() {
public String call() {
return "1." + ProtocolVersion.SERVER_VERSION.getMinorVersion() + ".x";
return "1." + NMSStorage.getInstance().minorVersion + ".x";
}
}));
}
@@ -1,6 +1,8 @@
package me.neznamy.tab.platforms.bukkit;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -10,6 +12,11 @@ import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.PropertyMap;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.libs.gson.JsonParser;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
@@ -18,6 +25,7 @@ import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.packets.EnumChatFormat;
import me.neznamy.tab.shared.packets.IChatBaseComponent;
import me.neznamy.tab.shared.packets.IChatBaseComponent.ClickAction;
import me.neznamy.tab.shared.packets.PacketBuilder;
import me.neznamy.tab.shared.packets.PacketPlayOutBoss;
import me.neznamy.tab.shared.packets.PacketPlayOutBoss.Action;
@@ -32,16 +40,14 @@ import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardScore;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardTeam;
import me.neznamy.tab.shared.packets.PacketPlayOutTitle;
import us.myles.ViaVersion.api.type.Type;
import us.myles.viaversion.libs.gson.JsonParser;
import me.neznamy.tab.shared.rgb.TextColor;
@SuppressWarnings("unchecked")
public class BukkitPacketBuilder implements PacketBuilder {
//nms storage
private NMSStorage nms;
//entity type ids
private Map<EntityType, Integer> entityIds = new HashMap<EntityType, Integer>();
@@ -51,22 +57,22 @@ public class BukkitPacketBuilder implements PacketBuilder {
*/
public BukkitPacketBuilder(NMSStorage nms) {
this.nms = nms;
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 13) {
if (nms.minorVersion >= 13) {
entityIds.put(EntityType.ARMOR_STAND, 1);
entityIds.put(EntityType.WITHER, 83);
} else {
entityIds.put(EntityType.WITHER, 64);
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 8){
if (nms.minorVersion >= 8){
entityIds.put(EntityType.ARMOR_STAND, 30);
}
}
}
@Override
public Object build(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws Exception {
if (nms.minorVersion >= 9) {
//1.9+ server
return buildBossPacket19(packet, clientVersion);
//1.9+ server, handled using bukkit api
return packet;
}
if (clientVersion.getMinorVersion() >= 9 && Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
//1.9+ client on 1.8 server
@@ -78,27 +84,6 @@ public class BukkitPacketBuilder implements PacketBuilder {
return buildBossPacketEntity(packet, clientVersion);
}
/**
* Build 1.9 boss packet
* @param packet - packet to build
* @param clientVersion - client version
* @return 1.9 bossbar packet
* @throws Exception - if something fails
*/
private Object buildBossPacket19(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws Exception {
Object nmsPacket = nms.newPacketPlayOutBoss.newInstance();
nms.PacketPlayOutBoss_UUID.set(nmsPacket, packet.id);
nms.PacketPlayOutBoss_ACTION.set(nmsPacket, Enum.valueOf(nms.PacketPlayOutBoss_Action, packet.operation.toString()));
nms.PacketPlayOutBoss_PROGRESS.set(nmsPacket, packet.pct);
nms.PacketPlayOutBoss_NAME.set(nmsPacket, packet.name == null ? null : stringToComponent(IChatBaseComponent.optimizedComponent(packet.name).toString(clientVersion)));
nms.PacketPlayOutBoss_COLOR.set(nmsPacket, packet.color == null ? null : Enum.valueOf(nms.BarColor, packet.color.toString()));
nms.PacketPlayOutBoss_STYLE.set(nmsPacket, packet.overlay == null ? null : Enum.valueOf(nms.BarStyle, packet.overlay.toString()));
nms.PacketPlayOutBoss_DARKEN_SKY.set(nmsPacket, packet.darkenScreen);
nms.PacketPlayOutBoss_PLAY_MUSIC.set(nmsPacket, packet.playMusic);
nms.PacketPlayOutBoss_CREATE_FOG.set(nmsPacket, packet.createWorldFog);
return nmsPacket;
}
/**
* Builds 1.9 bossbar packet bytebuf using viaversion
* @param packet - packet to build
@@ -106,11 +91,11 @@ public class BukkitPacketBuilder implements PacketBuilder {
* @return bytebuf with 1.9 bossbar packet content
* @throws Exception - if something fails
*/
private ByteBuf buildBossPacketVia(PacketPlayOutBoss packet, ProtocolVersion clientVersion) throws Exception {
private ByteBuf buildBossPacketVia(PacketPlayOutBoss packet, ProtocolVersion clientVersion) {
if (clientVersion == ProtocolVersion.UNKNOWN) return null; //preventing disconnect if packet ID changes and users do not update
try {
ByteBuf buf = Unpooled.buffer();
Type.VAR_INT.writePrimitive(buf, clientVersion.getMinorVersion() == 15 ? 0x0D : 0x0C);
Type.VAR_INT.writePrimitive(buf, clientVersion.getMinorVersion() == 15 || clientVersion.getMinorVersion() >= 17 ? 0x0D : 0x0C);
Type.UUID.write(buf, packet.id);
Type.VAR_INT.writePrimitive(buf, packet.operation.ordinal());
switch (packet.operation) {
@@ -178,36 +163,41 @@ public class BukkitPacketBuilder implements PacketBuilder {
@Override
public Object build(PacketPlayOutChat packet, ProtocolVersion clientVersion) throws Exception {
Object nmsPacket = nms.newPacketPlayOutChat.newInstance();
nms.PacketPlayOutChat_MESSAGE.set(nmsPacket, stringToComponent(packet.message.toString(clientVersion)));
if (nms.minorVersion >= 12) {
nms.PacketPlayOutChat_POSITION.set(nmsPacket, Enum.valueOf(nms.ChatMessageType, packet.type.toString()));
} else if (nms.minorVersion >= 8) {
nms.PacketPlayOutChat_POSITION.set(nmsPacket, (byte)packet.type.ordinal());
}
Object component = stringToComponent(packet.message.toString(clientVersion));
if (nms.minorVersion >= 16) {
nms.PacketPlayOutChat_SENDER.set(nmsPacket, UUID.randomUUID());
return nms.newPacketPlayOutChat.newInstance(component, ((Object[])nms.ChatMessageType.getMethod("values").invoke(null))[packet.type.ordinal()], UUID.randomUUID());
}
return nmsPacket;
if (nms.minorVersion >= 12) {
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.type.toString()));
}
if (nms.minorVersion >= 8) {
return nms.newPacketPlayOutChat.newInstance(component, (byte) packet.type.ordinal());
}
if (nms.minorVersion == 7) {
return nms.newPacketPlayOutChat.newInstance(component);
}
throw new IllegalStateException("Not supported on <1.7");
}
@Override
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws Exception {
if (nms.minorVersion < 8) return null;
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance();
nms.PacketPlayOutPlayerInfo_ACTION.set(nmsPacket, Enum.valueOf(nms.EnumPlayerInfoAction, packet.action.toString()));
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(((Object[])nms.EnumPlayerInfoAction.getMethod("values").invoke(null))[packet.action.ordinal()], Array.newInstance(nms.EntityPlayer, 0));
List<Object> items = new ArrayList<Object>();
for (PlayerInfoData data : packet.entries) {
Object profile = nms.newGameProfile.newInstance(data.uniqueId, data.name);
if (data.skin != null) nms.PropertyMap_putAll.invoke(nms.GameProfile_PROPERTIES.get(profile), data.skin);
GameProfile profile = new GameProfile(data.uniqueId, data.name);
if (data.skin != null) profile.getProperties().putAll((PropertyMap) data.skin);
List<Object> parameters = new ArrayList<Object>();
if (nms.newPlayerInfoData.getParameterCount() == 5) {
items.add(nms.newPlayerInfoData.newInstance(nms.newPacketPlayOutPlayerInfo.newInstance(), profile, data.latency, data.gameMode == null ? null : Enum.valueOf(nms.EnumGamemode, data.gameMode.toString()),
data.displayName == null ? null : stringToComponent(data.displayName.toString(clientVersion))));
} else {
//1.8.8 paper
items.add(nms.newPlayerInfoData.newInstance(profile, data.latency, data.gameMode == null ? null : Enum.valueOf(nms.EnumGamemode, data.gameMode.toString()),
data.displayName == null ? null : stringToComponent(data.displayName.toString(clientVersion))));
parameters.add(nmsPacket);
}
parameters.add(profile);
parameters.add(data.latency);
Object[] values = (Object[]) nms.EnumGamemode.getMethod("values").invoke(null);
parameters.add(data.gameMode == null ? null : values[values.length-EnumGamemode.values().length+data.gameMode.ordinal()]); //not_set was removed in 1.17
parameters.add(data.displayName == null ? null : toNMSComponent(data.displayName, clientVersion));
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
}
nms.PacketPlayOutPlayerInfo_PLAYERS.set(nmsPacket, items);
return nmsPacket;
@@ -215,39 +205,36 @@ public class BukkitPacketBuilder implements PacketBuilder {
@Override
public Object build(PacketPlayOutPlayerListHeaderFooter packet, ProtocolVersion clientVersion) throws Exception {
if (nms.PacketPlayOutPlayerListHeaderFooter == null) return null;
if (nms.minorVersion >= 17) {
return nms.newPacketPlayOutPlayerListHeaderFooter.newInstance(toNMSComponent(packet.header, clientVersion), toNMSComponent(packet.footer, clientVersion));
}
Object nmsPacket = nms.newPacketPlayOutPlayerListHeaderFooter.newInstance();
nms.PacketPlayOutPlayerListHeaderFooter_HEADER.set(nmsPacket, stringToComponent(packet.header.toString(clientVersion, true)));
nms.PacketPlayOutPlayerListHeaderFooter_FOOTER.set(nmsPacket, stringToComponent(packet.footer.toString(clientVersion, true)));
nms.PacketPlayOutPlayerListHeaderFooter_HEADER.set(nmsPacket, toNMSComponent(packet.header, clientVersion));
nms.PacketPlayOutPlayerListHeaderFooter_FOOTER.set(nmsPacket, toNMSComponent(packet.footer, clientVersion));
return nmsPacket;
}
@Override
public Object build(PacketPlayOutScoreboardDisplayObjective packet, ProtocolVersion clientVersion) throws Exception {
Object nmsPacket = nms.newPacketPlayOutScoreboardDisplayObjective.newInstance();
nms.PacketPlayOutScoreboardDisplayObjective_POSITION.set(nmsPacket, packet.slot);
nms.PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME.set(nmsPacket, packet.objectiveName);
return nmsPacket;
return nms.newPacketPlayOutScoreboardDisplayObjective.newInstance(packet.slot, newScoreboardObjective(packet.objectiveName));
}
@Override
public Object build(PacketPlayOutScoreboardObjective packet, ProtocolVersion clientVersion) throws Exception {
String displayName = packet.displayName;
if (clientVersion.getMinorVersion() < 13) {
displayName = cutTo(displayName, 32);
String displayName = clientVersion.getMinorVersion() < 13 ? cutTo(packet.displayName, 32) : packet.displayName;
if (nms.minorVersion >= 13) {
return nms.newPacketPlayOutScoreboardObjective.newInstance(nms.newScoreboardObjective.newInstance(null, packet.objectiveName, null,
toNMSComponent(IChatBaseComponent.optimizedComponent(displayName), clientVersion),
packet.renderType == null ? null : ((Object[])nms.EnumScoreboardHealthDisplay.getMethod("values").invoke(null))[packet.renderType.ordinal()]),
packet.method);
}
Object nmsPacket = nms.newPacketPlayOutScoreboardObjective.newInstance();
nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME.set(nmsPacket, packet.objectiveName);
if (nms.minorVersion >= 13) {
nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.set(nmsPacket, stringToComponent(IChatBaseComponent.optimizedComponent(displayName).toString(clientVersion)));
} else {
nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.set(nmsPacket, displayName);
}
nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.set(nmsPacket, displayName);
if (nms.PacketPlayOutScoreboardObjective_RENDERTYPE != null && packet.renderType != null) {
if (nms.minorVersion >= 8) {
nms.PacketPlayOutScoreboardObjective_RENDERTYPE.set(nmsPacket, Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.renderType.toString()));
} else {
nms.PacketPlayOutScoreboardObjective_RENDERTYPE.set(nmsPacket, packet.renderType.ordinal());
}
nms.PacketPlayOutScoreboardObjective_RENDERTYPE.set(nmsPacket, Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.renderType.toString()));
}
nms.PacketPlayOutScoreboardObjective_METHOD.set(nmsPacket, packet.method);
return nmsPacket;
@@ -256,21 +243,17 @@ public class BukkitPacketBuilder implements PacketBuilder {
@Override
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws Exception {
if (nms.minorVersion >= 13) {
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(Enum.valueOf(nms.EnumScoreboardAction, packet.action.toString()), packet.objectiveName, packet.player, packet.score);
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(((Object[])nms.EnumScoreboardAction.getMethod("values").invoke(null))[packet.action.ordinal()], packet.objectiveName, packet.player, packet.score);
}
if (packet.action == PacketPlayOutScoreboardScore.Action.REMOVE) {
return nms.newPacketPlayOutScoreboardScore_String.newInstance(packet.player);
}
Object nmsPacket = nms.newPacketPlayOutScoreboardScore0.newInstance();
nms.PacketPlayOutScoreboardScore_PLAYER.set(nmsPacket, packet.player);
nms.PacketPlayOutScoreboardScore_OBJECTIVENAME.set(nmsPacket, packet.objectiveName);
nms.PacketPlayOutScoreboardScore_SCORE.set(nmsPacket, packet.score);
Object score = nms.newScoreboardScore.newInstance(nms.newScoreboard.newInstance(), newScoreboardObjective(packet.objectiveName), packet.player);
nms.ScoreboardScore_setScore.invoke(score, packet.score);
if (nms.minorVersion >= 8) {
nms.PacketPlayOutScoreboardScore_ACTION.set(nmsPacket, Enum.valueOf(nms.EnumScoreboardAction, packet.action.toString()));
} else {
nms.PacketPlayOutScoreboardScore_ACTION.set(nmsPacket, packet.action.ordinal());
return nms.newPacketPlayOutScoreboardScore.newInstance(score);
}
return nmsPacket;
return nms.newPacketPlayOutScoreboardScore.newInstance(score, 0);
}
@Override
@@ -281,44 +264,52 @@ public class BukkitPacketBuilder implements PacketBuilder {
prefix = cutTo(prefix, 16);
suffix = cutTo(suffix, 16);
}
Object nmsPacket = nms.newPacketPlayOutScoreboardTeam.newInstance();
nms.PacketPlayOutScoreboardTeam_NAME.set(nmsPacket, packet.name);
Object team = nms.newScoreboardTeam.newInstance(nms.newScoreboard.newInstance(), packet.name);
((Collection<String>)nms.ScoreboardTeam_getPlayerNameSet.invoke(team)).addAll(packet.players);
if (nms.minorVersion >= 13) {
nms.PacketPlayOutScoreboardTeam_DISPLAYNAME.set(nmsPacket, stringToComponent(IChatBaseComponent.optimizedComponent(packet.name).toString(clientVersion)));
if (prefix != null && prefix.length() > 0) nms.PacketPlayOutScoreboardTeam_PREFIX.set(nmsPacket, stringToComponent(IChatBaseComponent.optimizedComponent(prefix).toString(clientVersion)));
if (suffix != null && suffix.length() > 0) nms.PacketPlayOutScoreboardTeam_SUFFIX.set(nmsPacket, stringToComponent(IChatBaseComponent.optimizedComponent(suffix).toString(clientVersion)));
if (prefix != null && prefix.length() > 0) nms.ScoreboardTeam_setPrefix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(prefix), clientVersion));
if (suffix != null && suffix.length() > 0) nms.ScoreboardTeam_setSuffix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(suffix), clientVersion));
EnumChatFormat format = packet.color != null ? packet.color : EnumChatFormat.lastColorsOf(prefix);
nms.PacketPlayOutScoreboardTeam_CHATFORMAT.set(nmsPacket, Enum.valueOf(nms.EnumChatFormat, format.toString()));
nms.ScoreboardTeam_setColor.invoke(team, ((Object[])nms.EnumChatFormat.getMethod("values").invoke(null))[format.ordinal()]);
} else {
nms.PacketPlayOutScoreboardTeam_DISPLAYNAME.set(nmsPacket, packet.name);
nms.PacketPlayOutScoreboardTeam_PREFIX.set(nmsPacket, prefix);
nms.PacketPlayOutScoreboardTeam_SUFFIX.set(nmsPacket, suffix);
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, prefix);
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, suffix);
}
if (nms.PacketPlayOutScoreboardTeam_VISIBILITY != null) nms.PacketPlayOutScoreboardTeam_VISIBILITY.set(nmsPacket, packet.nametagVisibility);
if (nms.PacketPlayOutScoreboardTeam_COLLISION != null) nms.PacketPlayOutScoreboardTeam_COLLISION.set(nmsPacket, packet.collisionRule);
nms.PacketPlayOutScoreboardTeam_PLAYERS.set(nmsPacket, packet.players);
nms.PacketPlayOutScoreboardTeam_ACTION.set(nmsPacket, packet.method);
nms.PacketPlayOutScoreboardTeam_SIGNATURE.set(nmsPacket, packet.options);
return nmsPacket;
if (nms.EnumNameTagVisibility != null && packet.nametagVisibility != null) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, packet.nametagVisibility.equals("always") ? ((Object[])nms.EnumNameTagVisibility.getMethod("values").invoke(null))[0] : ((Object[])nms.EnumNameTagVisibility.getMethod("values").invoke(null))[1]);
if (nms.EnumTeamPush != null && packet.collisionRule != null) nms.ScoreboardTeam_setCollisionRule.invoke(team, packet.collisionRule.equals("always") ? ((Object[])nms.EnumTeamPush.getMethod("values").invoke(null))[0] : ((Object[])nms.EnumTeamPush.getMethod("values").invoke(null))[1]);
if (nms.minorVersion >= 17) {
switch (packet.method) {
case 0:
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, true);
case 1:
return nms.PacketPlayOutScoreboardTeam_of.invoke(null, team);
case 2:
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, false);
case 3:
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.players.toArray(new String[0])[0], ((Object[])nms.PacketPlayOutScoreboardTeam_a.getMethod("values").invoke(null))[0]);
case 4:
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.players.toArray(new String[0])[0], ((Object[])nms.PacketPlayOutScoreboardTeam_a.getMethod("values").invoke(null))[1]);
default:
throw new IllegalArgumentException("Invalid action: " + packet.method);
}
}
return nms.newPacketPlayOutScoreboardTeam.newInstance(team, packet.method);
}
@Override
public Object build(PacketPlayOutTitle packet, ProtocolVersion clientVersion) throws Exception {
if (nms.minorVersion < 8) return null;
return nms.newPacketPlayOutTitle.newInstance(Enum.valueOf(nms.EnumTitleAction, packet.action.toString()),
packet.text == null ? null : stringToComponent(IChatBaseComponent.optimizedComponent(packet.text).toString(clientVersion)), packet.fadeIn, packet.stay, packet.fadeOut);
}
/**
* Builds entity destroy packet with given parameter
* @param ids - entity ids to destroy
* @param id - entity id to destroy
* @return destroy packet
* @throws Exception - if reflection fails
*/
public Object buildEntityDestroyPacket(int... ids) throws Exception {
return nms.newPacketPlayOutEntityDestroy.newInstance(ids);
public Object buildEntityDestroyPacket(int id) throws Exception {
if (nms.minorVersion >= 17) {
return nms.newPacketPlayOutEntityDestroy.newInstance(id);
}
return nms.newPacketPlayOutEntityDestroy.newInstance(new int[] {id});
}
/**
* Builds entity metadata packet with given parameters
* @param entityId - entity id
@@ -329,7 +320,7 @@ public class BukkitPacketBuilder implements PacketBuilder {
public Object buildEntityMetadataPacket(int entityId, DataWatcher dataWatcher) throws Exception {
return nms.newPacketPlayOutEntityMetadata.newInstance(entityId, dataWatcher.toNMS(), true);
}
/**
* Builds entity spawn packet with given parameters
* @param entityId - entity id
@@ -341,7 +332,7 @@ public class BukkitPacketBuilder implements PacketBuilder {
* @throws Exception - if reflection fails
*/
public Object buildEntitySpawnPacket(int entityId, UUID uuid, EntityType entityType, Location loc, DataWatcher dataWatcher) throws Exception {
Object nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance();
Object nmsPacket = nms.newPacketPlayOutSpawnEntityLiving.newInstance(nms.getHandle.invoke(Bukkit.getOnlinePlayers().iterator().next()));
nms.PacketPlayOutSpawnEntityLiving_ENTITYID.set(nmsPacket, entityId);
nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE.set(nmsPacket, entityIds.get(entityType));
nms.PacketPlayOutSpawnEntityLiving_YAW.set(nmsPacket, (byte)(loc.getYaw() * 256.0f / 360.0f));
@@ -349,7 +340,7 @@ public class BukkitPacketBuilder implements PacketBuilder {
if (nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER != null) {
nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.set(nmsPacket, dataWatcher.toNMS());
}
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
if (nms.minorVersion >= 9) {
nms.PacketPlayOutSpawnEntityLiving_UUID.set(nmsPacket, uuid);
nms.PacketPlayOutSpawnEntityLiving_X.set(nmsPacket, loc.getX());
nms.PacketPlayOutSpawnEntityLiving_Y.set(nmsPacket, loc.getY());
@@ -361,7 +352,7 @@ public class BukkitPacketBuilder implements PacketBuilder {
}
return nmsPacket;
}
/**
* Builds entity teleport packet with given parameters
* @param entityId - entity id
@@ -370,9 +361,9 @@ public class BukkitPacketBuilder implements PacketBuilder {
* @throws Exception - if reflection fails
*/
public Object buildEntityTeleportPacket(int entityId, Location location) throws Exception {
Object nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance();
Object nmsPacket = nms.newPacketPlayOutEntityTeleport.newInstance(nms.getHandle.invoke(Bukkit.getOnlinePlayers().iterator().next()));
nms.PacketPlayOutEntityTeleport_ENTITYID.set(nmsPacket, entityId);
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
if (nms.minorVersion >= 9) {
nms.PacketPlayOutEntityTeleport_X.set(nmsPacket, location.getX());
nms.PacketPlayOutEntityTeleport_Y.set(nmsPacket, location.getY());
nms.PacketPlayOutEntityTeleport_Z.set(nmsPacket, location.getZ());
@@ -392,12 +383,12 @@ public class BukkitPacketBuilder implements PacketBuilder {
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
List<PlayerInfoData> listData = new ArrayList<PlayerInfoData>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGamemode = nms.PlayerInfoData_GAMEMODE.get(nmsData);
EnumGamemode gamemode = nmsGamemode == null ? null : EnumGamemode.valueOf(nmsGamemode.toString());
Object profile = nms.PlayerInfoData_PROFILE.get(nmsData);
Object nmsComponent = nms.PlayerInfoData_LISTNAME.get(nmsData);
IChatBaseComponent listName = IChatBaseComponent.fromString(componentToString(nmsComponent));
listData.add(new PlayerInfoData((String) nms.GameProfile_NAME.get(profile), (UUID) nms.GameProfile_ID.get(profile), nms.GameProfile_PROPERTIES.get(profile), nms.PlayerInfoData_PING.getInt(nmsData), gamemode, listName));
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);
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), profile.getProperties(), (int)nms.PlayerInfoData_getLatency.invoke(nmsData), gamemode, listName));
}
return new PacketPlayOutPlayerInfo(action, listData);
}
@@ -408,7 +399,8 @@ public class BukkitPacketBuilder implements PacketBuilder {
String displayName;
if (nms.minorVersion >= 13) {
Object component = nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.get(nmsPacket);
displayName = component == null ? null : IChatBaseComponent.fromString(componentToString(component)).toLegacyText();
IChatBaseComponent c = component == null ? null : fromNMSComponent(component);
displayName = c == null ? null : c.toLegacyText();
} else {
displayName = (String) nms.PacketPlayOutScoreboardObjective_DISPLAYNAME.get(nmsPacket);
}
@@ -416,11 +408,7 @@ public class BukkitPacketBuilder implements PacketBuilder {
if (nms.PacketPlayOutScoreboardObjective_RENDERTYPE != null) {
Object nmsRender = nms.PacketPlayOutScoreboardObjective_RENDERTYPE.get(nmsPacket);
if (nmsRender != null) {
if (nms.minorVersion >= 8) {
renderType = EnumScoreboardHealthDisplay.valueOf(nmsRender.toString());
} else {
renderType = EnumScoreboardHealthDisplay.values()[(int)nmsRender];
}
renderType = EnumScoreboardHealthDisplay.valueOf(nmsRender.toString());
}
}
int method = nms.PacketPlayOutScoreboardObjective_METHOD.getInt(nmsPacket);
@@ -435,15 +423,6 @@ public class BukkitPacketBuilder implements PacketBuilder {
);
}
@Override
public PacketPlayOutPlayerListHeaderFooter readHeaderFooter(Object packet, ProtocolVersion clientVersion) throws Exception {
return new PacketPlayOutPlayerListHeaderFooter(
IChatBaseComponent.fromString(componentToString(nms.PacketPlayOutPlayerListHeaderFooter_HEADER.get(packet))),
IChatBaseComponent.fromString(componentToString(nms.PacketPlayOutPlayerListHeaderFooter_FOOTER.get(packet)))
);
}
/**
* A method yoinked from minecraft code used to convert double to int
* @param paramDouble double value
@@ -453,8 +432,7 @@ public class BukkitPacketBuilder implements PacketBuilder {
int i = (int)paramDouble;
return paramDouble < i ? i - 1 : i;
}
/**
* Converts json string into a component
* @param json json as string
@@ -465,15 +443,127 @@ public class BukkitPacketBuilder implements PacketBuilder {
if (json == null) return null;
return nms.ChatSerializer_DESERIALIZE.invoke(null, json);
}
/**
* Converts NMS component into a string
* @param component component to convert
* @return json in string format
* Converts minecraft IChatBaseComponent into TAB's component class. Currently does not support hover event.
* @param component - component to convert
* @return converted component
* @throws Exception if something fails
*/
public String componentToString(Object component) throws Exception {
public IChatBaseComponent fromNMSComponent(Object component) throws Exception {
long time = System.nanoTime();
IChatBaseComponent obj = fromNMSComponent0(component);
TAB.getInstance().getCPUManager().addMethodTime("fromNMSComponent", System.nanoTime()-time);
return obj;
}
//separate method to prevent extras counting cpu again due to recursion and finally showing higher usage than real
@SuppressWarnings("rawtypes")
public IChatBaseComponent fromNMSComponent0(Object component) throws Exception {
if (!nms.ChatComponentText.isInstance(component)) return null; //paper
IChatBaseComponent chat = new IChatBaseComponent((String) nms.ChatComponentText_text.get(component));
Object modifier = nms.ChatBaseComponent_modifier.get(component);
if (modifier != null) {
Object color = nms.ChatModifier_color.get(modifier);
if (color != null) {
if (nms.minorVersion >= 16) {
String name = (String) nms.ChatHexColor_name.get(color);
if (name != null) {
//legacy code
chat.setColor(new TextColor(EnumChatFormat.valueOf(name.toUpperCase())));
} else {
int rgb = (int) nms.ChatHexColor_rgb.get(color);
chat.setColor(new TextColor((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF));
}
} else {
chat.setColor(new TextColor(EnumChatFormat.valueOf(((Enum)color).name())));
}
}
chat.setBold((Boolean) nms.ChatModifier_bold.get(modifier));
chat.setItalic((Boolean) nms.ChatModifier_italic.get(modifier));
chat.setObfuscated((Boolean) nms.ChatModifier_obfuscated.get(modifier));
chat.setStrikethrough((Boolean) nms.ChatModifier_strikethrough.get(modifier));
chat.setUnderlined((Boolean) nms.ChatModifier_underlined.get(modifier));
Object clickEvent = nms.ChatModifier_clickEvent.get(modifier);
if (clickEvent != null) {
chat.onClick(ClickAction.valueOf(nms.ChatClickable_action.get(clickEvent).toString().toUpperCase()), (String) nms.ChatClickable_value.get(clickEvent));
}
/* Object hoverEvent = nms.ChatModifier_hoverEvent.get(modifier);
if (hoverEvent != null) {
chat.onHover(HoverAction.fromString(nms.ChatHoverable_action.get(hoverEvent).toString()), fromNMSComponent(nms.ChatHoverable_value.get(hoverEvent)));
}*/
}
for (Object extra : (List<Object>) nms.ChatBaseComponent_extra.get(component)) {
chat.addExtra(fromNMSComponent0(extra));
}
return chat;
}
/**
* 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 Exception if something fails
*/
public Object toNMSComponent(IChatBaseComponent component, ProtocolVersion clientVersion) throws Exception {
long time = System.nanoTime();
Object obj = toNMSComponent0(component, clientVersion);
TAB.getInstance().getCPUManager().addMethodTime("toNMSComponent", System.nanoTime()-time);
return obj;
}
//separate method to prevent extras counting cpu again due to recursion and finally showing higher usage than real
private Object toNMSComponent0(IChatBaseComponent component, ProtocolVersion clientVersion) throws Exception {
if (component == null) return null;
return (String) nms.ChatSerializer_SERIALIZE.invoke(null, component);
Object chat = nms.newChatComponentText.newInstance(component.getText());
Object modifier;
if (nms.minorVersion >= 16) {
Object color = null;
if (component.getColor() != null) {
if (clientVersion.getMinorVersion() >= 16) {
color = nms.ChatHexColor_ofInt.invoke(null, (component.getColor().getRed() << 16) + (component.getColor().getGreen() << 8) + component.getColor().getBlue());
} else {
color = nms.ChatHexColor_ofString.invoke(null, component.getColor().getLegacyColor().toString().toLowerCase());
}
}
modifier = nms.newChatModifier.newInstance(
color,
component.getBold(),
component.getItalic(),
component.getUnderlined(),
component.getStrikethrough(),
component.getObfuscated(),
component.getClickAction() == null ? null : nms.newChatClickable.newInstance(nms.EnumClickAction_a.invoke(null, component.getClickAction().toString().toLowerCase()), component.getClickValue().toString()),
null, //component.getHoverAction() == null ? null : nms.newChatHoverable.newInstance(nms.EnumHoverAction_a.invoke(null, component.getHoverAction().toString().toLowerCase()), stringToComponent(component.getHoverValue().toString())),
null, null
);
} else {
modifier = nms.newChatModifier.newInstance();
if (component.getClickAction() != null){
nms.ChatModifier_clickEvent.set(modifier, nms.newChatClickable.newInstance(nms.EnumClickAction_a.invoke(null, component.getClickAction().toString().toLowerCase()), component.getClickValue().toString()));
}
/* if (component.getHoverAction() != null) {
nms.ChatModifier_hoverEvent.set(modifier, nms.newChatHoverable.newInstance(nms.EnumHoverAction_a.invoke(null, (component.getHoverAction().toString().toLowerCase())), stringToComponent(component.getHoverValue().toString())));
}*/
if (component.getColor() != null) nms.ChatModifier_color.set(modifier, Enum.valueOf(nms.EnumChatFormat, component.getColor().getLegacyColor().toString()));
if (component.isBold()) nms.ChatModifier_bold.set(modifier, true);
if (component.isItalic()) nms.ChatModifier_italic.set(modifier, true);
if (component.isUnderlined()) nms.ChatModifier_underlined.set(modifier, true);
if (component.isStrikethrough()) nms.ChatModifier_strikethrough.set(modifier, true);
if (component.isObfuscated()) nms.ChatModifier_obfuscated.set(modifier, true);
}
nms.ChatBaseComponent_modifier.set(chat, modifier);
for (IChatBaseComponent extra : component.getExtra()) {
nms.ChatComponentText_addSibling.invoke(chat, toNMSComponent0(extra, clientVersion));
}
return chat;
}
private Object newScoreboardObjective(String objectiveName) throws Exception {
if (nms.minorVersion >= 13) {
return nms.newScoreboardObjective.newInstance(null, objectiveName, null, stringToComponent("{\"text\":\"\"}"), null);
}
return nms.newScoreboardObjective.newInstance(null, objectiveName, nms.getFields(nms.IScoreboardCriteria, nms.IScoreboardCriteria).get(0).get(null));
}
}
@@ -64,24 +64,25 @@ public class BukkitPipelineInjector extends PipelineInjector {
@SuppressWarnings("unchecked")
private void modifyPlayers(Object packetPlayOutScoreboardTeam) throws Exception {
long time = System.nanoTime();
if (nms.PacketPlayOutScoreboardTeam_SIGNATURE.getInt(packetPlayOutScoreboardTeam) != 69) {
Collection<String> players = (Collection<String>) nms.PacketPlayOutScoreboardTeam_PLAYERS.get(packetPlayOutScoreboardTeam);
//creating a new list to prevent NoSuchFieldException in minecraft packet encoder when a player is removed
Collection<String> newList = new ArrayList<String>();
for (String entry : players) {
TabPlayer p = tab.getPlayer(entry);
if (p == null) {
newList.add(entry);
continue;
}
if (tab.getFeatureManager().getNameTagFeature().isDisabledWorld(p.getWorldName())) {
newList.add(entry);
} else {
logTeamOverride((String) nms.PacketPlayOutScoreboardTeam_NAME.get(packetPlayOutScoreboardTeam), entry);
}
Collection<String> players = (Collection<String>) nms.PacketPlayOutScoreboardTeam_PLAYERS.get(packetPlayOutScoreboardTeam);
String teamName = (String) nms.PacketPlayOutScoreboardTeam_NAME.get(packetPlayOutScoreboardTeam);
if (players == null) return;
//creating a new list to prevent NoSuchFieldException in minecraft packet encoder when a player is removed
Collection<String> newList = new ArrayList<String>();
for (String entry : players) {
TabPlayer p = tab.getPlayer(entry);
if (p == null) {
newList.add(entry);
continue;
}
if (!tab.getFeatureManager().getNameTagFeature().isDisabledWorld(p.getWorldName()) && !p.hasTeamHandlingPaused() &&
!p.getTeamName().equals(teamName)) {
logTeamOverride(teamName, entry);
} else {
newList.add(entry);
}
nms.PacketPlayOutScoreboardTeam_PLAYERS.set(packetPlayOutScoreboardTeam, newList);
}
nms.PacketPlayOutScoreboardTeam_PLAYERS.set(packetPlayOutScoreboardTeam, newList);
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
}
@@ -129,9 +130,6 @@ public class BukkitPipelineInjector extends PipelineInjector {
if (antiOverrideObjectives && nms.PacketPlayOutScoreboardObjective.isInstance(packet)) {
tab.getFeatureManager().onObjective(player, packet);
}
if (nms.PacketPlayOutPlayerListHeaderFooter.isInstance(packet) && antiOverrideHeaderFooter && tab.getFeatureManager().onHeaderFooter(player, packet)) {
return;
}
tab.getFeatureManager().onPacketSend(player, packet);
} catch (Throwable e){
tab.getErrorManager().printError("An error occurred when reading packets", e);
@@ -1,6 +1,7 @@
package me.neznamy.tab.platforms.bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
@@ -16,6 +17,7 @@ import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PlaceholderManager;
import me.neznamy.tab.shared.placeholders.Placeholder;
@@ -32,19 +34,23 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
//formatter for 2 decimal places
public final DecimalFormat decimal2 = ((DecimalFormat)NumberFormat.getNumberInstance(Locale.US));
//plugin instance
private JavaPlugin plugin;
//vault economy
private Economy economy;
//vault chat
private Chat chat;
//placeholder registry buffer
private List<Placeholder> placeholders;
//essentials methods
private Method getUser;
private Method getNickname;
/**
* Constructs new instance with given parameter
* @param plugin - plugin instance
@@ -58,6 +64,14 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
RegisteredServiceProvider<Chat> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
if (rspChat != null) chat = rspChat.getProvider();
}
if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
try {
getUser = Class.forName("com.earth2me.essentials.Essentials").getMethod("getUser", Player.class);
getNickname = Class.forName("com.earth2me.essentials.User").getMethod("getNickname");
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to load essentials methods", e);
}
}
}
@Override
@@ -77,14 +91,16 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
});
placeholders.add(new PlayerPlaceholder("%deaths%", 1000) {
public String get(TabPlayer p) {
return ((Player) p.getPlayer()).getStatistic(Statistic.DEATHS)+"";
}
});
placeholders.add(new PlayerPlaceholder("%health%", 100) {
public String get(TabPlayer p) {
return (int) Math.ceil(((Player) p.getPlayer()).getHealth())+"";
return String.valueOf(((Player) p.getPlayer()).getStatistic(Statistic.DEATHS));
}
});
if (NMSStorage.getInstance().minorVersion >= 6) {
placeholders.add(new PlayerPlaceholder("%health%", 100) {
public String get(TabPlayer p) {
return String.valueOf((int) Math.ceil(((Player) p.getPlayer()).getHealth()));
}
});
}
placeholders.add(new ServerPlaceholder("%tps%", 1000) {
public String get() {
try {
@@ -102,7 +118,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
for (TabPlayer all : TAB.getInstance().getPlayers()){
if (((Player) p.getPlayer()).canSee((Player) all.getPlayer())) var++;
}
return var+"";
return String.valueOf(var);
}
});
placeholders.add(new PlayerPlaceholder("%canseestaffonline%", 2000) {
@@ -111,7 +127,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
for (TabPlayer all : TAB.getInstance().getPlayers()){
if (all.isStaff() && ((Player) p.getPlayer()).canSee((Player) all.getPlayer())) var++;
}
return var+"";
return String.valueOf(var);
}
});
registerAFKPlaceholder();
@@ -158,6 +174,14 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
} catch (Throwable t) {
return TAB.getInstance().getErrorManager().printError("", "Failed to check AFK status of " + p.getName(), t);
}
if (!afk) {
try {
//purpur AFK API
afk = (boolean) p.getPlayer().getClass().getMethod("isAfk").invoke(p.getPlayer());
} catch (Throwable t) {
//not purpur
}
}
return afk ? yesAfk : noAfk;
}
@Override
@@ -214,17 +238,17 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
private void registerPositionPlaceholders() {
placeholders.add(new PlayerPlaceholder("%xPos%", 100) {
public String get(TabPlayer p) {
return ((Player) p.getPlayer()).getLocation().getBlockX()+"";
return String.valueOf(((Player) p.getPlayer()).getLocation().getBlockX());
}
});
placeholders.add(new PlayerPlaceholder("%yPos%", 100) {
public String get(TabPlayer p) {
return ((Player) p.getPlayer()).getLocation().getBlockY()+"";
return String.valueOf(((Player) p.getPlayer()).getLocation().getBlockY());
}
});
placeholders.add(new PlayerPlaceholder("%zPos%", 100) {
public String get(TabPlayer p) {
return ((Player) p.getPlayer()).getLocation().getBlockZ()+"";
return String.valueOf(((Player) p.getPlayer()).getLocation().getBlockZ());
}
});
}
@@ -236,18 +260,15 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
placeholders.add(new PlayerPlaceholder("%essentialsnick%", 1000) {
public String get(TabPlayer p) {
if (getNickname == null) return "<Internal plugin error>";
String name = null;
if (Bukkit.getPluginManager().isPluginEnabled("Essentials")) {
try {
Object essentials = Bukkit.getPluginManager().getPlugin("Essentials");
Object user = essentials.getClass().getMethod("getUser", Player.class).invoke(essentials, p.getPlayer());
name = (String) user.getClass().getMethod("getNickname").invoke(user);
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to get Essentials nickname of " + p.getName(), e);
}
try {
name = (String) getNickname.invoke(getUser.invoke(Bukkit.getPluginManager().getPlugin("Essentials"), p.getPlayer()));
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to get Essentials nickname of " + p.getName(), e);
}
if (name == null || name.length() == 0) return p.getName();
return TAB.getInstance().getConfiguration().getSecretOption("essentials-nickname-prefix", "") + name;
return TAB.getInstance().getConfiguration().essentialsNickPrefix + name;
}
});
} else {
@@ -290,7 +311,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
TAB.getInstance().getCPUManager().addPlaceholderTime(getIdentifier(), System.nanoTime()-time);
}
});
return getLastValue(p);
return lastValue.get(p.getName());
}
});
}
@@ -1,12 +1,8 @@
package me.neznamy.tab.platforms.bukkit;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bukkit.Bukkit;
@@ -25,11 +21,14 @@ import me.neznamy.tab.platforms.bukkit.features.unlimitedtags.NameTagX;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.permission.Vault;
import me.neznamy.tab.shared.Platform;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.config.ConfigurationFile;
import me.neznamy.tab.shared.features.NameTag16;
import me.neznamy.tab.shared.features.BelowName;
import me.neznamy.tab.shared.features.NameTag;
import me.neznamy.tab.shared.features.PingSpoof;
import me.neznamy.tab.shared.features.PlaceholderManager;
import me.neznamy.tab.shared.features.SpectatorFix;
import me.neznamy.tab.shared.features.TabObjective;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.None;
import me.neznamy.tab.shared.permission.PermissionPlugin;
@@ -54,6 +53,13 @@ public class BukkitPlatform implements Platform {
//nms storage
private NMSStorage nms;
//booleans to check plugin presence
public boolean placeholderAPI;
public boolean viaversion;
public boolean idisguise;
public boolean libsdisguises;
public boolean essentials;
/**
* Constructs new instance with given parameters
@@ -80,21 +86,30 @@ public class BukkitPlatform implements Platform {
@Override
public void loadFeatures() throws Exception {
int version = ProtocolVersion.SERVER_VERSION.getMinorVersion();
placeholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
viaversion = Bukkit.getPluginManager().isPluginEnabled("ViaVersion");
idisguise = Bukkit.getPluginManager().isPluginEnabled("iDisguise");
libsdisguises = Bukkit.getPluginManager().isPluginEnabled("LibsDisguises");
essentials = Bukkit.getPluginManager().isPluginEnabled("Essentials");
TAB tab = TAB.getInstance();
usedExpansions = new HashSet<String>();
tab.getPlaceholderManager().addRegistry(new BukkitPlaceholderRegistry(plugin));
tab.getPlaceholderManager().addRegistry(new UniversalPlaceholderRegistry());
tab.getPlaceholderManager().registerPlaceholders();
if (version >= 8) {
if (nms.minorVersion >= 8 && tab.getConfiguration().pipelineInjection) {
tab.getFeatureManager().registerFeature("injection", new BukkitPipelineInjector(tab, nms));
}
loadNametagFeature(tab);
loadUniversalFeatures();
if (tab.getConfiguration().BossBarEnabled && version < 9) tab.getFeatureManager().registerFeature("bossbar1.8", new BossBar_legacy(tab));
if (version >= 9 && tab.getConfiguration().config.getBoolean("fix-pet-names", false)) tab.getFeatureManager().registerFeature("petfix", new PetFix(nms));
if (tab.getConfiguration().config.getBoolean("ping-spoof.enabled", false)) tab.getFeatureManager().registerFeature("pingspoof", new PingSpoof());
if (tab.getConfiguration().config.getString("yellow-number-in-tablist", "%ping%").length() > 0) tab.getFeatureManager().registerFeature("tabobjective", new TabObjective(tab));
if (tab.getConfiguration().config.getBoolean("do-not-move-spectators", false)) tab.getFeatureManager().registerFeature("spectatorfix", new SpectatorFix());
if (tab.getConfiguration().config.getBoolean("classic-vanilla-belowname.enabled", true)) tab.getFeatureManager().registerFeature("belowname", new BelowName(tab));
if (tab.getConfiguration().premiumconfig != null && tab.getConfiguration().premiumconfig.getBoolean("scoreboard.enabled", false)) tab.getFeatureManager().registerFeature("scoreboard", new ScoreboardManager(tab));
if (tab.getConfiguration().bossbar.getBoolean("bossbar-enabled", false) && nms.minorVersion < 9) tab.getFeatureManager().registerFeature("bossbar1.8", new BossBar_legacy(tab, plugin));
if (nms.minorVersion >= 9 && tab.getConfiguration().config.getBoolean("fix-pet-names", false)) tab.getFeatureManager().registerFeature("petfix", new PetFix(nms));
if (tab.getConfiguration().config.getBoolean("per-world-playerlist.enabled", false)) tab.getFeatureManager().registerFeature("pwp", new PerWorldPlayerlist(plugin, tab));
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
if (placeholderAPI) {
new TabExpansion(plugin);
new ExpansionDownloader(plugin).download(usedExpansions);
}
@@ -110,10 +125,10 @@ public class BukkitPlatform implements Platform {
*/
private void loadNametagFeature(TAB tab) {
if (tab.getConfiguration().config.getBoolean("change-nametag-prefix-suffix", true)) {
if (tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.enabled", false) && ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 8) {
if (tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.enabled", false) && nms.minorVersion >= 8) {
tab.getFeatureManager().registerFeature("nametagx", new NameTagX(plugin, nms, tab));
} else {
tab.getFeatureManager().registerFeature("nametag16", new NameTag16(tab));
tab.getFeatureManager().registerFeature("nametag16", new NameTag(tab));
}
}
}
@@ -205,7 +220,7 @@ public class BukkitPlatform implements Platform {
@Override
public String get(TabPlayer viewer, TabPlayer target) {
if (!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) return identifier;
if (!placeholderAPI) return identifier;
try {
return PlaceholderAPI.setRelationalPlaceholders((Player) viewer.getPlayer(), (Player) target.getPlayer(), identifier);
} catch (Throwable t) {
@@ -223,7 +238,7 @@ public class BukkitPlatform implements Platform {
* @return result from PlaceholderAPI
*/
public String setPlaceholders(Player player, String placeholder) {
if (!Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) return placeholder;
if (!placeholderAPI) return placeholder;
try {
return PlaceholderAPI.setPlaceholders(player, placeholder);
} catch (Throwable t) {
@@ -233,57 +248,6 @@ public class BukkitPlatform implements Platform {
}
}
@Override
public void convertConfig(ConfigurationFile config) {
convertUniversalOptions(config);
if (config.getName().equals("config.yml")) {
removeOld(config, "nametag-refresh-interval-ticks");
removeOld(config, "tablist-refresh-interval-ticks");
removeOld(config, "header-footer-refresh-interval-ticks");
removeOld(config, "belowname.refresh-interval-ticks");
removeOld(config, "placeholders.deluxetag-yes");
removeOld(config, "placeholders.deluxetag-no");
removeOld(config, "placeholders.faction-yes");
removeOld(config, "placeholders.faction-no");
removeOld(config, "staff-groups");
removeOld(config, "use-essentials-nickname");
removeOld(config, "deluxetag-empty-value");
removeOld(config, "factions-faction");
removeOld(config, "factions-nofaction");
removeOld(config, "date-format");
removeOld(config, "time-format");
removeOld(config, "relational-placeholders-refresh");
removeOld(config, "bukkit-bridge-mode");
if (config.hasConfigOption("tablist-objective")) {
String type = config.getString("tablist-objective");
String value;
if (type.equals("NONE")) {
value = "";
} else if (type.equals("PING")){
value = "%ping%";
} else if (type.equals("HEARTS")) {
value = "%health%";
} else {
value = config.getString("tablist-objective-custom-value");
}
config.set("tablist-objective", null);
config.set("tablist-objective-custom-value", null);
config.set("yellow-number-in-tablist", value);
TAB.getInstance().print('2', "Converted old tablist-objective config option to new yellow-number-in-tablist");
}
if (config.getObject("per-world-playerlist") instanceof Boolean) {
rename(config, "per-world-playerlist", "per-world-playerlist.enabled");
rename(config, "allow-pwp-bypass-permission", "per-world-playerlist.allow-bypass-permission");
rename(config, "ignore-pwp-in-worlds", "per-world-playerlist.ignore-effect-in-worlds");
Map<String, List<String>> sharedWorlds = new HashMap<String, List<String>>();
sharedWorlds.put("lobby", Arrays.asList("lobby1", "lobby2"));
sharedWorlds.put("minigames", Arrays.asList("paintball", "bedwars"));
config.set("per-world-playerlist.shared-playerlist-world-groups", sharedWorlds);
TAB.getInstance().print('2', "Converted old per-world-playerlist section to new one in advancedconfig.yml.");
}
}
}
@Override
public String getServerVersion() {
return Bukkit.getBukkitVersion().split("-")[0] + " (" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + ")";
@@ -327,4 +291,9 @@ public class BukkitPlatform implements Platform {
public int getMaxPlayers() {
return Bukkit.getMaxPlayers();
}
@Override
public String getConfigName() {
return "bukkitconfig.yml";
}
}
@@ -1,8 +1,18 @@
package me.neznamy.tab.platforms.bukkit;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.viaversion.viaversion.api.Via;
import org.bukkit.Bukkit;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarFlag;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
@@ -14,7 +24,8 @@ import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import us.myles.ViaVersion.api.Via;
import me.neznamy.tab.shared.packets.PacketPlayOutBoss;
import me.neznamy.tab.shared.rgb.RGBUtils;
/**
* TabPlayer for Bukkit
@@ -23,12 +34,15 @@ public class BukkitTabPlayer extends ITabPlayer {
//bukkit player
private Player player;
//nms handle
private Object handle;
//nms player connection
private Object playerConnection;
//player's visible boss bars
private Map<UUID, BossBar> bossbars = new HashMap<UUID, BossBar>();
/**
* Constructs new instance with given parameter
@@ -65,7 +79,7 @@ public class BukkitTabPlayer extends ITabPlayer {
//some PS versions return -1 on unsupported server versions instead of throwing exception
if (version != -1 && version < ProtocolVersion.SERVER_VERSION.getNetworkId()) return version;
}
if (Bukkit.getPluginManager().isPluginEnabled("ViaVersion")) {
if (((BukkitPlatform)TAB.getInstance().getPlatform()).viaversion) {
return getProtocolVersionVia();
}
return ProtocolVersion.SERVER_VERSION.getNetworkId();
@@ -96,7 +110,7 @@ public class BukkitTabPlayer extends ITabPlayer {
TAB.getInstance().debug("ViaVersion returned protocol version " + version + " for " + getName());
return version;
} catch (Throwable e) {
return TAB.getInstance().getErrorManager().printError(ProtocolVersion.SERVER_VERSION.getNetworkId(), "Failed to get protocol version of " + getName() + " using ViaVersion", e);
return TAB.getInstance().getErrorManager().printError(ProtocolVersion.SERVER_VERSION.getNetworkId(), "Failed to get protocol version of " + getName() + " using ViaVersion v" + Bukkit.getPluginManager().getPlugin("ViaVersion").getDescription().getVersion() + ". Is it the latest version?", e);
}
}
@@ -120,7 +134,7 @@ public class BukkitTabPlayer extends ITabPlayer {
public void sendPacket(Object nmsPacket) {
if (nmsPacket == null || !player.isOnline()) return;
try {
if (Bukkit.getPluginManager().isPluginEnabled("ViaVersion") && nmsPacket instanceof ByteBuf) {
if (((BukkitPlatform)TAB.getInstance().getPlatform()).viaversion && nmsPacket instanceof ByteBuf) {
try {
Via.getAPI().sendRawPacket(uniqueId, (ByteBuf) nmsPacket);
} catch (IllegalArgumentException e) {
@@ -129,11 +143,71 @@ public class BukkitTabPlayer extends ITabPlayer {
}
return;
}
if (nmsPacket instanceof PacketPlayOutBoss) {
handle((PacketPlayOutBoss) nmsPacket);
return;
}
NMSStorage.getInstance().sendPacket.invoke(playerConnection, nmsPacket);
} catch (Throwable e) {
TAB.getInstance().getErrorManager().printError("An error occurred when sending " + nmsPacket.getClass().getSimpleName(), e);
}
}
private void handle(PacketPlayOutBoss packet) {
Set<BarFlag> flags = new HashSet<BarFlag>();
BossBar bar;
switch (packet.operation) {
case ADD:
if (packet.createWorldFog) flags.add(BarFlag.CREATE_FOG);
if (packet.darkenScreen) flags.add(BarFlag.DARKEN_SKY);
if (packet.playMusic) flags.add(BarFlag.PLAY_BOSS_MUSIC);
bar = Bukkit.createBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.name, getVersion().getMinorVersion() >= 16 && NMSStorage.getInstance().minorVersion >= 16),
BarColor.valueOf(packet.color.name()),
BarStyle.valueOf(packet.overlay.name().replace("PROGRESS", "SOLID").replace("NOTCHED", "SEGMENTED")),
flags.toArray(new BarFlag[0]));
bossbars.put(packet.id, bar);
bar.addPlayer(player);
break;
case REMOVE:
bossbars.get(packet.id).removePlayer(player);
bossbars.remove(packet.id);
break;
case UPDATE_PCT:
bossbars.get(packet.id).setProgress(packet.pct);
break;
case UPDATE_NAME:
bossbars.get(packet.id).setTitle(RGBUtils.getInstance().convertToBukkitFormat(packet.name, getVersion().getMinorVersion() >= 16 && NMSStorage.getInstance().minorVersion >= 16));
break;
case UPDATE_STYLE:
bar = bossbars.get(packet.id);
//yet another platform with yet another buggy bossbar API, how lovely it is regressing back to APIs instead of internals
bar.removePlayer(player);
bar.setColor(BarColor.valueOf(packet.color.name()));
bar.setStyle(BarStyle.valueOf(packet.overlay.name().replace("PROGRESS", "SOLID").replace("NOTCHED", "SEGMENTED")));
bar.addPlayer(player);
break;
case UPDATE_PROPERTIES:
bar = bossbars.get(packet.id);
processFlag(bar, packet.createWorldFog, BarFlag.CREATE_FOG);
processFlag(bar, packet.darkenScreen, BarFlag.DARKEN_SKY);
processFlag(bar, packet.playMusic, BarFlag.PLAY_BOSS_MUSIC);
break;
default:
break;
}
}
private void processFlag(BossBar bar, boolean targetValue, BarFlag flag) {
if (targetValue) {
if (!bar.hasFlag(flag)) {
bar.addFlag(flag);
}
} else {
if (bar.hasFlag(flag)) {
bar.removeFlag(flag);
}
}
}
@Override
public boolean hasInvisibilityPotion() {
@@ -151,7 +225,7 @@ public class BukkitTabPlayer extends ITabPlayer {
*/
private boolean isDisguisedLD() {
try {
if (!Bukkit.getPluginManager().isPluginEnabled("LibsDisguises")) return false;
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).libsdisguises) return false;
return (boolean) Class.forName("me.libraryaddict.disguise.DisguiseAPI").getMethod("isDisguised", Entity.class).invoke(null, player);
} catch (Throwable e) {
return TAB.getInstance().getErrorManager().printError(false, "Failed to check disguise status using LibsDisguises", e);
@@ -164,7 +238,7 @@ public class BukkitTabPlayer extends ITabPlayer {
*/
private boolean isDisguisediDis() {
try {
if (!Bukkit.getPluginManager().isPluginEnabled("iDisguise")) return false;
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).idisguise) return false;
RegisteredServiceProvider<?> provider = Bukkit.getServicesManager().getRegistration(Class.forName("de.robingrether.idisguise.api.DisguiseAPI"));
Object iDisguise = provider.getProvider();
Method m = iDisguise.getClass().getMethod("isDisguised", Player.class);
@@ -193,4 +267,28 @@ public class BukkitTabPlayer extends ITabPlayer {
public boolean isOnline() {
return player.isOnline();
}
@Override
public boolean isVanished() {
try {
if (((BukkitPlatform)TAB.getInstance().getPlatform()).essentials) {
Object essentials = Bukkit.getPluginManager().getPlugin("Essentials");
Object user = essentials.getClass().getMethod("getUser", Player.class).invoke(essentials, player);
boolean vanished = (boolean) user.getClass().getMethod("isVanished").invoke(user);
if (vanished) return true;
}
if (player.hasMetadata("vanished") && !player.getMetadata("vanished").isEmpty()) {
return player.getMetadata("vanished").get(0).asBoolean();
}
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to check vanish status of " + player.getName(), e);
}
return false;
}
@SuppressWarnings("deprecation")
@Override
public int getGamemode() {
return player.getGameMode().getValue();
}
}
@@ -1,5 +1,6 @@
package me.neznamy.tab.platforms.bukkit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -7,6 +8,7 @@ import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
@@ -35,18 +37,30 @@ public class Main extends JavaPlugin {
sender.sendMessage(message.replace('&', '\u00a7'));
}
} else {
TAB.getInstance().command.execute(sender instanceof Player ? TAB.getInstance().getPlayer(((Player)sender).getUniqueId()) : null, args);
TabPlayer p = null;
if (sender instanceof Player) {
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
if (p == null) return false; //player not loaded correctly
}
TAB.getInstance().command.execute(p, args);
}
return false;
});
Bukkit.getPluginCommand("tab").setTabCompleter((sender, c, cmd, args) -> TAB.getInstance().command.complete(sender instanceof Player ? TAB.getInstance().getPlayer(((Player)sender).getUniqueId()) : null, args));
Bukkit.getPluginCommand("tab").setTabCompleter((sender, c, cmd, args) -> {
TabPlayer p = null;
if (sender instanceof Player) {
p = TAB.getInstance().getPlayer(((Player)sender).getUniqueId());
if (p == null) return new ArrayList<String>(); //player not loaded correctly
}
return TAB.getInstance().command.complete(p, args);
});
TAB.getInstance().load();
new BukkitMetrics(this);
}
@Override
public void onDisable() {
//null check due to L22 return making L27 not run
//null check due to L23 return making L30 not run
if (TAB.getInstance() != null) TAB.getInstance().unload();
}
@@ -56,9 +70,10 @@ public class Main extends JavaPlugin {
*/
private boolean isVersionSupported(){
List<String> SUPPORTED_VERSIONS = Arrays.asList(
"v1_5_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3",
"v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2", "v1_8_R3",
"v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1", "v1_13_R1", "v1_13_R2",
"v1_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3");
"v1_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1");
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
try {
NMSStorage.setInstance(new NMSStorage());
@@ -0,0 +1,87 @@
package me.neznamy.tab.platforms.bukkit.features;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.BukkitPacketBuilder;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.cpu.UsageType;
import me.neznamy.tab.shared.features.bossbar.BossBar;
import me.neznamy.tab.shared.features.types.Loadable;
/**
* An additional class with additional code for <1.9 servers due to an entity being required
*/
public class BossBar_legacy implements Listener, Loadable {
//distance of wither in blocks
private final int WITHER_DISTANCE = 75;
//main bossbar feature
private BossBar mainFeature;
/**
* Constructs a new instance of the class
* @param mainFeature - main bossbar feature
*/
public BossBar_legacy(TAB tab, JavaPlugin plugin) {
this.mainFeature = (BossBar) tab.getFeatureManager().getFeature("bossbar");
Bukkit.getPluginManager().registerEvents(this, plugin);
//bar disappears in client after ~1 second of not seeing boss entity
tab.getCPUManager().startRepeatingMeasuredTask(900, "refreshing bossbar", TabFeature.BOSSBAR, UsageType.TELEPORTING_ENTITY, () -> teleport());
}
@Override
public void load() {
teleport();
}
/**
* Updates wither location for all players
*/
private void teleport() {
for (TabPlayer all : TAB.getInstance().getPlayers()) {
if (all.getVersion().getMinorVersion() > 8) continue; //sending VV packets to those
for (me.neznamy.tab.api.bossbar.BossBar l : all.getActiveBossBars()) {
try {
all.sendPacket(((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).buildEntityTeleportPacket(Math.abs(l.getUniqueId().hashCode()), getWitherLocation(all)), TabFeature.BOSSBAR);
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to create PacketPlayOutEntityTeleport", e);
}
}
}
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
}
/**
* Respawning wither as respawn screen destroys all entities in client
*/
@EventHandler
public void onRespawn(PlayerRespawnEvent e) {
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerRespawnEvent", TabFeature.BOSSBAR, UsageType.PLAYER_RESPAWN_EVENT, () -> mainFeature.detectBossBarsAndSend(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
}
/**
* Returns location where wither should be located based on where player is looking
* @param p - player to get wither location for
* @return location of wither
*/
public Location getWitherLocation(TabPlayer p) {
Player pl = (Player) p.getPlayer();
Location loc = pl.getEyeLocation().add(pl.getEyeLocation().getDirection().normalize().multiply(WITHER_DISTANCE));
if (loc.getY() < 1) loc.setY(1);
return loc;
}
}
@@ -46,8 +46,8 @@ public class ExpansionDownloader {
@Override
public void run() {
try {
//initial delay to give PlaceholderAPI time to load
Thread.sleep(5000);
//initial delay to give PlaceholderAPI & plugins with expansions time to load
Thread.sleep(10000);
TAB.getInstance().debug("Used expansions: " + expansions.toString());
TAB.getInstance().debug("Registered expansions in PlaceholderAPI: " + PlaceholderAPI.getRegisteredIdentifiers());
expansions.removeAll(PlaceholderAPI.getRegisteredIdentifiers());
@@ -0,0 +1,107 @@
package me.neznamy.tab.platforms.bukkit.features;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.cpu.UsageType;
import me.neznamy.tab.shared.features.types.Loadable;
/**
* Per-world-playerlist feature handler
*/
@SuppressWarnings("deprecation")
public class PerWorldPlayerlist implements Loadable, Listener {
//plugin instance
private JavaPlugin plugin;
//config options
private boolean allowBypass;
private List<String> ignoredWorlds;
private Map<String, List<String>> sharedWorlds;
/**
* Constructs new instance with given parameters and loads config options
* @param plugin - plugin instance
* @param tab - tab instance
*/
public PerWorldPlayerlist(JavaPlugin plugin, TAB tab) {
this.plugin = plugin;
allowBypass = tab.getConfiguration().config.getBoolean("per-world-playerlist.allow-bypass-permission", false);
ignoredWorlds = tab.getConfiguration().config.getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
sharedWorlds = tab.getConfiguration().config.getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
for (String group : sharedWorlds.keySet()) {
if (sharedWorlds.get(group).size() == 1) {
tab.getErrorManager().startupWarn("World group \"" + group + "\" in per-world-playerlist only contain a single world (\"" + sharedWorlds.get(group).get(0) +
"\"), which has no effect and only makes config less readable. Delete the group entirely for a cleaner config.");
}
}
tab.debug(String.format("Loaded PerWorldPlayerlist feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
Bukkit.getPluginManager().registerEvents(this, plugin);
}
@Override
public void load(){
Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getOnlinePlayers().forEach(p -> checkPlayer(p)));
}
@Override
public void unload(){
for (Player p : Bukkit.getOnlinePlayers()) {
for (Player pl : Bukkit.getOnlinePlayers()) {
p.showPlayer(pl);
}
}
HandlerList.unregisterAll(this);
}
@EventHandler
public void onJoin(PlayerJoinEvent e) {
long time = System.nanoTime();
checkPlayer(e.getPlayer());
TAB.getInstance().getCPUManager().addTime(TabFeature.PER_WORLD_PLAYERLIST, UsageType.PLAYER_JOIN_EVENT, System.nanoTime()-time);
}
@EventHandler
public void onWorldChange(PlayerChangedWorldEvent e) {
long time = System.nanoTime();
checkPlayer(e.getPlayer());
TAB.getInstance().getCPUManager().addTime(TabFeature.PER_WORLD_PLAYERLIST, UsageType.WORLD_SWITCH_EVENT, System.nanoTime()-time);
}
private void checkPlayer(Player p) {
for (Player all : Bukkit.getOnlinePlayers()){
if (all == p) continue;
if (!shouldSee(p, all) && p.canSee(all)) p.hidePlayer(all);
if (shouldSee(p, all) && !p.canSee(all)) p.showPlayer(all);
if (!shouldSee(all, p) && all.canSee(p)) all.hidePlayer(p);
if (shouldSee(all, p) && !all.canSee(p)) all.showPlayer(p);
}
}
private boolean shouldSee(Player viewer, Player target) {
if (target == viewer) return true;
if ((allowBypass && viewer.hasPermission("tab.bypass")) || ignoredWorlds.contains(viewer.getWorld().getName())) return true;
String viewerWorldGroup = viewer.getWorld().getName() + "-default"; //preventing unwanted behavior when some group is called exactly like a world
String targetWorldGroup = target.getWorld().getName() + "-default";
for (String group : sharedWorlds.keySet()) {
if (sharedWorlds.get(group) != null) {
if (sharedWorlds.get(group).contains(viewer.getWorld().getName())) viewerWorldGroup = group;
if (sharedWorlds.get(group).contains(target.getWorld().getName())) targetWorldGroup = group;
}
}
return viewerWorldGroup.equals(targetWorldGroup);
}
}
@@ -9,7 +9,7 @@ import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
import me.neznamy.tab.shared.features.types.packet.RawPacketListener;
@@ -22,7 +22,7 @@ import me.neznamy.tab.shared.features.types.packet.RawPacketListener;
public class PetFix implements RawPacketListener, QuitEventListener {
//datawatcher position of pet owner field
private int PET_OWNER_POSITION = getPetOwnerPosition();
private int PET_OWNER_POSITION;
//logger of last interacts to prevent feature not working on 1.16
private Map<String, Long> lastInteractFix = new HashMap<String, Long>();
@@ -36,6 +36,8 @@ public class PetFix implements RawPacketListener, QuitEventListener {
*/
public PetFix(NMSStorage nms) {
this.nms = nms;
PET_OWNER_POSITION = getPetOwnerPosition();
TAB.getInstance().debug("Loaded PetFix feature");
}
/**
@@ -43,13 +45,16 @@ public class PetFix implements RawPacketListener, QuitEventListener {
* @return position of pet owner field based on server version
*/
private int getPetOwnerPosition() {
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 15) {
if (nms.minorVersion >= 17) {
//1.17.x
return 18;
} else if (nms.minorVersion >= 15) {
//1.15.x, 1.16.x
return 17;
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 14) {
} else if (nms.minorVersion >= 14) {
//1.14.x
return 16;
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 10) {
} else if (nms.minorVersion >= 10) {
//1.10.x - 1.13.x
return 14;
} else {
@@ -87,16 +92,19 @@ public class PetFix implements RawPacketListener, QuitEventListener {
if (items == null) return;
List<Object> newList = new ArrayList<Object>();
for (Object item : items) {
DataWatcherItem i = DataWatcherItem.fromNMS(item);
if (i.type.position == PET_OWNER_POSITION && i.value.toString().startsWith("Optional")) continue;
if (nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item)) == PET_OWNER_POSITION) {
Object value = nms.DataWatcherItem_VALUE.get(item);
if (value.getClass().getSimpleName().equals("Optional")) continue;
}
newList.add(item);
}
nms.PacketPlayOutEntityMetadata_LIST.set(packet, newList);
}
//<1.15
if (nms.PacketPlayOutSpawnEntityLiving.isInstance(packet) && nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER != null) {
DataWatcher watcher = DataWatcher.fromNMS(nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.get(packet));
DataWatcherItem petOwner = watcher.getItem(PET_OWNER_POSITION);
if (petOwner != null && petOwner.value.toString().startsWith("Optional")) {
if (petOwner != null && petOwner.value.getClass().getSimpleName().equals("Optional")) {
watcher.removeValue(PET_OWNER_POSITION);
nms.PacketPlayOutSpawnEntityLiving_DATAWATCHER.set(packet, watcher.toNMS());
}
@@ -60,13 +60,13 @@ public class TabExpansion extends PlaceholderExpansion {
if (player == null) return "";
TabPlayer p = TAB.getInstance().getPlayer(player.getUniqueId());
if (identifier.equals("scoreboard_visible")) {
return p.isScoreboardVisible() ? "Enabled" : "Disabled";
return translate(p.isScoreboardVisible());
}
if (identifier.equals("bossbar_visible")) {
return p.hasBossbarVisible() ? "Enabled" : "Disabled";
return translate(p.hasBossbarVisible());
}
if (identifier.equals("ntpreview")) {
return p.isPreviewingNametag() ? "Enabled" : "Disabled";
return translate(p.isPreviewingNametag());
}
if (identifier.startsWith("replace_")) {
return findReplacement("%" + identifier.substring(8) + "%", player);
@@ -78,6 +78,10 @@ public class TabExpansion extends PlaceholderExpansion {
return getProperty(identifier, p);
}
private String translate(boolean b) {
return b ? "Enabled" : "Disabled";
}
/**
* Find replacement for specified placeholder
* @param placeholder - placeholder to find replacement for
@@ -10,6 +10,7 @@ import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Pose;
import org.bukkit.potion.PotionEffectType;
import me.neznamy.tab.api.ArmorStand;
@@ -171,7 +172,7 @@ public class BukkitArmorStand implements ArmorStand {
if (this.sneaking == sneaking) return; //idk
this.sneaking = sneaking;
for (TabPlayer viewer : getNearbyPlayers()) {
if (viewer.getVersion().getMinorVersion() == 14 && !(boolean) TAB.getInstance().getConfiguration().getSecretOption("unlimited-nametag-prefix-suffix-mode.always-visible", false)) {
if (viewer.getVersion().getMinorVersion() == 14 && !TAB.getInstance().getConfiguration().armorStandsAlwaysVisible) {
//1.14.x client sided bug, despawning completely
if (sneaking) {
viewer.sendPacket(getDestroyPacket(), TabFeature.NAMETAGX);
@@ -275,7 +276,7 @@ public class BukkitArmorStand implements ArmorStand {
*/
private boolean getVisibility() {
if (((BukkitTabPlayer)owner).isDisguised() || (((NameTagX)TAB.getInstance().getFeatureManager().getFeature("nametagx")).playersOnBoats.contains(owner))) return false;
if ((boolean) TAB.getInstance().getConfiguration().getSecretOption("unlimited-nametag-prefix-suffix-mode.always-visible", false)) return true;
if (TAB.getInstance().getConfiguration().armorStandsAlwaysVisible) return true;
return !player.hasPotionEffect(PotionEffectType.INVISIBILITY) && player.getGameMode() != GameMode.SPECTATOR && !owner.hasHiddenNametag() && property.get().length() > 0;
}
@@ -304,28 +305,33 @@ public class BukkitArmorStand implements ArmorStand {
* @return correct player's Y
*/
private double getY() {
//1.14+ bukkit api bug
//1.14+ server sided bug
Entity vehicle = player.getVehicle();
if (vehicle != null) {
if (vehicle.getType() == EntityType.HORSE) {
if (vehicle.getType().toString().contains("HORSE")) { //covering all 3 horse types
return vehicle.getLocation().getY() + 0.85;
}
if (vehicle.getType().toString().equals("DONKEY")) {
if (vehicle.getType() == EntityType.DONKEY) {
return vehicle.getLocation().getY() + 0.525;
}
if (vehicle.getType() == EntityType.PIG) {
return vehicle.getLocation().getY() + 0.325;
}
if (vehicle.getType().toString().equals("STRIDER")) { //preventing errors on <1.16
if (vehicle.getType().toString().equals("STRIDER")) { //added in 1.16
return vehicle.getLocation().getY() + 1.15;
}
}
//1.13+ swimming
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 13 && player.isSwimming()) {
return player.getLocation().getY()-1.22;
}
//1.13+ swimming or 1.9+ flying with elytra
if (isSwimming() || (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9 && player.isGliding())) {
return player.getLocation().getY()-1.22;
}
return player.getLocation().getY();
}
private boolean isSwimming() {
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
return ProtocolVersion.SERVER_VERSION.getMinorVersion() == 13 && player.isSwimming();
}
@Override
public int getEntityId() {
@@ -0,0 +1,55 @@
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.cpu.UsageType;
/**
* The event listener part for securing proper functionality of armor stands
*/
public class EventListener implements Listener {
//the nametag feature handler
private NameTagX feature;
/**
* Constructs new instance with given parameters
* @param feature - nametagx feature handler
*/
public EventListener(NameTagX feature) {
this.feature = feature;
}
/**
* Sneak event listener to despawn & 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.isDisabledWorld(p.getWorldName()) || feature.isDisabledWorld(feature.disabledUnlimitedWorlds, p.getWorldName())) return;
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerToggleSneakEvent", TabFeature.NAMETAGX, UsageType.PLAYER_TOGGLE_SNEAK_EVENT, () -> {
if (p.getArmorStandManager() != null) p.getArmorStandManager().sneak(e.isSneaking());
});
}
/**
* Respawning armor stands as respawn screen destroys all entities in client
* @param e - respawn event
*/
@EventHandler
public void onRespawn(PlayerRespawnEvent e) {
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerRespawnEvent", TabFeature.NAMETAGX, UsageType.PLAYER_RESPAWN_EVENT, () -> {
TabPlayer respawned = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
if (feature.isDisabledWorld(respawned.getWorldName()) || feature.isDisabledWorld(feature.disabledUnlimitedWorlds, respawned.getWorldName())) return;
respawned.getArmorStandManager().teleport();
});
}
}
@@ -3,9 +3,11 @@ package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
@@ -16,8 +18,6 @@ import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.java.JavaPlugin;
import com.google.common.collect.Lists;
import me.neznamy.tab.api.ArmorStand;
import me.neznamy.tab.api.ArmorStandManager;
import me.neznamy.tab.api.TabPlayer;
@@ -28,30 +28,20 @@ import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.cpu.UsageType;
import me.neznamy.tab.shared.features.NameTag;
import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
import me.neznamy.tab.shared.features.types.event.RespawnEventListener;
import me.neznamy.tab.shared.features.types.event.SneakEventListener;
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
/**
* The core class for unlimited nametag mode
*/
public class NameTagX extends NameTag implements Loadable, JoinEventListener, QuitEventListener, WorldChangeListener, RespawnEventListener, SneakEventListener {
public class NameTagX extends NameTag {
//entity tracking range
public double entityTrackingRange;
//if using marker tag for 1.8.x clients or not
//config options
public boolean markerFor18x;
//if disable feature on boats or not
public boolean disableOnBoats;
//space between lines in blocks
private double spaceBetweenLines;
//list of worlds with unlimited nametag mode disabled
protected List<String> disabledUnlimitedWorlds;
//list of defined dynamic lines
public List<String> dynamicLines = Arrays.asList("belowname", "nametag", "abovename");
@@ -72,6 +62,11 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
//list of players currently on boats
public List<TabPlayer> playersOnBoats = new ArrayList<TabPlayer>();
//list of players currently in a vehicle
private Map<TabPlayer, Entity> playersInVehicle = new ConcurrentHashMap<TabPlayer, Entity>();
private Map<TabPlayer, Location> playerLocations = new ConcurrentHashMap<TabPlayer, Location>();
/**
* Constructs new instance with given parameters and loads config options
@@ -84,7 +79,7 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
markerFor18x = tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.use-marker-tag-for-1-8-x-clients", false);
disableOnBoats = tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.disable-on-boats", true);
spaceBetweenLines = tab.getConfiguration().config.getDouble("unlimited-nametag-prefix-suffix-mode.space-between-lines", 0.22);
entityTrackingRange = tab.getConfiguration().config.getDouble("unlimited-nametag-prefix-suffix-mode.tracking-range", 48);
disabledUnlimitedWorlds = tab.getConfiguration().config.getStringList("disable-features-in-worlds.unlimited-nametags", Arrays.asList("disabledworld"));
if (tab.getConfiguration().premiumconfig != null) {
List<String> realList = tab.getConfiguration().premiumconfig.getStringList("unlimited-nametag-mode-dynamic-lines", Arrays.asList("abovename", "nametag", "belowname", "another"));
dynamicLines = new ArrayList<String>();
@@ -96,28 +91,31 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
eventListener = new EventListener(this);
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
tab.getFeatureManager().registerFeature("nametagx-packet", new PacketListener(this, nms, tab));
tab.debug(String.format("Loaded Unlimited nametag feature with parameters markerFor18x=%s, disableOnBoats=%s, spaceBetweenLines=%s, disabledUnlimitedWorlds=%s",
markerFor18x, disableOnBoats, spaceBetweenLines, disabledUnlimitedWorlds));
}
@Override
public void load() {
for (TabPlayer all : tab.getPlayers()){
super.load();
for (TabPlayer all : tab.getPlayers()) {
entityIdMap.put(((Player) all.getPlayer()).getEntityId(), all);
all.setTeamName(sorting.getTeamName(all));
updateProperties(all);
collision.put(all, true);
loadArmorStands(all);
if (isDisabledWorld(all.getWorldName())) continue;
registerTeam(all);
if (isDisabledWorld(all.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, all.getWorldName())) continue;
loadPassengers(all);
for (TabPlayer viewer : tab.getPlayers()) {
spawnArmorStands(all, viewer, false);
}
}
startRefreshingTasks();
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing nametag visibility", getFeatureType(), UsageType.REFRESHING_NAMETAG_VISIBILITY, new Runnable() {
startVisibilityRefreshTask();
startVehicleTickingTask();
}
private void startVisibilityRefreshTask() {
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing nametag visibility", getFeatureType(), UsageType.REFRESHING_NAMETAG_VISIBILITY_AND_COLLISION, new Runnable() {
public void run() {
for (TabPlayer p : tab.getPlayers()) {
if (!p.isLoaded() || isDisabledWorld(p.getWorldName())) continue;
if (!p.isLoaded() || isDisabledWorld(p.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, p.getWorldName())) continue;
p.getArmorStandManager().updateVisibility(false);
if (!disableOnBoats) continue;
boolean onBoat = ((Player)p.getPlayer()).getVehicle() != null && ((Player)p.getPlayer()).getVehicle().getType() == EntityType.BOAT;
@@ -136,12 +134,70 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
}
});
}
private void startVehicleTickingTask() {
tab.getCPUManager().startRepeatingMeasuredTask(100, "ticking vehicles", TabFeature.NAMETAGX, UsageType.TICKING_VEHICLES, () -> {
for (TabPlayer p : TAB.getInstance().getPlayers()) {
if (!p.isLoaded()) continue;
if (isDisabledWorld(p.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, p.getWorldName())) {
playersInVehicle.remove(p);
playerLocations.remove(p);
continue;
}
processVehicles(p);
if (!playerLocations.containsKey(p) || !playerLocations.get(p).equals(((Player)p.getPlayer()).getLocation())) {
playerLocations.put(p, ((Player)p.getPlayer()).getLocation());
processPassengers((Entity) p.getPlayer());
//also updating position if player is previewing since we're here as the code would be same if we want to avoid listening to move event
if (p.isPreviewingNametag() && p.getArmorStandManager() != null) {
p.getArmorStandManager().teleport(p);
}
}
}
});
}
/**
* Checks for vehicle changes of player and sends packets if needed
* @param p - player to check
*/
private void processVehicles(TabPlayer p) {
Entity vehicle = ((Player)p.getPlayer()).getVehicle();
if (playersInVehicle.containsKey(p) && vehicle == null) {
//vehicle exit
vehicles.remove(playersInVehicle.get(p).getEntityId());
p.getArmorStandManager().teleport();
playersInVehicle.remove(p);
}
if (!playersInVehicle.containsKey(p) && vehicle != null) {
//vehicle enter
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
p.getArmorStandManager().teleport();
playersInVehicle.put(p, vehicle);
}
}
/**
* Teleports armor stands of all passengers on specified vehicle
* @param vehicle - entity to check passengers of
*/
private void processPassengers(Entity vehicle) {
for (Entity passenger : getPassengers(vehicle)) {
if (passenger instanceof Player) {
TabPlayer pl = TAB.getInstance().getPlayer(passenger.getUniqueId());
pl.getArmorStandManager().teleport();
} else {
processPassengers(passenger);
}
}
}
@Override
public void unload() {
super.unload();
HandlerList.unregisterAll(eventListener);
for (TabPlayer p : tab.getPlayers()) {
if (!isDisabledWorld(p.getWorldName())) unregisterTeam(p);
if (p.getArmorStandManager() != null) p.getArmorStandManager().destroy();
}
entityIdMap.clear();
@@ -149,18 +205,10 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
@Override
public void onJoin(TabPlayer connectedPlayer) {
super.onJoin(connectedPlayer);
entityIdMap.put(((Player) connectedPlayer.getPlayer()).getEntityId(), connectedPlayer);
connectedPlayer.setTeamName(sorting.getTeamName(connectedPlayer));
updateProperties(connectedPlayer);
collision.put(connectedPlayer, true);
for (TabPlayer all : tab.getPlayers()) {
if (!all.isLoaded()) continue; //avoiding NPE when 2 players join at once
if (all == connectedPlayer) continue;
if (!isDisabledWorld(all.getWorldName())) registerTeam(all, connectedPlayer);
}
loadArmorStands(connectedPlayer);
if (isDisabledWorld(connectedPlayer.getWorldName())) return;
registerTeam(connectedPlayer);
if (isDisabledWorld(connectedPlayer.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, connectedPlayer.getWorldName())) return;
loadPassengers(connectedPlayer);
for (TabPlayer viewer : tab.getPlayers()) {
spawnArmorStands(connectedPlayer, viewer, true);
@@ -191,40 +239,26 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
if (!isDisabledWorld(disconnectedPlayer.getWorldName())) unregisterTeam(disconnectedPlayer);
invisiblePlayers.remove(disconnectedPlayer.getName());
collision.remove(disconnectedPlayer);
super.onQuit(disconnectedPlayer);
for (TabPlayer all : tab.getPlayers()) {
if (all.getArmorStandManager() != null) all.getArmorStandManager().unregisterPlayer(disconnectedPlayer);
}
for (TabPlayer all : tab.getPlayers()) {
if (all == disconnectedPlayer) continue;
all.showNametag(disconnectedPlayer.getUniqueId()); //clearing memory from API method
}
entityIdMap.remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
eventListener.onQuit(disconnectedPlayer);
playersInVehicle.remove(disconnectedPlayer);
playerLocations.remove(disconnectedPlayer);
tab.getCPUManager().runTaskLater(100, "processing onQuit", getFeatureType(), UsageType.PLAYER_QUIT_EVENT, () -> disconnectedPlayer.getArmorStandManager().destroy());
}
@Override
public void onWorldChange(TabPlayer p, String from, String to) {
if (!p.isLoaded()) return;
updateProperties(p);
super.onWorldChange(p, from, to);
Set<TabPlayer> nearby = p.getArmorStandManager().getNearbyPlayers();
p.getArmorStandManager().destroy();
loadArmorStands(p);
loadPassengers(p);
for (TabPlayer viewer : tab.getPlayers()) {
spawnArmorStands(p, viewer, true);
viewer.getArmorStandManager().destroy(p);
}
if (isDisabledWorld(p.getWorldName()) && !isDisabledWorld(disabledWorlds, from)) {
unregisterTeam(p);
} else if (!isDisabledWorld(p.getWorldName()) && isDisabledWorld(disabledWorlds, from)) {
registerTeam(p);
} else {
updateTeam(p);
p.getArmorStandManager().refresh();
if (nearby.contains(viewer) && to.equals(viewer.getWorldName())) spawnArmorStands(p, viewer, true);
}
}
@@ -237,9 +271,11 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
private void spawnArmorStands(TabPlayer owner, TabPlayer viewer, boolean sendMutually) {
if (owner == viewer) return; //not displaying own armorstands
if (((Player) viewer.getPlayer()).getWorld() != ((Player) owner.getPlayer()).getWorld()) return; //different world
if (getDistance(viewer, owner) <= entityTrackingRange) {
if (((Player)viewer.getPlayer()).canSee((Player)owner.getPlayer())) owner.getArmorStandManager().spawn(viewer);
if (sendMutually && viewer.getArmorStandManager() != null && ((Player)owner.getPlayer()).canSee((Player)viewer.getPlayer())) viewer.getArmorStandManager().spawn(owner);
if (isDisabledWorld(owner.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, owner.getWorldName())) return;
if (getDistance(viewer, owner) <= 48) {
if (((Player)viewer.getPlayer()).canSee((Player)owner.getPlayer()) && !owner.isVanished()) owner.getArmorStandManager().spawn(viewer);
if (sendMutually && viewer.getArmorStandManager() != null && ((Player)owner.getPlayer()).canSee((Player)viewer.getPlayer())
&& !viewer.isVanished()) viewer.getArmorStandManager().spawn(owner);
}
}
@@ -259,7 +295,7 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
for (Entry<String, Object> line : staticLines.entrySet()) {
Property p = pl.getProperty(line.getKey());
if (p.getCurrentRawValue().length() == 0) continue;
pl.getArmorStandManager().addArmorStand(line.getKey(), new BukkitArmorStand(idCounter++, pl, p, Double.parseDouble(line.getValue()+""), true));
pl.getArmorStandManager().addArmorStand(line.getKey(), new BukkitArmorStand(idCounter++, pl, p, Double.parseDouble(line.getValue().toString()), true));
}
fixArmorStandHeights(pl);
}
@@ -282,24 +318,12 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
@Override
public void refresh(TabPlayer refreshed, boolean force) {
if (isDisabledWorld(refreshed.getWorldName())) return;
boolean refresh;
if (force) {
updateProperties(refreshed);
refresh = true;
} else {
boolean prefix = refreshed.getProperty("tagprefix").update();
boolean suffix = refreshed.getProperty("tagsuffix").update();
refresh = prefix || suffix;
}
if (refresh) updateTeam(refreshed);
super.refresh(refreshed, force);
if (isDisabledWorld(refreshed.getWorldName()) || isDisabledWorld(disabledUnlimitedWorlds, refreshed.getWorldName())) return;
if (force) {
refreshed.getArmorStandManager().destroy();
loadArmorStands(refreshed);
if (((Entity) refreshed.getPlayer()).getVehicle() != null) {
Entity vehicle = ((Entity) refreshed.getPlayer()).getVehicle();
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
}
loadPassengers(refreshed);
for (TabPlayer viewer : tab.getPlayers()) {
if (viewer == refreshed) continue;
if (viewer.getWorldName().equals(refreshed.getWorldName())) {
@@ -322,10 +346,9 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
* Updates raw values of properties for specified player
* @param p - player to update
*/
private void updateProperties(TabPlayer p) {
p.loadPropertyFromConfig("tagprefix");
public void updateProperties(TabPlayer p) {
super.updateProperties(p);
p.loadPropertyFromConfig("customtagname", p.getName());
p.loadPropertyFromConfig("tagsuffix");
p.setProperty("nametag", p.getProperty("tagprefix").getCurrentRawValue() + p.getProperty("customtagname").getCurrentRawValue() + p.getProperty("tagsuffix").getCurrentRawValue());
for (String property : dynamicLines) {
if (!property.equals("nametag")) p.loadPropertyFromConfig(property);
@@ -346,7 +369,7 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
return vehicle.getPassengers();
} else {
if (vehicle.getPassenger() != null) {
return Lists.newArrayList(vehicle.getPassenger());
return Arrays.asList(vehicle.getPassenger());
} else {
return new ArrayList<Entity>();
}
@@ -355,13 +378,18 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
@Override
public void refreshUsedPlaceholders() {
usedPlaceholders = tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive("tagprefix", "customtagname", "tagsuffix");
usedPlaceholders = new HashSet<>(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive("tagprefix", "customtagname", "tagsuffix"));
for (String line : dynamicLines) {
usedPlaceholders.addAll(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive(line));
}
for (String line : staticLines.keySet()) {
usedPlaceholders.addAll(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive(line));
}
for (TabPlayer p : tab.getPlayers()) {
usedPlaceholders.addAll(tab.getPlaceholderManager().getUsedPlaceholderIdentifiersRecursive(p.getProperty("tagprefix").getCurrentRawValue(),
p.getProperty("customtagname").getCurrentRawValue(), p.getProperty("tagsuffix").getCurrentRawValue(),
p.getProperty("abovename").getCurrentRawValue(), p.getProperty("belowname").getCurrentRawValue()));
}
}
@Override
@@ -369,20 +397,9 @@ public class NameTagX extends NameTag implements Loadable, JoinEventListener, Qu
return TabFeature.NAMETAGX;
}
@Override
public void onRespawn(TabPlayer respawned) {
if (isDisabledWorld(respawned.getWorldName())) return;
respawned.getArmorStandManager().teleport();
}
@Override
public void onSneak(TabPlayer player, boolean isSneaking) {
if (isDisabledWorld(player.getWorldName())) return;
player.getArmorStandManager().sneak(isSneaking);
}
@Override
public boolean getTeamVisibility(TabPlayer p, TabPlayer viewer) {
return playersOnBoats.contains(p);
//only visible if player is on boat & config option is enabled and player is not invisible (1.8 bug) or feature is disabled
return (playersOnBoats.contains(p) && !invisiblePlayers.contains(p.getName())) || isDisabledWorld(disabledUnlimitedWorlds, p.getWorldName());
}
}
@@ -10,17 +10,13 @@ import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.cpu.UsageType;
import me.neznamy.tab.shared.features.types.packet.PlayerInfoPacketListener;
import me.neznamy.tab.shared.features.types.packet.RawPacketListener;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
/**
* The packet listening part for securing proper functionality of armor stands
* Bukkit events are too unreliable and delayed/ahead which causes desync
*/
public class PacketListener implements RawPacketListener, PlayerInfoPacketListener {
public class PacketListener implements RawPacketListener {
//main feature
private NameTagX nameTagX;
@@ -30,9 +26,6 @@ public class PacketListener implements RawPacketListener, PlayerInfoPacketListen
//tab instance
private TAB tab;
//if modifying npc names or not
private boolean modifyNPCnames;
/**
* Constructs new instance with given parameters and loads config options
@@ -44,7 +37,6 @@ public class PacketListener implements RawPacketListener, PlayerInfoPacketListen
this.nameTagX = nameTagX;
this.nms = nms;
this.tab = tab;
modifyNPCnames = tab.getConfiguration().config.getBoolean("unlimited-nametag-prefix-suffix-mode.modify-npc-names", false);
}
@Override
@@ -68,9 +60,28 @@ public class PacketListener implements RawPacketListener, PlayerInfoPacketListen
@Override
public void onPacketSend(TabPlayer receiver, Object packet) throws Throwable {
if (nms.PacketPlayOutEntity.isInstance(packet) && !packet.getClass().getSimpleName().equals("PacketPlayOutEntityLook")) {
if (receiver.getVersion().getMinorVersion() < 8) return;
//using bukkit player to check world due to old data on world change due to asynchronous processing & world name changing
String world = ((Player)receiver.getPlayer()).getWorld().getName();
if (!receiver.isLoaded() || nameTagX.isDisabledWorld(world) || nameTagX.isDisabledWorld(nameTagX.disabledUnlimitedWorlds, world)) return;
if (nms.PacketPlayOutEntity.isInstance(packet) && !nms.PacketPlayOutEntityLook.isInstance(packet)) { //ignoring head rotation only packets
onEntityMove(receiver, nms.PacketPlayOutEntity_ENTITYID.getInt(packet));
}
if (nms.PacketPlayOutEntityTeleport.isInstance(packet)) {
onEntityMove(receiver, nms.PacketPlayOutEntityTeleport_ENTITYID.getInt(packet));
}
if (nms.PacketPlayOutNamedEntitySpawn.isInstance(packet)) {
onEntitySpawn(receiver, nms.PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(packet));
}
if (nms.PacketPlayOutEntityDestroy.isInstance(packet)) {
if (nms.minorVersion >= 17) {
onEntityDestroy(receiver, nms.PacketPlayOutEntityDestroy_ENTITIES.getInt(packet));
} else {
for (int entity : (int[]) nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet)) {
onEntityDestroy(receiver, entity);
}
}
}
}
/**
@@ -83,33 +94,31 @@ public class PacketListener implements RawPacketListener, PlayerInfoPacketListen
List<Entity> vehicleList;
if (pl != null) {
//player moved
if (!pl.isLoaded() || nameTagX.isDisabledWorld(pl.getWorldName())) return;
tab.getCPUManager().runMeasuredTask("processing EntityMove", getFeatureType(), UsageType.PACKET_ENTITY_MOVE, () -> pl.getArmorStandManager().teleport(receiver));
} else if ((vehicleList = nameTagX.vehicles.get(entityId)) != null){
//a vehicle carrying something moved
for (Entity entity : vehicleList) {
TabPlayer passenger = nameTagX.entityIdMap.get(entity.getEntityId());
if (passenger != null && passenger.getArmorStandManager() != null && !nameTagX.isDisabledWorld(passenger.getWorldName())) {
if (passenger != null && passenger.getArmorStandManager() != null) {
tab.getCPUManager().runMeasuredTask("processing EntityMove", getFeatureType(), UsageType.PACKET_ENTITY_MOVE, () -> passenger.getArmorStandManager().teleport(receiver));
}
}
}
}
@Override
public void onPacketSend(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
if (!modifyNPCnames || info.action != EnumPlayerInfoAction.ADD_PLAYER) return;
for (PlayerInfoData playerInfoData : info.entries) {
if (tab.getPlayerByTablistUUID(playerInfoData.uniqueId) == null && playerInfoData.name.length() <= 15) {
if (playerInfoData.name.length() <= 14) {
playerInfoData.name += "\u00a7r";
} else {
playerInfoData.name += " ";
}
}
public void onEntitySpawn(TabPlayer receiver, int entityId) {
TabPlayer spawnedPlayer = nameTagX.entityIdMap.get(entityId);
if (spawnedPlayer != null && spawnedPlayer.isLoaded()) {
tab.getCPUManager().runMeasuredTask("processing NamedEntitySpawn", getFeatureType(), UsageType.PACKET_NAMED_ENTITY_SPAWN, () -> spawnedPlayer.getArmorStandManager().spawn(receiver));
}
}
public void onEntityDestroy(TabPlayer receiver, int entity) {
TabPlayer despawnedPlayer = nameTagX.entityIdMap.get(entity);
if (despawnedPlayer != null && despawnedPlayer.isLoaded())
tab.getCPUManager().runMeasuredTask("processing EntityDestroy", getFeatureType(), UsageType.PACKET_ENTITY_DESTROY, () -> despawnedPlayer.getArmorStandManager().destroy(receiver));
}
@Override
public TabFeature getFeatureType() {
return TabFeature.NAMETAGX;
@@ -0,0 +1,664 @@
package me.neznamy.tab.platforms.bukkit.nms;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import org.bukkit.Bukkit;
import io.netty.channel.Channel;
import me.neznamy.tab.platforms.bukkit.Main;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherRegistry;
@SuppressWarnings({"unchecked", "rawtypes"})
public class NMSStorage {
//instance of this class
private static NMSStorage instance;
//data watcher registry
public DataWatcherRegistry dataWatcherRegistry;
//server package, such as "v1_16_R3"
private String serverPackage;
//server minor version such as "16"
public int minorVersion;
public Class<?> Entity;
public Class<?> EntityLiving;
private Class<?> Packet;
private Class<?> PlayerConnection;
private Class<?> NetworkManager;
public Class<?> EntityPlayer;
public Field PING;
public Field PLAYER_CONNECTION;
public Field NETWORK_MANAGER;
public Field CHANNEL;
public Method getHandle;
public Method getProfile;
public Method sendPacket;
//chat
public Class<Enum> EnumChatFormat;
private Class<?> IChatBaseComponent;
private Class<?> ChatBaseComponent;
private Class<?> ChatSerializer;
private Class<?> EnumClickAction;
private Class<?> EnumHoverAction;
private Class<?> ChatModifier;
public Class<?> ChatComponentText;
private Class<?> ChatClickable;
private Class<?> ChatHoverable;
private Class<?> ChatHexColor;
public Constructor<?> newChatComponentText;
public Constructor<?> newChatClickable;
public Constructor<?> newChatHoverable;
public Constructor<?> newChatModifier;
public Field ChatBaseComponent_extra;
public Field ChatBaseComponent_modifier;
public Field ChatClickable_action;
public Field ChatClickable_value;
public Field ChatHexColor_name;
public Field ChatHexColor_rgb;
public Field ChatHoverable_action;
public Field ChatHoverable_value;
public Field ChatModifier_color;
public Field ChatModifier_bold;
public Field ChatModifier_italic;
public Field ChatModifier_underlined;
public Field ChatModifier_strikethrough;
public Field ChatModifier_obfuscated;
public Field ChatModifier_clickEvent;
public Field ChatModifier_hoverEvent;
public Field ChatComponentText_text;
public Method ChatSerializer_DESERIALIZE;
public Method EnumClickAction_a;
public Method EnumHoverAction_a;
public Method ChatHexColor_ofInt;
public Method ChatHexColor_ofString;
public Method ChatComponentText_addSibling;
//PacketPlayOutChat
private Class<?> PacketPlayOutChat;
public Class<Enum> ChatMessageType;
public Constructor<?> newPacketPlayOutChat;
//PacketPlayOutPlayerListHeaderFooter
public Class<?> PacketPlayOutPlayerListHeaderFooter;
public Constructor<?> newPacketPlayOutPlayerListHeaderFooter;
public Field PacketPlayOutPlayerListHeaderFooter_HEADER;
public Field PacketPlayOutPlayerListHeaderFooter_FOOTER;
//PacketPlayOutScoreboardDisplayObjective
public Class<?> PacketPlayOutScoreboardDisplayObjective;
public Constructor<?> newPacketPlayOutScoreboardDisplayObjective;
public Field PacketPlayOutScoreboardDisplayObjective_POSITION;
public Field PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME;
//PacketPlayOutScoreboardObjective
public Class<?> PacketPlayOutScoreboardObjective;
public Class<?> IScoreboardCriteria;
private Class<?> ScoreboardObjective;
public Class<Enum> EnumScoreboardHealthDisplay;
public Constructor<?> newPacketPlayOutScoreboardObjective;
public Constructor<?> newScoreboardObjective;
public Field PacketPlayOutScoreboardObjective_OBJECTIVENAME;
public Field PacketPlayOutScoreboardObjective_DISPLAYNAME;
public Field PacketPlayOutScoreboardObjective_RENDERTYPE;
public Field PacketPlayOutScoreboardObjective_METHOD;
//PacketPlayOutScoreboardScore
private Class<?> PacketPlayOutScoreboardScore;
private Class<?> ScoreboardScore;
public Class<Enum> EnumScoreboardAction;
public Constructor<?> newPacketPlayOutScoreboardScore;
public Constructor<?> newPacketPlayOutScoreboardScore_String;
public Constructor<?> newPacketPlayOutScoreboardScore_1_13;
public Constructor<?> newScoreboardScore;
public Method ScoreboardScore_setScore;
//PacketPlayOutScoreboardTeam
public Class<?> PacketPlayOutScoreboardTeam;
public Class<?> EnumNameTagVisibility;
public Class<?> EnumTeamPush;
public Constructor<?> newPacketPlayOutScoreboardTeam;
public Field PacketPlayOutScoreboardTeam_NAME;
public Field PacketPlayOutScoreboardTeam_PLAYERS;
private Class<?> Scoreboard;
private Class<?> ScoreboardTeam;
public Constructor<?> newScoreboard;
public Constructor<?> newScoreboardTeam;
public Method ScoreboardTeam_setPrefix;
public Method ScoreboardTeam_setSuffix;
public Method ScoreboardTeam_setNameTagVisibility;
public Method ScoreboardTeam_setCollisionRule;
public Method ScoreboardTeam_setColor;
public Method ScoreboardTeam_getPlayerNameSet;
//1.17
public Class<?> PacketPlayOutScoreboardTeam_a;
public Method PacketPlayOutScoreboardTeam_of;
public Method PacketPlayOutScoreboardTeam_ofBoolean;
public Method PacketPlayOutScoreboardTeam_ofString;
//PacketPlayOutPlayerInfo
public Class<?> PacketPlayOutPlayerInfo;
public Class<Enum> EnumGamemode;
public Class<Enum> EnumPlayerInfoAction;
private Class<?> PlayerInfoData;
public Constructor<?> newPacketPlayOutPlayerInfo;
public Constructor<?> newPlayerInfoData;
public Field PacketPlayOutPlayerInfo_ACTION;
public Field PacketPlayOutPlayerInfo_PLAYERS;
public Method PlayerInfoData_getProfile;
public Method PlayerInfoData_getLatency;
public Method PlayerInfoData_getGamemode;
public Method PlayerInfoData_getDisplayName;
public Class<?> PacketPlayOutEntityDestroy;
public Constructor<?> newPacketPlayOutEntityDestroy;
public Field PacketPlayOutEntityDestroy_ENTITIES;
public Class<?> PacketPlayInUseEntity;
private Class<?> EnumEntityUseAction;
public Field PacketPlayInUseEntity_ENTITY;
public Field PacketPlayInUseEntity_ACTION;
public Class<?> PacketPlayOutNamedEntitySpawn;
public Field PacketPlayOutNamedEntitySpawn_ENTITYID;
public Class<?> PacketPlayOutEntity;
public Class<?> PacketPlayOutEntityLook;
public Field PacketPlayOutEntity_ENTITYID;
public Class<?> PacketPlayOutEntityTeleport;
public Constructor<?> newPacketPlayOutEntityTeleport;
public Field PacketPlayOutEntityTeleport_ENTITYID;
public Field PacketPlayOutEntityTeleport_X;
public Field PacketPlayOutEntityTeleport_Y;
public Field PacketPlayOutEntityTeleport_Z;
public Field PacketPlayOutEntityTeleport_YAW;
public Field PacketPlayOutEntityTeleport_PITCH;
public Class<?> PacketPlayOutEntityMetadata;
public Constructor<?> newPacketPlayOutEntityMetadata;
public Field PacketPlayOutEntityMetadata_LIST;
public Class<?> PacketPlayOutSpawnEntityLiving;
public Constructor<?> newPacketPlayOutSpawnEntityLiving;
public Field PacketPlayOutSpawnEntityLiving_ENTITYID;
public Field PacketPlayOutSpawnEntityLiving_UUID;
public Field PacketPlayOutSpawnEntityLiving_ENTITYTYPE;
public Field PacketPlayOutSpawnEntityLiving_X;
public Field PacketPlayOutSpawnEntityLiving_Y;
public Field PacketPlayOutSpawnEntityLiving_Z;
public Field PacketPlayOutSpawnEntityLiving_YAW;
public Field PacketPlayOutSpawnEntityLiving_PITCH;
public Field PacketPlayOutSpawnEntityLiving_DATAWATCHER;
//DataWatcher
private Class<?> DataWatcher;
public Constructor<?> newDataWatcher;
public Method DataWatcher_REGISTER;
private Class<?> DataWatcherItem;
public Field DataWatcherItem_TYPE;
public Field DataWatcherItem_VALUE;
private Class<?> DataWatcherObject;
public Constructor<?> newDataWatcherObject;
public Field DataWatcherObject_SLOT;
public Field DataWatcherObject_SERIALIZER;
private Class<?> DataWatcherRegistry;
private Class<?> DataWatcherSerializer;
/**
* Creates new instance, initializes required NMS classes and fields
* @throws Exception - if something fails
*/
public NMSStorage() throws Exception {
serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
initializeClasses();
initializeConstructors();
initializeFields();
initializeMethods();
dataWatcherRegistry = new DataWatcherRegistry(this, DataWatcherRegistry);
}
/**
* Sets new instance
* @param instance - new instance
*/
public static void setInstance(NMSStorage instance) {
NMSStorage.instance = instance;
}
/**
* Returns instance
* @return instance
*/
public static NMSStorage getInstance() {
return instance;
}
/**
* Initializes required NMS classes
* @throws Exception - if something fails
*/
private void initializeClasses() throws Exception {
DataWatcher = getNMSClass("net.minecraft.network.syncher.DataWatcher", "DataWatcher");
DataWatcherItem = getNMSClass("net.minecraft.network.syncher.DataWatcher$Item", "DataWatcher$Item", "DataWatcher$WatchableObject", "WatchableObject");
EntityPlayer = getNMSClass("net.minecraft.server.level.EntityPlayer", "EntityPlayer");
EnumChatFormat = (Class<Enum>) getNMSClass("net.minecraft.EnumChatFormat", "EnumChatFormat");
Entity = getNMSClass("net.minecraft.world.entity.Entity", "Entity");
EntityLiving = getNMSClass("net.minecraft.world.entity.EntityLiving", "EntityLiving");
Packet = getNMSClass("net.minecraft.network.protocol.Packet", "Packet");
PacketPlayInUseEntity = getNMSClass("net.minecraft.network.protocol.game.PacketPlayInUseEntity", "PacketPlayInUseEntity", "Packet7UseEntity");
PacketPlayOutChat = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutChat", "PacketPlayOutChat", "Packet3Chat");
PacketPlayOutEntity = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntity", "PacketPlayOutEntity", "Packet30Entity");
PacketPlayOutEntityDestroy = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy", "PacketPlayOutEntityDestroy", "Packet29DestroyEntity");
PacketPlayOutEntityLook = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntity$PacketPlayOutEntityLook", "PacketPlayOutEntityLook", "Packet32EntityLook");
PacketPlayOutEntityMetadata = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata", "PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
PacketPlayOutEntityTeleport = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport", "PacketPlayOutEntityTeleport", "Packet34EntityTeleport");
PacketPlayOutNamedEntitySpawn = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn", "PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
PacketPlayOutScoreboardDisplayObjective = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective", "PacketPlayOutScoreboardDisplayObjective", "Packet208SetScoreboardDisplayObjective");
PacketPlayOutScoreboardObjective = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective", "PacketPlayOutScoreboardObjective", "Packet206SetScoreboardObjective");
PacketPlayOutScoreboardScore = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore", "PacketPlayOutScoreboardScore", "Packet207SetScoreboardScore");
PacketPlayOutScoreboardTeam = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam", "PacketPlayOutScoreboardTeam", "Packet209SetScoreboardTeam");
PacketPlayOutSpawnEntityLiving = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving", "PacketPlayOutSpawnEntityLiving", "Packet24MobSpawn");
PlayerConnection = getNMSClass("net.minecraft.server.network.PlayerConnection", "PlayerConnection");
NetworkManager = getNMSClass("net.minecraft.network.NetworkManager", "NetworkManager");
Scoreboard = getNMSClass("net.minecraft.world.scores.Scoreboard", "Scoreboard");
ScoreboardObjective = getNMSClass("net.minecraft.world.scores.ScoreboardObjective", "ScoreboardObjective");
ScoreboardTeam = getNMSClass("net.minecraft.world.scores.ScoreboardTeam", "ScoreboardTeam");
ScoreboardScore = getNMSClass("net.minecraft.world.scores.ScoreboardScore", "ScoreboardScore");
IScoreboardCriteria = getNMSClass("net.minecraft.world.scores.criteria.IScoreboardCriteria", "IScoreboardCriteria");
if (minorVersion >= 7) {
ChatBaseComponent = getNMSClass("net.minecraft.network.chat.ChatBaseComponent", "ChatBaseComponent");
ChatClickable = getNMSClass("net.minecraft.network.chat.ChatClickable", "ChatClickable");
ChatComponentText = getNMSClass("net.minecraft.network.chat.ChatComponentText", "ChatComponentText");
ChatHoverable = getNMSClass("net.minecraft.network.chat.ChatHoverable", "ChatHoverable");
ChatModifier = getNMSClass("net.minecraft.network.chat.ChatModifier", "ChatModifier");
ChatSerializer = getNMSClass("net.minecraft.network.chat.IChatBaseComponent$ChatSerializer", "IChatBaseComponent$ChatSerializer", "ChatSerializer");
EnumClickAction = getNMSClass("net.minecraft.network.chat.ChatClickable$EnumClickAction", "ChatClickable$EnumClickAction", "EnumClickAction");
EnumHoverAction = getNMSClass("net.minecraft.network.chat.ChatHoverable$EnumHoverAction", "ChatHoverable$EnumHoverAction", "EnumHoverAction");
IChatBaseComponent = getNMSClass("net.minecraft.network.chat.IChatBaseComponent", "IChatBaseComponent");
EnumEntityUseAction = getNMSClass("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction", "PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction");
}
if (minorVersion >= 8) {
PacketPlayOutPlayerInfo = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo", "PacketPlayOutPlayerInfo");
PacketPlayOutPlayerListHeaderFooter = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter", "PacketPlayOutPlayerListHeaderFooter");
EnumPlayerInfoAction = (Class<Enum>) getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
PlayerInfoData = getNMSClass("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData", "PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
EnumScoreboardHealthDisplay = (Class<Enum>) getNMSClass("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay", "IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
EnumGamemode = (Class<Enum>) getNMSClass("net.minecraft.world.level.EnumGamemode", "EnumGamemode", "WorldSettings$EnumGamemode");
EnumScoreboardAction = (Class<Enum>) getNMSClass("net.minecraft.server.ScoreboardServer$Action", "ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
EnumNameTagVisibility = getNMSClass("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility", "ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
}
if (minorVersion >= 9) {
DataWatcherObject = getNMSClass("net.minecraft.network.syncher.DataWatcherObject", "DataWatcherObject");
DataWatcherRegistry = getNMSClass("net.minecraft.network.syncher.DataWatcherRegistry", "DataWatcherRegistry");
DataWatcherSerializer = getNMSClass("net.minecraft.network.syncher.DataWatcherSerializer", "DataWatcherSerializer");
EnumTeamPush = getNMSClass("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush", "ScoreboardTeamBase$EnumTeamPush");
}
if (minorVersion >= 12) {
ChatMessageType = (Class<Enum>) getNMSClass("net.minecraft.network.chat.ChatMessageType", "ChatMessageType");
}
if (minorVersion >= 16) {
ChatHexColor = getNMSClass("net.minecraft.network.chat.ChatHexColor", "ChatHexColor");
}
if (minorVersion >= 17) {
PacketPlayOutScoreboardTeam_a = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$a");
}
}
/**
* Initializes required NMS constructors
* @throws Exception - if something fails
*/
private void initializeConstructors() throws Exception {
newDataWatcher = DataWatcher.getConstructors()[0];
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class);
newScoreboardObjective = ScoreboardObjective.getConstructors()[0];
newScoreboard = Scoreboard.getConstructor();
newScoreboardTeam = ScoreboardTeam.getConstructor(Scoreboard, String.class);
newScoreboardScore = ScoreboardScore.getConstructor(Scoreboard, ScoreboardObjective, String.class);
newPacketPlayOutEntityDestroy = getConstructor(PacketPlayOutEntityDestroy, 1);
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor(Entity);
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
newPacketPlayOutScoreboardDisplayObjective = PacketPlayOutScoreboardDisplayObjective.getConstructor(int.class, ScoreboardObjective);
if (minorVersion >= 7) {
newChatComponentText = ChatComponentText.getConstructor(String.class);
newChatClickable = ChatClickable.getConstructor(EnumClickAction, String.class);
newChatHoverable = ChatHoverable.getConstructors()[0];
}
if (minorVersion >= 8) {
newPacketPlayOutPlayerListHeaderFooter = PacketPlayOutPlayerListHeaderFooter.getConstructors()[0];
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
newPlayerInfoData = PlayerInfoData.getConstructors()[0];
}
if (minorVersion >= 9) {
newDataWatcherObject = DataWatcherObject.getConstructors()[0];
}
if (minorVersion >= 13) {
newPacketPlayOutScoreboardObjective = PacketPlayOutScoreboardObjective.getConstructor(ScoreboardObjective, int.class);
newPacketPlayOutScoreboardScore_1_13 = PacketPlayOutScoreboardScore.getConstructor(EnumScoreboardAction, String.class, String.class, int.class);
} else {
newPacketPlayOutScoreboardObjective = PacketPlayOutScoreboardObjective.getConstructor();
newPacketPlayOutScoreboardScore_String = PacketPlayOutScoreboardScore.getConstructor(String.class);
if (minorVersion >= 8) {
newPacketPlayOutScoreboardScore = PacketPlayOutScoreboardScore.getConstructor(ScoreboardScore);
} else {
newPacketPlayOutScoreboardScore = PacketPlayOutScoreboardScore.getConstructor(ScoreboardScore, int.class);
}
}
if (minorVersion >= 16) {
newChatModifier = getConstructor(ChatModifier, 10);
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType, UUID.class);
} else if (minorVersion >= 7) {
newChatModifier = ChatModifier.getConstructor();
if (minorVersion >= 12) {
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, ChatMessageType);
} else if (minorVersion >= 8) {
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, byte.class);
} else {
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent);
}
}
if (minorVersion < 17) {
newPacketPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.getConstructor(ScoreboardTeam, int.class);
}
}
/**
* Initializes required NMS fields
* @throws Exception - if something fails
*/
private void initializeFields() throws Exception {
PLAYER_CONNECTION = getFields(EntityPlayer, PlayerConnection).get(0);
PacketPlayOutScoreboardDisplayObjective_POSITION = getFields(PacketPlayOutScoreboardDisplayObjective, int.class).get(0);
PacketPlayOutScoreboardDisplayObjective_OBJECTIVENAME = getFields(PacketPlayOutScoreboardDisplayObjective, String.class).get(0);
PacketPlayOutScoreboardObjective_OBJECTIVENAME = getFields(PacketPlayOutScoreboardObjective, String.class).get(0);
List<Field> fields = getFields(PacketPlayOutScoreboardObjective, int.class);
PacketPlayOutScoreboardObjective_METHOD = fields.get(fields.size()-1);
PacketPlayOutScoreboardTeam_NAME = getFields(PacketPlayOutScoreboardTeam, String.class).get(0);
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
PacketPlayOutEntityTeleport_ENTITYID = getFields(PacketPlayOutEntityTeleport, int.class).get(0);
PacketPlayOutEntityTeleport_YAW = getFields(PacketPlayOutEntityTeleport, byte.class).get(0);
PacketPlayOutEntityTeleport_PITCH = getFields(PacketPlayOutEntityTeleport, byte.class).get(1);
PacketPlayOutEntity_ENTITYID = getFields(PacketPlayOutEntity, int.class).get(0);
PacketPlayOutEntityMetadata_LIST = getFields(PacketPlayOutEntityMetadata, List.class).get(0);
PacketPlayOutSpawnEntityLiving_ENTITYID = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(0);
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(1);
PacketPlayOutSpawnEntityLiving_YAW = getFields(PacketPlayOutSpawnEntityLiving, byte.class).get(0);
PacketPlayOutSpawnEntityLiving_PITCH = getFields(PacketPlayOutSpawnEntityLiving, byte.class).get(0);
(PacketPlayOutEntityDestroy_ENTITIES = PacketPlayOutEntityDestroy.getDeclaredFields()[0]).setAccessible(true);
PacketPlayOutNamedEntitySpawn_ENTITYID = getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
if (minorVersion >= 7) {
NETWORK_MANAGER = getFields(PlayerConnection, NetworkManager).get(0);
ChatBaseComponent_extra = getFields(ChatBaseComponent, List.class).get(0);
ChatBaseComponent_modifier = getFields(ChatBaseComponent, ChatModifier).get(0);
ChatComponentText_text = getFields(ChatComponentText, String.class).get(0);
ChatClickable_action = getFields(ChatClickable, EnumClickAction).get(0);
ChatClickable_value = getFields(ChatClickable, String.class).get(0);
ChatHoverable_action = getFields(ChatHoverable, EnumHoverAction).get(0);
List<Field> booleans = getFields(ChatModifier, Boolean.class);
ChatModifier_bold = booleans.get(0);
ChatModifier_italic = booleans.get(1);
ChatModifier_underlined = booleans.get(2);
ChatModifier_strikethrough = booleans.get(3);
ChatModifier_obfuscated = booleans.get(4);
ChatModifier_clickEvent = getFields(ChatModifier, ChatClickable).get(0);
ChatModifier_hoverEvent = getFields(ChatModifier, ChatHoverable).get(0);
PacketPlayInUseEntity_ENTITY = getFields(PacketPlayInUseEntity, int.class).get(0);
PacketPlayInUseEntity_ACTION = getFields(PacketPlayInUseEntity, EnumEntityUseAction).get(0);
}
if (minorVersion >= 8) {
CHANNEL = getFields(NetworkManager, Channel.class).get(0);
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
PacketPlayOutPlayerInfo_PLAYERS = getFields(PacketPlayOutPlayerInfo, List.class).get(0);
PacketPlayOutPlayerListHeaderFooter_HEADER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(0);
PacketPlayOutPlayerListHeaderFooter_FOOTER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(1);
PacketPlayOutScoreboardObjective_RENDERTYPE = getFields(PacketPlayOutScoreboardObjective, EnumScoreboardHealthDisplay).get(0);
}
if (minorVersion >= 9) {
DataWatcherItem_TYPE = getFields(DataWatcherItem, DataWatcherObject).get(0);
DataWatcherItem_VALUE = getFields(DataWatcherItem, Object.class).get(0);
DataWatcherObject_SLOT = getFields(DataWatcherObject, int.class).get(0);
DataWatcherObject_SERIALIZER = getFields(DataWatcherObject, DataWatcherSerializer).get(0);
PacketPlayOutEntityTeleport_X = getFields(PacketPlayOutEntityTeleport, double.class).get(0);
PacketPlayOutEntityTeleport_Y = getFields(PacketPlayOutEntityTeleport, double.class).get(1);
PacketPlayOutEntityTeleport_Z = getFields(PacketPlayOutEntityTeleport, double.class).get(2);
PacketPlayOutSpawnEntityLiving_UUID = getFields(PacketPlayOutSpawnEntityLiving, UUID.class).get(0);
PacketPlayOutSpawnEntityLiving_X = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(0);
PacketPlayOutSpawnEntityLiving_Y = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(1);
PacketPlayOutSpawnEntityLiving_Z = getFields(PacketPlayOutSpawnEntityLiving, double.class).get(2);
} else {
//1.8-
DataWatcherItem_TYPE = getFields(DataWatcherItem, int.class).get(1);
DataWatcherItem_VALUE = getFields(DataWatcherItem, Object.class).get(0);
PacketPlayOutEntityTeleport_X = getFields(PacketPlayOutEntityTeleport, int.class).get(1);
PacketPlayOutEntityTeleport_Y = getFields(PacketPlayOutEntityTeleport, int.class).get(2);
PacketPlayOutEntityTeleport_Z = getFields(PacketPlayOutEntityTeleport, int.class).get(3);
PacketPlayOutSpawnEntityLiving_X = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(2);
PacketPlayOutSpawnEntityLiving_Y = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(3);
PacketPlayOutSpawnEntityLiving_Z = getFields(PacketPlayOutSpawnEntityLiving, int.class).get(4);
}
if (minorVersion >= 13) {
PacketPlayOutScoreboardObjective_DISPLAYNAME = getFields(PacketPlayOutScoreboardObjective, IChatBaseComponent).get(0);
} else {
PacketPlayOutScoreboardObjective_DISPLAYNAME = getFields(PacketPlayOutScoreboardObjective, String.class).get(1);
}
if (minorVersion <= 14) {
PacketPlayOutSpawnEntityLiving_DATAWATCHER = getFields(PacketPlayOutSpawnEntityLiving, DataWatcher).get(0);
}
if (minorVersion >= 16) {
//1.16+
fields = getFields(ChatHexColor, String.class);
ChatHexColor_name = fields.get(fields.size()-1);
ChatHexColor_rgb = getFields(ChatHexColor, int.class).get(0);
ChatHoverable_value = getFields(ChatHoverable, Object.class).get(0);
ChatModifier_color = getFields(ChatModifier, ChatHexColor).get(0);
} else if (minorVersion >= 7) {
ChatHoverable_value = getFields(ChatHoverable, IChatBaseComponent).get(0);
ChatModifier_color = getFields(ChatModifier, EnumChatFormat).get(0);
}
if (minorVersion >= 17) {
try {
PING = getField(EntityPlayer, "e");
} catch (NoSuchFieldException e) {
//deobfuscated spigot jat
PING = getField(EntityPlayer, "latency");
}
} else {
PING = getField(EntityPlayer, "ping");
}
}
/**
* Initializes required NMS methods
* @throws Exception - if something fails
*/
private void initializeMethods() throws Exception {
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
sendPacket = getMethod(PlayerConnection, new String[]{"sendPacket", "func_147359_a"}, Packet);
ScoreboardTeam_getPlayerNameSet = getMethod(ScoreboardTeam, new String[]{"getPlayerNameSet", "func_96670_d"});
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[]{"setScore", "func_96647_c"}, int.class);
if (minorVersion >= 7) {
ChatComponentText_addSibling = getMethod(ChatComponentText, new String[]{"addSibling", "a", "func_150257_a"}, IChatBaseComponent); //v1.7.R4+, v1.7.R3-
ChatSerializer_DESERIALIZE = getMethod(ChatSerializer, new String[]{"a", "func_150699_a"}, String.class);
EnumClickAction_a = getMethod(EnumClickAction, new String[]{"a", "func_150672_a"}, String.class);
EnumHoverAction_a = getMethod(EnumHoverAction, new String[]{"a", "func_150684_a"}, String.class);
}
if (minorVersion >= 8) {
PlayerInfoData_getProfile = PlayerInfoData.getMethod("a");
PlayerInfoData_getLatency = PlayerInfoData.getMethod("b");
PlayerInfoData_getGamemode = PlayerInfoData.getMethod("c");
PlayerInfoData_getDisplayName = PlayerInfoData.getMethod("d");
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[]{"setNameTagVisibility", "a"}, EnumNameTagVisibility);
getProfile = EntityPlayer.getMethod("getProfile");
}
if (minorVersion >= 9) {
ScoreboardTeam_setCollisionRule = ScoreboardTeam.getMethod("setCollisionRule", EnumTeamPush);
DataWatcher_REGISTER = DataWatcher.getMethod("register", DataWatcherObject, Object.class);
} else {
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"a", "func_75682_a"}, int.class, Object.class);
}
if (minorVersion >= 13) {
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("setPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("setSuffix", IChatBaseComponent);
ScoreboardTeam_setColor = ScoreboardTeam.getMethod("setColor", EnumChatFormat);
} else {
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[]{"setPrefix", "func_96666_b"}, String.class);
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[]{"setSuffix", "func_96662_c"}, String.class);
}
if (minorVersion >= 16) {
ChatHexColor_ofInt = ChatHexColor.getMethod("a", int.class);
ChatHexColor_ofString = ChatHexColor.getMethod("a", String.class);
}
if (minorVersion >= 17) {
PacketPlayOutScoreboardTeam_of = PacketPlayOutScoreboardTeam.getMethod("a", ScoreboardTeam);
PacketPlayOutScoreboardTeam_ofBoolean = PacketPlayOutScoreboardTeam.getMethod("a", ScoreboardTeam, boolean.class);
PacketPlayOutScoreboardTeam_ofString = PacketPlayOutScoreboardTeam.getMethod("a", ScoreboardTeam, String.class, PacketPlayOutScoreboardTeam_a);
}
}
/**
* A helper method that prints all methods of class into console, including their return type, name and parameters
* Useful for modded servers which code I can not access
* @param clazz - class to show methods of
*/
public void showMethods(Class<?> clazz) {
System.out.println("--- " + clazz.getSimpleName() + " ---");
for (Method m : clazz.getMethods()) {
System.out.println(m.getReturnType().getName() + " " + m.getName() + "(" + Arrays.toString(m.getParameterTypes()) + ")");
}
}
/**
* Returns class with given potential names in same order
* @param names - possible class names
* @return class for specified name(s)
* @throws ClassNotFoundException if class does not exist
*/
public Class<?> getNMSClass(String... names) throws ClassNotFoundException {
for (String name : names) {
try {
return getNMSClass(name);
} catch (ClassNotFoundException e) {
}
}
throw new ClassNotFoundException("No class found with possible names " + Arrays.toString(names));
}
/**
* Returns class from given name
* @param name - class name
* @return class from given name
* @throws ClassNotFoundException if class was not found
*/
private Class<?> getNMSClass(String name) throws ClassNotFoundException {
if (minorVersion >= 17) {
return Class.forName(name);
} else {
try {
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
} catch (ClassNotFoundException e) {
//modded server?
return Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
} catch (NullPointerException e) {
//nested class in modded server
throw new ClassNotFoundException(name);
}
}
}
/**
* Returns method with specified possible names and parameters. Throws exception if no such method was found
* @param clazz - class to get method from
* @param names - possible method names
* @param parameterTypes - parameter types of the method
* @return method with specified name and parameters
* @throws NoSuchMethodException if no such method exists
*/
private Method getMethod(Class<?> clazz, String[] names, Class<?>... parameterTypes) throws NoSuchMethodException {
for (String name : names) {
try {
return clazz.getMethod(name, parameterTypes);
} catch (Exception e) {
}
}
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(names) + " in class " + clazz.getName());
}
/**
* Returns all fields of class with defined class type
* @param clazz - class to check fields of
* @param type - field type to check for
* @return list of all fields with specified class type
*/
public List<Field> getFields(Class<?> clazz, Class<?> type){
if (clazz == null) throw new IllegalArgumentException("Source class cannot be null");
List<Field> list = new ArrayList<Field>();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
if (field.getType() == type) list.add(field);
}
return list;
}
/**
* Returns field with specified name and makes it accessible
* @param clazz - class to get field from
* @param name - field name
* @return accessible field with defined name
* @throws NoSuchFieldException if field was not found
*/
private Field getField(Class<?> clazz, String name) throws NoSuchFieldException {
for (Field f : clazz.getDeclaredFields()) {
if (f.getName().equals(name) || (f.getName().split("_").length == 3 && f.getName().split("_")[2].equals(name))) {
f.setAccessible(true);
return f;
}
}
if (name.equals("ping")){
return getField(clazz, "field_71138_i"); //thermos
}
throw new NoSuchFieldException("Field \"" + name + "\" was not found in class " + clazz.getName());
}
private Constructor<?> getConstructor(Class<?> clazz, int parameterCount) throws NoSuchMethodException {
for (Constructor<?> c : clazz.getDeclaredConstructors()) {
if (c.getParameterCount() == parameterCount) {
c.setAccessible(true);
return c;
}
}
throw new NoSuchMethodException("No constructor found in class " + clazz.getName() + " with " + parameterCount + " parameters");
}
}
@@ -5,7 +5,6 @@ import java.util.List;
import java.util.Map;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.ProtocolVersion;
/**
* A class representing the n.m.s.DataWatcher class to make work with it much easier
@@ -59,10 +58,15 @@ public class DataWatcher {
*/
public Object toNMS() throws Exception {
NMSStorage nms = NMSStorage.getInstance();
Object nmsWatcher = nms.newDataWatcher.newInstance(new Object[] {null});
Object nmsWatcher;
if (nms.newDataWatcher.getParameterCount() == 1) {
nmsWatcher = nms.newDataWatcher.newInstance(new Object[] {null});
} else {
nmsWatcher = nms.newDataWatcher.newInstance();
}
for (DataWatcherItem item : dataValues.values()) {
Object position;
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
if (nms.minorVersion >= 9) {
position = nms.newDataWatcherObject.newInstance(item.type.position, item.type.classType);
} else {
position = item.type.position;
@@ -36,13 +36,16 @@ public class DataWatcherHelper {
* @return armor stand flags position based on server version
*/
private int getArmorStandFlagsPosition() {
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 15) {
if (NMSStorage.getInstance().minorVersion >= 17) {
//1.17.x
return 15;
} else if (NMSStorage.getInstance().minorVersion >= 15) {
//1.15.x, 1.16.x
return 14;
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 14) {
} else if (NMSStorage.getInstance().minorVersion >= 14) {
//1.14.x
return 13;
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 10) {
} else if (NMSStorage.getInstance().minorVersion >= 10) {
//1.10.x - 1.13.x
return 11;
} else {
@@ -65,17 +68,22 @@ public class DataWatcherHelper {
* @param clientVersion - client version
*/
public void setCustomName(String customName, ProtocolVersion clientVersion) {
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 13) {
if (NMSStorage.getInstance().minorVersion >= 13) {
try {
data.setValue(new DataWatcherObject(2, registry.Optional_IChatBaseComponent), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).stringToComponent(IChatBaseComponent.optimizedComponent(customName).toString(clientVersion))));
data.setValue(new DataWatcherObject(2, registry.Optional_IChatBaseComponent), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
} catch (Exception e) {
TAB.getInstance().getErrorManager().printError("Failed to create component", e);
}
} else if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 8){
} else if (NMSStorage.getInstance().minorVersion >= 8){
data.setValue(new DataWatcherObject(2, registry.String), customName);
} else {
String cutName = (customName.length() > 64 ? customName.substring(0, 64) : customName);
data.setValue(new DataWatcherObject(10, registry.String), cutName);
if (NMSStorage.getInstance().minorVersion >= 6){
data.setValue(new DataWatcherObject(10, registry.String), cutName);
} else {
data.setValue(new DataWatcherObject(5, registry.String), cutName);
}
}
}
@@ -85,7 +93,7 @@ public class DataWatcherHelper {
* @param visible - if visible or not
*/
public void setCustomNameVisible(boolean visible) {
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
if (NMSStorage.getInstance().minorVersion >= 9) {
data.setValue(new DataWatcherObject(3, registry.Boolean), visible);
} else {
data.setValue(new DataWatcherObject(3, registry.Byte), (byte)(visible?1:0));
@@ -97,7 +105,11 @@ public class DataWatcherHelper {
* @param health - health of entity
*/
public void setHealth(float health) {
data.setValue(new DataWatcherObject(6, registry.Float), health);
if (NMSStorage.getInstance().minorVersion >= 6) {
data.setValue(new DataWatcherObject(6, registry.Float), health);
} else {
data.setValue(new DataWatcherObject(16, registry.Integer), (int)health);
}
}
/**
@@ -1,7 +1,6 @@
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.shared.ProtocolVersion;
public class DataWatcherItem {
@@ -29,7 +28,7 @@ public class DataWatcherItem {
*/
public static DataWatcherItem fromNMS(Object nmsItem) throws Exception {
NMSStorage nms = NMSStorage.getInstance();
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
if (NMSStorage.getInstance().minorVersion >= 9) {
Object nmsObject = nms.DataWatcherItem_TYPE.get(nmsItem);
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherObject_SLOT.getInt(nmsObject), nms.DataWatcherObject_SERIALIZER.get(nmsObject)), nms.DataWatcherItem_VALUE.get(nmsItem));
} else {
@@ -5,7 +5,7 @@ import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
/**
* A class representing the n.m.s.DataWatcherRegistry class to make work with it much easier
@@ -23,24 +23,19 @@ public class DataWatcherRegistry {
/**
* Initializes required NMS classes and fields
*/
public DataWatcherRegistry(Class<?> registry) {
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
public DataWatcherRegistry(NMSStorage nms, Class<?> registry) {
if (nms.minorVersion >= 9) {
Map<String, Object> fields = getStaticFields(registry);
Byte = fields.get("a");
Integer = fields.get("b");
Float = fields.get("c");
String = fields.get("d");
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 13) {
if (nms.minorVersion >= 13) {
Optional_IChatBaseComponent = fields.get("f");
Boolean = fields.get("i");
} else {
Boolean = fields.get("h");
}
} else {
Byte = 0;
Integer = 2;
Float = 3;
String = 4;
}
}
@@ -1,245 +1,247 @@
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
#changing nametags, affecting sorting, collision rule
change-nametag-prefix-suffix: true
#prefix/suffix in tablist
change-tablist-prefix-suffix: true
#header/footer
enable-header-footer: true
header:
- "&3&lServer name"
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
- "&7Online players: &f%online%"
- "&6Online staff: &e%staffonline%"
- "%animation:ping-tps%"
- ""
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- "%animation:MyAnimation1%"
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- ""
- "&7Visit our webpage %animation:web%"
#supports any numeric placeholder, set to "" to disable the feature
yellow-number-in-tablist: "%ping%"
#literally what it says
group-sorting-priority-list:
- owner
- admin
- mod
- helper
- builder
- premium
- player
- default
Groups:
Owner:
tabprefix: "&0&l[&a&lOwner&0&l] &a"
tagprefix: "&2&lOwner &a"
Admin:
tabprefix: "&0&l[&4&lAdmin&0&l] &1"
tagprefix: "%animation:admin_prefix%"
Player:
tabprefix: "&0&l[&7&lPlayer&0&l] &3"
tagprefix: "&2&lPlayer &3"
example_group:
header:
- "This is an example of per-group header/footer"
footer:
- "applied to a group"
#default settings for all groups, all groups will take properties from this section unless player's primary group overrides a specific setting
_OTHER_:
tabprefix: "%vault-prefix%"
tagprefix: "%vault-prefix%"
tabsuffix: "%afk%"
tagsuffix: "%afk%"
customtabname: "%essentialsnick%"
customtagname: "%essentialsnick%"
Users:
_NEZNAMY_:
tabprefix: "&6&lTAB Author &a"
tagprefix: "&6&lTAB &r&a"
abovename: "&bOh look, 3 lines of text !"
belowname: "&eThere's only one plugin out there supporting it"
#additional very limited line of text below nametag
#don't forget this enables on all player entities, which includes NPCs
#tags so you can find this section using the mighty ctrl + f feature: [NPC, Citizens, below_name, health]
classic-vanilla-belowname:
#enabling the feature
enabled: true
#number or numeric placeholder, supports per-player placeholders
number: "%health%"
#text after number, must be same on all players
text: "Health"
#controlling the collision rule
enable-collision: true
#using the opposite of the setting above for worlds
revert-collision-rule-in-worlds:
- revertedworld
#preventing players with spectator gamemode from being moved to the bottom of tablist with transparent name
do-not-move-spectators: false
#allows players with tab.spectatorbypass permission to see spectators moved despite the option above enabled
allow-spectator-bypass-permission: false
#using armor stands for nametags, bypassing all limits
#THEY ARE CLIENT SIDED AND DO NOT EXIST ON SERVER SO STOP SAYING YOUR SERVER IS LAGGING FROM THIS
unlimited-nametag-prefix-suffix-mode:
#enabling the feature
enabled: false
#enabling will result in NPCs ignoring nametag prefix/suffix/visibility to prevent NPCs with names of online players from being invisible
modify-npc-names: false
#using a feature introduced in 1.8.1 to make hitbox zero so you can't cry it affects pvp despite hit redirection
#1.8.0 clients will see wild things
#automatically enabled for 1.9+
use-marker-tag-for-1-8-x-clients: false
#disabling the feature for players riding boats to prevent massive desync
disable-on-boats: true
#in blocks
space-between-lines: 0.22
tracking-range: 48
per-world-settings:
world1:
header:
- "an example of world with custom"
footer:
- "header/footer and prefix/suffix"
Groups:
Owner:
tabprefix: "&0&l[&a&lOwner&0&l] &a"
tagprefix: "&2&lOwner &a"
#separating multiple worlds with "-"
world2-world3:
header:
- "This is a shared header for"
- "world2 and world3"
placeholders:
#output of %afk%
afk-yes: " &4*&4&lAFK&4*&r"
afk-no: ""
#%date%
date-format: "dd.MM.yyyy"
#%time%
time-format: "[HH:mm:ss / h:mm a]"
#in hours, in case of wrong timezone
time-offset: 0
#don't forget color codes
remove-strings:
- "[]"
- "< >"
#aliases for %rank%, this is not the place where group prefix is defined!
rank-aliases:
Admin: "&4&lADMIN"
Mod: "&b&lMOD"
Premium: "&6&lPREMIUM"
Ultra: "&b&lULTRA"
Legend: "&a&lLEGEND"
Titan: "&c&lTITAN"
Youtuber: "&c&lYOUTUBE"
_OTHER_: "&7No Rank"
disable-features-in-worlds:
header-footer:
- disabledworld
tablist-names:
- disabledworld
nametag:
- disabledworld
yellow-number:
- disabledworld
bossbar:
- disabledworld
belowname:
- disabledworld
placeholderapi-refresh-intervals:
default-refresh-interval: 100
server:
"%server_uptime%": 1000
"%server_tps_1_colored%": 1000
player:
"%player_health%": 200
"%player_ping%": 1000
"%vault_prefix%": 1000
relational:
"%rel_factionsuuid_relation_color%": 500
#different playerlist in different worlds
per-world-playerlist:
#to enable the feature
enabled: false
#allows players with tab.bypass permission to see everyone in tablist despite this option enabled
allow-bypass-permission: false
#players in these worlds will see everyone despite the feature enabled
ignore-effect-in-worlds:
- ignoredworld
- build
#groups of worlds to share playerlist
shared-playerlist-world-groups:
#group name, can be anything
lobby:
- lobby1
- lobby2
minigames:
- paintball
- bedwars
#permission node is tab.sort.<permission group from sorting list>
sort-players-by-permissions: false
#making tamed animals no longer copy nametag properties from their owner (prefix/suffix/visibility)
#do not enable this if you're on paper, you'll feed the trolls even more
fix-pet-names: false
#taking primary group from permission plugin
use-primary-group: true
#if the option above is false, all groups are taken from permission plugin and the one higher in this list is used as primary
primary-group-finding-list:
- Owner
- Admin
- Helper
- default
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list above> permission
assign-groups-by-permissions: false
#preventing other plugins from overriding TAB
anti-override:
scoreboard-teams: true
scoreboard-objectives: true
tablist-names: true
header-footer: true
usernames: true
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
#changing nametags, affecting sorting, collision rule
change-nametag-prefix-suffix: true
#prefix/suffix in tablist
change-tablist-prefix-suffix: true
#header/footer
enable-header-footer: true
header:
- "&3&lServer name"
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
- "&7Online players: &f%online%"
- "&6Online staff: &e%staffonline%"
- "%animation:ping-tps%"
- ""
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- "%animation:MyAnimation1%"
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- ""
- "&7Visit our webpage %animation:web%"
#supports any numeric placeholder, set to "" to disable the feature
yellow-number-in-tablist: "%ping%"
group-sorting-priority-list:
- owner
- admin
- mod
- helper
- builder
- premium
- player
- default
Groups:
Owner:
tabprefix: "&0&l[&a&lOwner&0&l] &a"
tagprefix: "&2&lOwner &a"
Admin:
tabprefix: "&0&l[&4&lAdmin&0&l] &1"
tagprefix: "%animation:admin_prefix%"
Player:
tabprefix: "&0&l[&7&lPlayer&0&l] &3"
tagprefix: "&2&lPlayer &3"
example_group:
header:
- "This is an example of per-group header/footer"
footer:
- "applied to a group"
#default settings for all groups, all groups will take properties from this section unless player's primary group overrides a specific setting
_OTHER_:
tabprefix: "%vault-prefix%"
tagprefix: "%vault-prefix%"
tabsuffix: "%afk%"
tagsuffix: "%afk%"
customtabname: "%essentialsnick%"
customtagname: "%essentialsnick%"
Users:
_NEZNAMY_:
tabprefix: "&6&lTAB Author &a"
tagprefix: "&6&lTAB &r&a"
abovename: "&bOh look, 3 lines of text !"
belowname: "&eThere's only one plugin out there supporting it"
#additional very limited line of text below nametag
#don't forget this enables on all player entities, which includes NPCs
#tags so you can find this section using the mighty ctrl + f feature: [NPC, Citizens, below_name, health]
classic-vanilla-belowname:
#enabling the feature
enabled: true
#number or numeric placeholder, supports per-player placeholders
number: "%health%"
#text after number, must be same on all players
text: "Health"
#controlling the collision rule
enable-collision: true
#using the opposite of the setting above for worlds
revert-collision-rule-in-worlds:
- revertedworld
#preventing players with spectator gamemode from being moved to the bottom of tablist with transparent name
do-not-move-spectators: false
#allows players with tab.spectatorbypass permission to see spectators moved despite the option above enabled
allow-spectator-bypass-permission: false
#using armor stands for nametags, bypassing all limits (they are not on server sided & asynchronous - no TPS loss)
unlimited-nametag-prefix-suffix-mode:
#enabling the feature
enabled: false
#using a feature introduced in 1.8.1 to make hitbox zero so you can't cry it affects pvp despite hit redirection
#1.8.0 clients will see wild things
#automatically enabled for 1.9+
use-marker-tag-for-1-8-x-clients: false
#disabling the feature for players riding boats to prevent massive desync
disable-on-boats: true
#in blocks
space-between-lines: 0.22
per-world-settings:
world1:
header:
- "an example of world with custom"
footer:
- "header/footer and prefix/suffix"
Groups:
Owner:
tabprefix: "&0&l[&a&lOwner&0&l] &a"
tagprefix: "&2&lOwner &a"
#separating multiple worlds with "-"
world2-world3:
header:
- "This is a shared header for"
- "world2 and world3"
placeholders:
#output of %afk%
afk-yes: " &4*&4&lAFK&4*&r"
afk-no: ""
#%date%
date-format: "dd.MM.yyyy"
#%time%
time-format: "[HH:mm:ss / h:mm a]"
#in hours, in case of wrong timezone
time-offset: 0
#don't forget color codes
remove-strings:
- "[]"
- "< >"
#aliases for %rank%, this is not the place where group prefix is defined!
rank-aliases:
Admin: "&4&lADMIN"
Mod: "&b&lMOD"
Premium: "&6&lPREMIUM"
Ultra: "&b&lULTRA"
Legend: "&a&lLEGEND"
Titan: "&c&lTITAN"
Youtuber: "&c&lYOUTUBE"
_OTHER_: "&7No Rank"
disable-features-in-worlds:
header-footer:
- disabledworld
tablist-names:
- disabledworld
nametag:
- disabledworld
yellow-number:
- disabledworld
bossbar:
- disabledworld
belowname:
- disabledworld
unlimited-nametags:
- disabledworld
placeholderapi-refresh-intervals:
default-refresh-interval: 100
server:
"%server_uptime%": 1000
"%server_tps_1_colored%": 1000
player:
"%player_health%": 200
"%player_ping%": 1000
"%vault_prefix%": 1000
relational:
"%rel_factionsuuid_relation_color%": 500
#different playerlist in different worlds
per-world-playerlist:
#to enable the feature
enabled: false
#allows players with tab.bypass permission to see everyone in tablist despite this option enabled
allow-bypass-permission: false
#players in these worlds will see everyone despite the feature enabled
ignore-effect-in-worlds:
- ignoredworld
- build
#groups of worlds to share playerlist
shared-playerlist-world-groups:
#group name, can be anything
lobby:
- lobby1
- lobby2
minigames:
- paintball
- bedwars
#permission node is tab.sort.<permission group from sorting list>
sort-players-by-permissions: false
#making tamed animals no longer copy nametag properties from their owner (prefix/suffix/visibility)
fix-pet-names: false
#taking primary group from permission plugin
use-primary-group: true
#if the option above is false, all groups are taken from permission plugin and the one higher in this list is used as primary
primary-group-finding-list:
- Owner
- Admin
- Helper
- default
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list above> permission
assign-groups-by-permissions: false
#preventing other plugins from overriding TAB
anti-override:
scoreboard-teams: true
scoreboard-objectives: true
tablist-names: true
usernames: true
#permanently invisible nametags
invisible-nametags: false
#faking ping of players
ping-spoof:
enabled: false
value: 0
@@ -1,10 +1,10 @@
name: ${project.name}
version: ${project.version}
author: NEZNAMY
main: me.neznamy.tab.platforms.bukkit.Main
api-version: 1.13
softdepend: [PlaceholderAPI, Vault, UltraPermissions, LuckPerms, Essentials, LibsDisguises]
website: "https://www.spigotmc.org/resources/57806/"
description: ${project.description}
commands:
name: ${project.parent.name}
version: ${project.parent.version}
author: NEZNAMY
main: me.neznamy.tab.platforms.bukkit.Main
api-version: 1.13
softdepend: [PlaceholderAPI, Vault, UltraPermissions, LuckPerms, Essentials, LibsDisguises, ProtocolSupport]
website: "https://www.spigotmc.org/resources/57806/"
description: ${project.parent.description}
commands:
tab:
+164
View File
@@ -0,0 +1,164 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>me.neznamy</groupId>
<artifactId>TAB</artifactId>
<version>2.9.0</version>
</parent>
<artifactId>bungeecord</artifactId>
<repositories>
<!-- PremiumVanishAPI -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<!-- BungeeCord -->
<repository>
<id>luck-repo</id>
<url>https://ci.lucko.me/plugin/repository/everything/</url>
</repository>
</repositories>
<dependencies>
<!-- TAB Core -->
<dependency>
<groupId>me.neznamy</groupId>
<artifactId>shared</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
</exclusion>
<exclusion>
<groupId>com.github.NEZNAMY</groupId>
<artifactId>YamlAssist</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- BungeeCord -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-haproxy</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-unix-common</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>brigadier</artifactId>
</exclusion>
<exclusion>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
</exclusion>
<exclusion>
<groupId>net.sf.trove4j</groupId>
<artifactId>core</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-query</artifactId>
</exclusion>
<exclusion>
<groupId>se.llbit</groupId>
<artifactId>jo-nbt</artifactId>
</exclusion>
<exclusion>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
</exclusion>
<exclusion>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-resolver-provider</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>
maven-resolver-connector-basic
</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>
maven-resolver-transport-http
</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</exclusion>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- PremiumVanishAPI -->
<dependency>
<groupId>com.github.MyzelYam</groupId>
<artifactId>PremiumVanishAPI</artifactId>
<version>2.0.3</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Bungee Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bungeecord</artifactId>
<version>2.2.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -49,10 +49,6 @@ public class BungeeEventListener implements Listener {
@EventHandler
public void onChat(ChatEvent e) {
if (TAB.getInstance().isDisabled()) return;
if (e.getMessage().startsWith("/")) {
if (TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
} else {
if (TAB.getInstance().getFeatureManager().onChat(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage(), e.isCancelled())) e.setCancelled(true);
}
if (e.getMessage().startsWith("/") && TAB.getInstance().getFeatureManager().onCommand(TAB.getInstance().getPlayer(((ProxiedPlayer)e.getSender()).getUniqueId()), e.getMessage())) e.setCancelled(true);
}
}
@@ -19,18 +19,16 @@ import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardScore;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardTeam;
import me.neznamy.tab.shared.packets.PacketPlayOutTitle;
import net.md_5.bungee.protocol.packet.BossBar;
import net.md_5.bungee.protocol.packet.Chat;
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
import net.md_5.bungee.protocol.packet.PlayerListItem;
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
import net.md_5.bungee.protocol.packet.ScoreboardObjective;
import net.md_5.bungee.protocol.packet.ScoreboardObjective.HealthDisplay;
import net.md_5.bungee.protocol.packet.ScoreboardScore;
import net.md_5.bungee.protocol.packet.Team;
import net.md_5.bungee.protocol.packet.Title;
import net.md_5.bungee.protocol.packet.PlayerListItem.Item;
/**
* Packet builder for BungeeCord platform
@@ -116,18 +114,7 @@ public class BungeePacketBuilder implements PacketBuilder {
}
@Override
public Object build(PacketPlayOutTitle packet, ProtocolVersion clientVersion) throws Exception {
Title bungeePacket = new Title();
bungeePacket.setAction(Title.Action.valueOf(packet.action.toString()));
if (packet.text != null) bungeePacket.setText(IChatBaseComponent.optimizedComponent(packet.text).toString(clientVersion));
bungeePacket.setFadeIn(packet.fadeIn);
bungeePacket.setStay(packet.stay);
bungeePacket.setFadeOut(packet.fadeOut);
return bungeePacket;
}
@Override
public PacketPlayOutPlayerInfo readPlayerInfo(Object bungeePacket, ProtocolVersion clientVersion){
public PacketPlayOutPlayerInfo readPlayerInfo(Object bungeePacket, ProtocolVersion clientVersion) {
PlayerListItem item = (PlayerListItem) bungeePacket;
List<PlayerInfoData> listData = new ArrayList<PlayerInfoData>();
for (Item i : item.getItems()) {
@@ -137,7 +124,7 @@ public class BungeePacketBuilder implements PacketBuilder {
}
@Override
public PacketPlayOutScoreboardObjective readObjective(Object bungeePacket, ProtocolVersion clientVersion) throws Exception {
public PacketPlayOutScoreboardObjective readObjective(Object bungeePacket, ProtocolVersion clientVersion) {
ScoreboardObjective packet = (ScoreboardObjective) bungeePacket;
String title;
if (clientVersion.getMinorVersion() >= 13) {
@@ -150,15 +137,7 @@ public class BungeePacketBuilder implements PacketBuilder {
}
@Override
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object bungeePacket, ProtocolVersion clientVersion) throws Exception {
public PacketPlayOutScoreboardDisplayObjective readDisplayObjective(Object bungeePacket, ProtocolVersion clientVersion){
return new PacketPlayOutScoreboardDisplayObjective(((ScoreboardDisplay) bungeePacket).getPosition(), ((ScoreboardDisplay) bungeePacket).getName());
}
@Override
public PacketPlayOutPlayerListHeaderFooter readHeaderFooter(Object packet, ProtocolVersion clientVersion) throws Exception {
return new PacketPlayOutPlayerListHeaderFooter(
IChatBaseComponent.fromString(((PlayerListHeaderFooter)packet).getHeader()),
IChatBaseComponent.fromString(((PlayerListHeaderFooter)packet).getFooter())
);
}
}
@@ -16,7 +16,6 @@ import me.neznamy.tab.shared.cpu.UsageType;
import me.neznamy.tab.shared.features.PipelineInjector;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.protocol.packet.Login;
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
import net.md_5.bungee.protocol.packet.PlayerListItem;
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
import net.md_5.bungee.protocol.packet.ScoreboardObjective;
@@ -59,16 +58,15 @@ public class BungeePipelineInjector extends PipelineInjector {
private void modifyPlayers(Team packet){
long time = System.nanoTime();
if (packet.getPlayers() == null) return;
if (packet.getFriendlyFire() != 69) {
Collection<String> col = Lists.newArrayList(packet.getPlayers());
for (TabPlayer p : tab.getPlayers()) {
if (col.contains(p.getName()) && !tab.getFeatureManager().getNameTagFeature().isDisabledWorld(p.getWorldName())) {
logTeamOverride(packet.getName(), p.getName());
col.remove(p.getName());
}
Collection<String> col = Lists.newArrayList(packet.getPlayers());
for (TabPlayer p : tab.getPlayers()) {
if (col.contains(p.getName()) && !tab.getFeatureManager().getNameTagFeature().isDisabledWorld(p.getWorldName()) &&
!p.hasTeamHandlingPaused() && !p.getTeamName().equals(packet.getName())) {
logTeamOverride(packet.getName(), p.getName());
col.remove(p.getName());
}
packet.setPlayers(col.toArray(new String[0]));
}
packet.setPlayers(col.toArray(new String[0]));
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
}
@@ -95,51 +93,45 @@ public class BungeePipelineInjector extends PipelineInjector {
super.write(context, tab.getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
return;
}
Object modifiedPacket = packet;
long time = System.nanoTime();
if (packet instanceof ByteBuf) {
modifiedPacket = deserialize((ByteBuf) packet);
}
tab.getCPUManager().addTime(TabFeature.PACKET_DESERIALIZING, UsageType.PACKET_READING_OUT, System.nanoTime()-time);
if (tab.getFeatureManager().isFeatureEnabled("nametag16") && antiOverrideTeams) {
if (packet instanceof Team) {
//team packet coming from a bungeecord plugin
modifyPlayers((Team) packet);
super.write(context, packet, channelPromise);
time = System.nanoTime();
if (modifiedPacket instanceof Team) {
modifyPlayers((Team) modifiedPacket);
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
super.write(context, modifiedPacket, channelPromise);
return;
}
if (packet instanceof ByteBuf) {
long time = System.nanoTime();
ByteBuf buf = ((ByteBuf) packet);
//for now protocolsupport will break the plugin
/* if (modifiedPacket instanceof ByteBuf) {
ByteBuf buf = ((ByteBuf) modifiedPacket);
int marker = buf.readerIndex();
int packetId = buf.readByte();
if (packetId == ((BungeeTabPlayer)player).getPacketId(Team.class)) {
//team packet sent by backend server, proxy does not deserialize those for whatever reason
Team team = new Team();
team.read(buf, null, ((ProxiedPlayer)player.getPlayer()).getPendingConnection().getVersion());
if (packetId + 128 == ((BungeeTabPlayer)player).getPacketId(Team.class)){
//compressed team packet when using protocolsupport, proper decompression will be needed
buf.release();
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
modifyPlayers(team);
super.write(context, team, channelPromise);
return;
} else if (packetId + 128 == ((BungeeTabPlayer)player).getPacketId(Team.class)){
//compressed team packet when using protocolsupport, just kill it as it does not come from tab anyway
buf.release();
return;
}
buf.readerIndex(marker);
}
}*/
tab.getCPUManager().addTime(TabFeature.NAMETAGS, UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
}
if (packet instanceof ScoreboardDisplay && antiOverrideObjectives && tab.getFeatureManager().onDisplayObjective(player, packet)) {
//TODO add support for serialized packets as above with teams
if (modifiedPacket instanceof ScoreboardDisplay && antiOverrideObjectives && tab.getFeatureManager().onDisplayObjective(player, modifiedPacket)) {
return;
}
if (packet instanceof ScoreboardObjective && antiOverrideObjectives) {
//TODO add support for serialized packets as above with teams
tab.getFeatureManager().onObjective(player, packet);
}
if (packet instanceof PlayerListHeaderFooter && antiOverrideHeaderFooter && tab.getFeatureManager().onHeaderFooter(player, packet)) {
//TODO add support for serialized packets as above with teams
return;
if (modifiedPacket instanceof ScoreboardObjective && antiOverrideObjectives) {
tab.getFeatureManager().onObjective(player, modifiedPacket);
}
//client reset packet
if (packet instanceof Login) {
if (modifiedPacket instanceof Login) {
//making sure to not send own packets before login packet is actually sent
super.write(context, packet, channelPromise);
super.write(context, modifiedPacket, channelPromise);
tab.getFeatureManager().onLoginPacket(player);
return;
}
@@ -148,5 +140,36 @@ public class BungeePipelineInjector extends PipelineInjector {
}
super.write(context, packet, channelPromise);
}
/**
* Deserializes bytebuf 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
*/
private Object deserialize(ByteBuf buf) {
int marker = buf.readerIndex();
int packetId = buf.readByte();
if (packetId == ((BungeeTabPlayer)player).getPacketId(Team.class)) {
Team team = new Team();
team.read(buf, null, ((ProxiedPlayer)player.getPlayer()).getPendingConnection().getVersion());
buf.release();
return team;
}
if (packetId == ((BungeeTabPlayer)player).getPacketId(ScoreboardDisplay.class)) {
ScoreboardDisplay display = new ScoreboardDisplay();
display.read(buf, null, ((ProxiedPlayer)player.getPlayer()).getPendingConnection().getVersion());
buf.release();
return display;
}
if (packetId == ((BungeeTabPlayer)player).getPacketId(ScoreboardObjective.class)) {
ScoreboardObjective objective = new ScoreboardObjective();
objective.read(buf, null, ((ProxiedPlayer)player.getPlayer()).getPendingConnection().getVersion());
buf.release();
return objective;
}
buf.readerIndex(marker);
return buf;
}
}
}
@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import de.myzelyam.api.vanish.BungeeVanishAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.placeholders.Placeholder;
@@ -23,22 +22,24 @@ public class BungeePlaceholderRegistry implements PlaceholderRegistry {
@Override
public List<Placeholder> registerPlaceholders() {
List<Placeholder> placeholders = new ArrayList<Placeholder>();
if (ProxyServer.getInstance().getPluginManager().getPlugin("PremiumVanish") != null) {
placeholders.add(new ServerPlaceholder("%canseeonline%", 1000) {
public String get() {
return TAB.getInstance().getPlayers().size() - BungeeVanishAPI.getInvisiblePlayers().size()+"";
placeholders.add(new PlayerPlaceholder("%canseeonline%", 2000) {
public String get(TabPlayer p) {
int var = 0;
for (TabPlayer all : TAB.getInstance().getPlayers()){
if (!all.isVanished() || p.hasPermission("tab.seevanished")) var++;
}
});
placeholders.add(new ServerPlaceholder("%canseestaffonline%", 1000) {
public String get() {
int count = 0;
for (TabPlayer all : TAB.getInstance().getPlayers()) {
if (!((BungeeTabPlayer)all).isVanished() && all.isStaff()) count++;
}
return count+"";
return String.valueOf(var);
}
});
placeholders.add(new PlayerPlaceholder("%canseestaffonline%", 2000) {
public String get(TabPlayer p) {
int var = 0;
for (TabPlayer all : TAB.getInstance().getPlayers()){
if (all.isStaff() && (!all.isVanished() || p.hasPermission("tab.seevanished"))) var++;
}
});
}
return String.valueOf(var);
}
});
placeholders.add(new PlayerPlaceholder("%displayname%", 500) {
public String get(TabPlayer p) {
return ((ProxiedPlayer) p.getPlayer()).getDisplayName();
@@ -47,7 +48,7 @@ public class BungeePlaceholderRegistry implements PlaceholderRegistry {
for (Entry<String, ServerInfo> server : ProxyServer.getInstance().getServers().entrySet()) {
placeholders.add(new ServerPlaceholder("%online_" + server.getKey() + "%", 1000) {
public String get() {
return server.getValue().getPlayers().size()+"";
return String.valueOf(server.getValue().getPlayers().size());
}
});
placeholders.add(new ServerPlaceholder("%canseeonline_" + server.getKey() + "%", 1000) {
@@ -56,7 +57,7 @@ public class BungeePlaceholderRegistry implements PlaceholderRegistry {
for (ProxiedPlayer p : server.getValue().getPlayers()) {
if (((BungeeTabPlayer)TAB.getInstance().getPlayer(p.getUniqueId())).isVanished()) count--;
}
return count+"";
return String.valueOf(count);
}
});
}
@@ -1,19 +1,19 @@
package me.neznamy.tab.platforms.bungeecord;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.event.BungeeTABLoadEvent;
import me.neznamy.tab.shared.Platform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.config.ConfigurationFile;
import me.neznamy.tab.shared.features.BelowName;
import me.neznamy.tab.shared.features.GlobalPlayerlist;
import me.neznamy.tab.shared.features.NameTag16;
import me.neznamy.tab.shared.features.NameTag;
import me.neznamy.tab.shared.features.PingSpoof;
import me.neznamy.tab.shared.features.PlaceholderManager;
import me.neznamy.tab.shared.features.SpectatorFix;
import me.neznamy.tab.shared.features.TabObjective;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.PermissionPlugin;
import me.neznamy.tab.shared.permission.UltraPermissions;
@@ -42,7 +42,9 @@ public class BungeePlatform implements Platform {
@Override
public PermissionPlugin detectPermissionPlugin() {
if (ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms") != null) {
if (TAB.getInstance().getConfiguration().bukkitPermissions) {
return new VaultBridge(Main.plm);
} else if (ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms") != null) {
return new LuckPerms(ProxyServer.getInstance().getPluginManager().getPlugin("LuckPerms").getDescription().getVersion());
} else if (ProxyServer.getInstance().getPluginManager().getPlugin("UltraPermissions") != null) {
return new UltraPermissions(ProxyServer.getInstance().getPluginManager().getPlugin("UltraPermissions").getDescription().getVersion());
@@ -57,9 +59,14 @@ public class BungeePlatform implements Platform {
tab.getPlaceholderManager().addRegistry(new BungeePlaceholderRegistry());
tab.getPlaceholderManager().addRegistry(new UniversalPlaceholderRegistry());
tab.getPlaceholderManager().registerPlaceholders();
tab.getFeatureManager().registerFeature("injection", new BungeePipelineInjector(tab));
if (tab.getConfiguration().config.getBoolean("change-nametag-prefix-suffix", true)) tab.getFeatureManager().registerFeature("nametag16", new NameTag16(tab));
if (tab.getConfiguration().pipelineInjection) tab.getFeatureManager().registerFeature("injection", new BungeePipelineInjector(tab));
if (tab.getConfiguration().config.getBoolean("change-nametag-prefix-suffix", true)) tab.getFeatureManager().registerFeature("nametag16", new NameTag(tab));
loadUniversalFeatures();
if (tab.getConfiguration().config.getBoolean("ping-spoof.enabled", false)) tab.getFeatureManager().registerFeature("pingspoof", new PingSpoof());
if (tab.getConfiguration().config.getString("yellow-number-in-tablist", "%ping%").length() > 0) tab.getFeatureManager().registerFeature("tabobjective", new TabObjective(tab));
if (tab.getConfiguration().config.getBoolean("do-not-move-spectators", false)) tab.getFeatureManager().registerFeature("spectatorfix", new SpectatorFix());
if (tab.getConfiguration().config.getBoolean("classic-vanilla-belowname.enabled", true)) tab.getFeatureManager().registerFeature("belowname", new BelowName(tab));
if (tab.getConfiguration().premiumconfig != null && tab.getConfiguration().premiumconfig.getBoolean("scoreboard.enabled", false)) tab.getFeatureManager().registerFeature("scoreboard", new ScoreboardManager(tab));
if (tab.getConfiguration().config.getBoolean("global-playerlist.enabled", false)) tab.getFeatureManager().registerFeature("globalplayerlist", new GlobalPlayerlist(tab));
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
tab.addPlayer(new BungeeTabPlayer(p));
@@ -93,24 +100,6 @@ public class BungeePlatform implements Platform {
}
}
@Override
public void convertConfig(ConfigurationFile config) {
convertUniversalOptions(config);
if (config.getName().equals("config.yml")) {
if (config.getObject("global-playerlist") instanceof Boolean) {
rename(config, "global-playerlist", "global-playerlist.enabled");
config.set("global-playerlist.spy-servers", Arrays.asList("spyserver1", "spyserver2"));
Map<String, List<String>> serverGroups = new HashMap<String, List<String>>();
serverGroups.put("lobbies", Arrays.asList("lobby1", "lobby2"));
serverGroups.put("group2", Arrays.asList("server1", "server2"));
config.set("global-playerlist.server-groups", serverGroups);
config.set("global-playerlist.display-others-as-spectators", false);
TAB.getInstance().print('2', "Converted old global-playerlist section to new one in config.yml.");
}
rename(config, "tablist-objective-value", "yellow-number-in-tablist");
}
}
@Override
public String getServerVersion() {
return ProxyServer.getInstance().getVersion();
@@ -135,4 +124,9 @@ public class BungeePlatform implements Platform {
public int getMaxPlayers() {
return ProxyServer.getInstance().getConfigurationAdapter().getListeners().iterator().next().getMaxPlayers();
}
@Override
public String getConfigName() {
return "bungeeconfig.yml";
}
}
@@ -7,6 +7,7 @@ import de.myzelyam.api.vanish.BungeeVanishAPI;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.connection.InitialHandler;
@@ -64,6 +65,12 @@ public class BungeeTabPlayer extends ITabPlayer {
@Override
public boolean hasPermission(String permission) {
if (TAB.getInstance().getConfiguration().bukkitPermissions) {
String merge = "hasPermission:" + permission;
Main.plm.requestAttribute(this, merge);
if (!attributes.containsKey(merge)) return false;
return Boolean.parseBoolean(attributes.get(merge));
}
return player.hasPermission(permission);
}
@@ -97,7 +104,7 @@ public class BungeeTabPlayer extends ITabPlayer {
}
/**
* Returns packet ID for this packet of provided packet class
* Returns packet ID for this player of provided packet class
* @param clazz - packet class
* @return - packet ID
*/
@@ -117,7 +124,10 @@ public class BungeeTabPlayer extends ITabPlayer {
@Override
public boolean isVanished() {
return ProxyServer.getInstance().getPluginManager().getPlugin("PremiumVanish") != null && BungeeVanishAPI.isInvisible(player);
if (ProxyServer.getInstance().getPluginManager().getPlugin("PremiumVanish") != null && BungeeVanishAPI.isInvisible(player)) return true;
Main.plm.requestAttribute(this, "vanished");
if (!attributes.containsKey("vanished")) return false;
return Boolean.parseBoolean(attributes.get("vanished"));
}
@Override
@@ -138,4 +148,9 @@ public class BungeeTabPlayer extends ITabPlayer {
public boolean isOnline() {
return player.isConnected();
}
@Override
public int getGamemode() {
return ((UserConnection)player).getGamemode();
}
}
@@ -5,6 +5,7 @@ import java.util.ArrayList;
import org.bstats.bungeecord.Metrics;
import org.bstats.charts.SimplePie;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PluginMessageHandler;
@@ -81,14 +82,23 @@ public class Main extends Plugin {
sender.sendMessage(message.replace('&', '\u00a7'));
}
} else {
TAB.getInstance().command.execute(sender instanceof ProxiedPlayer ? TAB.getInstance().getPlayer(((ProxiedPlayer)sender).getUniqueId()) : null, args);
TabPlayer p = null;
if (sender instanceof ProxiedPlayer) {
p = TAB.getInstance().getPlayer(((ProxiedPlayer)sender).getUniqueId());
if (p == null) return; //player not loaded correctly
}
TAB.getInstance().command.execute(p, args);
}
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
if (TAB.getInstance().isDisabled()) return new ArrayList<String>();
return TAB.getInstance().command.complete(sender instanceof ProxiedPlayer ? TAB.getInstance().getPlayer(((ProxiedPlayer)sender).getUniqueId()) : null, args);
TabPlayer p = null;
if (sender instanceof ProxiedPlayer) {
p = TAB.getInstance().getPlayer(((ProxiedPlayer)sender).getUniqueId());
if (p == null) return new ArrayList<String>(); //player not loaded correctly
}
return TAB.getInstance().command.complete(p, args);
}
}
}
@@ -28,7 +28,7 @@ public class PremiumVanishListener implements Listener {
if (list == null) return;
TabPlayer vanished = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
for (TabPlayer all : TAB.getInstance().getPlayers()) {
boolean perm = all.hasPermission(list.PREMIUMVANISH_SEE_VANISHED_PERMISSION);
boolean perm = all.hasPermission("tab.seevanished");
if (all == vanished || perm) {
if (perm && list.vanishedAsSpectators) {
all.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_GAME_MODE, new PlayerInfoData(vanished.getUniqueId(), EnumGamemode.SPECTATOR)), TabFeature.GLOBAL_PLAYERLIST);
+4
View File
@@ -0,0 +1,4 @@
name: ${project.parent.name}
main: me.neznamy.tab.platforms.bungeecord.Main
version: ${project.parent.version}
author: NEZNAMY
@@ -1,205 +1,214 @@
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
#changing nametags, affecting sorting, collision rule
change-nametag-prefix-suffix: true
#prefix/suffix in tablist
change-tablist-prefix-suffix: true
#header/footer
enable-header-footer: true
global-playerlist:
enabled: false
display-others-as-spectators: false
display-vanished-players-as-spectators: true
isolate-unlisted-servers: false
spy-servers:
- spyserver1
- spyserver2
server-groups:
lobbies:
- lobby1
- lobby2
group2:
- server1
- server2
header:
- "&3&lServer name"
- "&6&lRank: %rank%"
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
- "&7Online players: &f%online%"
- "&6Online staff: &e%staffonline%"
- ""
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- "%animation:MyAnimation1%"
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- ""
- "&7Visit our webpage %animation:web%"
#supports any numeric placeholder, set to "" to disable the feature
yellow-number-in-tablist: "%ping%"
#literally what it says
group-sorting-priority-list:
- owner
- admin
- mod
- helper
- builder
- premium
- player
- default
Groups:
Owner:
tabprefix: "&0&l[&a&lOwner&0&l] &a"
tagprefix: "&2&lOwner &a"
Admin:
tabprefix: "&0&l[&4&lAdmin&0&l] &1"
tagprefix: "%animation:admin_prefix%"
Player:
tabprefix: "&0&l[&7&lPlayer&0&l] &3"
tagprefix: "&2&lPlayer &3"
example_group:
header:
- "This is an example of per-group header/footer"
footer:
- "applied to a group"
#default settings for all groups, all groups will take properties from this section unless player's primary group overrides a specific setting
_OTHER_:
tabprefix: "%rank% "
tagprefix: "%rank% "
Users:
_NEZNAMY_:
tabprefix: "&6&lTAB Author &a"
tagprefix: "&6&lTAB &r&a"
#additional very limited line of text below nametag
#don't forget this enables on all player entities, which includes NPCs
#tags so you can find this section using the mighty ctrl + f feature: [NPC, Citizens, below_name, health]
classic-vanilla-belowname:
#enabling the feature
enabled: true
#number or numeric placeholder, supports per-player placeholders
number: "%ping%"
#text after number, must be same on all players
text: "Ping"
#controlling the collision rule
enable-collision: true
#using the opposite of the setting above for worlds
revert-collision-rule-in-worlds:
- revertedworld
#preventing players with spectator gamemode from being moved to the bottom of tablist with transparent name
do-not-move-spectators: false
#allows players with tab.spectatorbypass permission to see spectators moved despite the option above enabled
allow-spectator-bypass-permission: false
per-server-settings:
server1:
header:
- "an example of world with custom"
footer:
- "header/footer and prefix/suffix"
Groups:
Owner:
tabprefix: "&0&l[&a&lOwner&0&l] &a"
tagprefix: "&2&lOwner &a"
server2-server3:
header:
- "This is a shared header for"
- "server2 and server3"
placeholders:
#%date%
date-format: "dd.MM.yyyy"
#%time%
time-format: "[HH:mm:ss / h:mm a]"
#in hours, in case of wrong timezone
time-offset: 0
#don't forget color codes
remove-strings:
- "[]"
- "< >"
#aliases for %server%
server-aliases:
hub: "&eHub"
survival: "&bSurvival"
plot: "&cPlot"
#aliases for %rank%, this is not the place where group prefix is defined!
rank-aliases:
Admin: "&4&lADMIN"
Mod: "&b&lMOD"
Premium: "&6&lPREMIUM"
Ultra: "&b&lULTRA"
Legend: "&a&lLEGEND"
Titan: "&c&lTITAN"
Youtuber: "&c&lYOUTUBE"
_OTHER_: "&7No Rank"
disable-features-in-servers:
header-footer:
- disabledserver
tablist-names:
- disabledserver
nametag:
- disabledserver
yellow-number:
- disabledserver
bossbar:
- disabledserver
belowname:
- disabledserver
placeholderapi-refresh-intervals:
default-refresh-interval: 100
server:
"%server_uptime%": 1000
"%server_tps_1_colored%": 1000
player:
"%player_health%": 200
"%player_ping%": 1000
"%vault_prefix%": 1000
relational:
"%rel_factionsuuid_relation_color%": 500
#permission node is tab.sort.<permission group from sorting list>
sort-players-by-permissions: false
#taking primary group from permission plugin
use-primary-group: true
#if the option above is false, all groups are taken from permission plugin and the one higher in this list is used as primary
primary-group-finding-list:
- Owner
- Admin
- Helper
- default
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list above> permission
assign-groups-by-permissions: false
#preventing other plugins from overriding TAB
anti-override:
scoreboard-teams: true
scoreboard-objectives: true
tablist-names: true
header-footer: true
usernames: true
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
#taking permission nodes and groups from backend servers
use-bukkit-permissions-manager: false
#changing nametags, affecting sorting, collision rule
change-nametag-prefix-suffix: true
#prefix/suffix in tablist
change-tablist-prefix-suffix: true
#header/footer
enable-header-footer: true
global-playerlist:
enabled: false
display-others-as-spectators: false
display-vanished-players-as-spectators: true
isolate-unlisted-servers: false
spy-servers:
- spyserver1
- spyserver2
server-groups:
lobbies:
- lobby1
- lobby2
group2:
- server1
- server2
header:
- "&3&lServer name"
- "&6&lRank: %rank%"
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
- "&7Online players: &f%online%"
- "&6Online staff: &e%staffonline%"
- ""
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- "%animation:MyAnimation1%"
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- ""
- "&7Visit our webpage %animation:web%"
#supports any numeric placeholder, set to "" to disable the feature
yellow-number-in-tablist: "%ping%"
group-sorting-priority-list:
- owner
- admin
- mod
- helper
- builder
- premium
- player
- default
Groups:
Owner:
tabprefix: "&0&l[&a&lOwner&0&l] &a"
tagprefix: "&2&lOwner &a"
Admin:
tabprefix: "&0&l[&4&lAdmin&0&l] &1"
tagprefix: "%animation:admin_prefix%"
Player:
tabprefix: "&0&l[&7&lPlayer&0&l] &3"
tagprefix: "&2&lPlayer &3"
example_group:
header:
- "This is an example of per-group header/footer"
footer:
- "applied to a group"
#default settings for all groups, all groups will take properties from this section unless player's primary group overrides a specific setting
_OTHER_:
tabprefix: "%rank% "
tagprefix: "%rank% "
Users:
_NEZNAMY_:
tabprefix: "&6&lTAB Author &a"
tagprefix: "&6&lTAB &r&a"
#additional very limited line of text below nametag
#don't forget this enables on all player entities, which includes NPCs
#tags so you can find this section using the mighty ctrl + f feature: [NPC, Citizens, below_name, health]
classic-vanilla-belowname:
#enabling the feature
enabled: true
#number or numeric placeholder, supports per-player placeholders
number: "%ping%"
#text after number, must be same on all players
text: "Ping"
#controlling the collision rule
enable-collision: true
#using the opposite of the setting above for servers
revert-collision-rule-in-servers:
- revertedserver
#preventing players with spectator gamemode from being moved to the bottom of tablist with transparent name
do-not-move-spectators: false
#allows players with tab.spectatorbypass permission to see spectators moved despite the option above enabled
allow-spectator-bypass-permission: false
per-server-settings:
server1:
header:
- "an example of world with custom"
footer:
- "header/footer and prefix/suffix"
Groups:
Owner:
tabprefix: "&0&l[&a&lOwner&0&l] &a"
tagprefix: "&2&lOwner &a"
server2-server3:
header:
- "This is a shared header for"
- "server2 and server3"
placeholders:
#%date%
date-format: "dd.MM.yyyy"
#%time%
time-format: "[HH:mm:ss / h:mm a]"
#in hours, in case of wrong timezone
time-offset: 0
#don't forget color codes
remove-strings:
- "[]"
- "< >"
#aliases for %server%
server-aliases:
hub: "&eHub"
survival: "&bSurvival"
plot: "&cPlot"
#aliases for %rank%, this is not the place where group prefix is defined!
rank-aliases:
Admin: "&4&lADMIN"
Mod: "&b&lMOD"
Premium: "&6&lPREMIUM"
Ultra: "&b&lULTRA"
Legend: "&a&lLEGEND"
Titan: "&c&lTITAN"
Youtuber: "&c&lYOUTUBE"
_OTHER_: "&7No Rank"
disable-features-in-servers:
header-footer:
- disabledserver
tablist-names:
- disabledserver
nametag:
- disabledserver
yellow-number:
- disabledserver
bossbar:
- disabledserver
belowname:
- disabledserver
placeholderapi-refresh-intervals:
default-refresh-interval: 100
server:
"%server_uptime%": 1000
"%server_tps_1_colored%": 1000
player:
"%player_health%": 200
"%player_ping%": 1000
"%vault_prefix%": 1000
relational:
"%rel_factionsuuid_relation_color%": 500
#permission node is tab.sort.<permission group from sorting list>
sort-players-by-permissions: false
#taking primary group from permission plugin
use-primary-group: true
#if the option above is false, all groups are taken from permission plugin and the one higher in this list is used as primary
primary-group-finding-list:
- Owner
- Admin
- Helper
- default
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list above> permission
assign-groups-by-permissions: false
#preventing other plugins from overriding TAB
anti-override:
scoreboard-teams: true
scoreboard-objectives: true
tablist-names: true
usernames: true
#permanently invisible nametags
invisible-nametags: false
#faking ping of players
ping-spoof:
enabled: false
value: 0
+91
View File
@@ -0,0 +1,91 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>me.neznamy</groupId>
<artifactId>TAB</artifactId>
<version>2.9.0</version>
</parent>
<artifactId>jar</artifactId>
<build>
<finalName>TAB-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<artifactSet>
<includes>
<include>com.googlecode.json-simple:json-simple</include>
<include>com.github.NEZNAMY:YamlAssist</include>
<include>com.github.NEZNAMY:VelocityPacketInjector</include>
<include>org.bstats:bstats-base</include>
<include>org.bstats:bstats-bungeecord</include>
<include>org.bstats:bstats-velocity</include>
<include>me.neznamy:*</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>me.neznamy.tab.platforms</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>me.neznamy</groupId>
<artifactId>bukkit</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>me.neznamy</groupId>
<artifactId>bungeecord</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>me.neznamy</groupId>
<artifactId>shared</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</exclusion>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>me.neznamy</groupId>
<artifactId>velocity-1.1.0</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
+16 -430
View File
@@ -1,13 +1,25 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.neznamy</groupId>
<artifactId>TAB</artifactId>
<version>2.8.11-pre9</version>
<name>TAB</name>
<version>2.9.0</version>
<packaging>pom</packaging>
<description>An all-in-one solution that works</description>
<modules>
<module>shared</module>
<module>bukkit</module>
<module>bungeecord</module>
<module>velocity-1.1.0</module>
<module>jar</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<resources>
<resource>
@@ -15,431 +27,5 @@
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<artifactSet>
<includes>
<include>com.googlecode.json-simple:json-simple</include>
<include>com.github.NEZNAMY:YamlAssist</include>
<include>com.github.NEZNAMY:VelocityPacketInjector</include>
<include>org.bstats:bstats-base</include>
<include>org.bstats:bstats-bungeecord</include>
<include>org.bstats:bstats-velocity</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>me.neznamy.tab.platforms</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<!-- Spigot API -->
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<!-- PlaceholderAPI -->
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<!-- Vault -->
<!-- PremiumVanishAPI -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<!-- LuckPerms5 -->
<!-- BungeeCord -->
<repository>
<id>luck-repo</id>
<url>https://ci.lucko.me/plugin/repository/everything/</url>
</repository>
<!-- ViaVersion -->
<repository>
<id>viaversion</id>
<url>https://repo.viaversion.com/</url>
</repository>
<repository>
<id>velocity</id>
<url>https://nexus.velocitypowered.com/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- json-simple, shaded -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- BungeeCord -->
<!-- com.google.common.io -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.16-R0.5-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-haproxy</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-unix-common</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>brigadier</artifactId>
</exclusion>
<exclusion>
<groupId>jline</groupId>
<artifactId>jline</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
</exclusion>
<exclusion>
<groupId>net.sf.trove4j</groupId>
<artifactId>core</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-query</artifactId>
</exclusion>
<exclusion>
<groupId>se.llbit</groupId>
<artifactId>jo-nbt</artifactId>
</exclusion>
<exclusion>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
</exclusion>
<exclusion>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- PlaceholderAPI -->
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.10.9</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Vault -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- LuckPerms5 -->
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.2</version>
<scope>provided</scope>
</dependency>
<!-- ViaVersion -->
<dependency>
<groupId>us.myles</groupId>
<artifactId>viaversion</artifactId>
<version>3.2.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>us.myles</groupId>
<artifactId>viaversion-common</artifactId>
</exclusion>
<exclusion>
<groupId>us.myles</groupId>
<artifactId>viaversion-bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>us.myles</groupId>
<artifactId>viaversion-bungee</artifactId>
</exclusion>
<exclusion>
<groupId>us.myles</groupId>
<artifactId>viaversion-fabric</artifactId>
</exclusion>
<exclusion>
<groupId>us.myles</groupId>
<artifactId>viaversion-sponge</artifactId>
</exclusion>
<exclusion>
<groupId>us.myles</groupId>
<artifactId>viaversion-velocity</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- PremiumVanishAPI -->
<dependency>
<groupId>com.github.MyzelYam</groupId>
<artifactId>PremiumVanishAPI</artifactId>
<version>2.0.3</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- YamlAssist -->
<dependency>
<groupId>com.github.NEZNAMY</groupId>
<artifactId>YamlAssist</artifactId>
<version>1.0.3</version>
</dependency>
<!-- Velocity API -->
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>1.1.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.moandjiezana.toml</groupId>
<artifactId>toml4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
</exclusion>
<exclusion>
<groupId>org.spongepowered</groupId>
<artifactId>configurate-gson</artifactId>
</exclusion>
<exclusion>
<groupId>org.spongepowered</groupId>
<artifactId>configurate-hocon</artifactId>
</exclusion>
<exclusion>
<groupId>com.mojang</groupId>
<artifactId>brigadier</artifactId>
</exclusion>
<exclusion>
<groupId>org.spongepowered</groupId>
<artifactId>configurate-yaml</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>
adventure-text-serializer-legacy-text3
</artifactId>
</exclusion>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>
adventure-text-serializer-legacy
</artifactId>
</exclusion>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>
adventure-text-serializer-plain
</artifactId>
</exclusion>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>text-serializer-plain</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.j2objc</groupId>
<artifactId>j2objc-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
</exclusion>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>text-serializer-legacy</artifactId>
</exclusion>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>text-serializer-gson</artifactId>
</exclusion>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>examination-string</artifactId>
</exclusion>
<exclusion>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Bungee Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bungeecord</artifactId>
<version>2.2.1</version>
<scope>compile</scope>
</dependency>
<!-- Velocity Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-velocity</artifactId>
<version>2.2.1</version>
<scope>compile</scope>
</dependency>
<!-- Missing velocity packets -->
<dependency>
<groupId>com.github.NEZNAMY</groupId>
<artifactId>VelocityPacketInjector</artifactId>
<version>1.0.0_fix</version>
</dependency>
</dependencies>
</project>
+121
View File
@@ -0,0 +1,121 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>me.neznamy</groupId>
<artifactId>TAB</artifactId>
<version>2.9.0</version>
</parent>
<artifactId>shared</artifactId>
<repositories>
<!-- LuckPerms5 -->
<repository>
<id>luck-repo</id>
<url>https://ci.lucko.me/plugin/repository/everything/</url>
</repository>
<!-- YamlAssist -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.63.Final</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>
netty-transport-native-unix-common
</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-event</artifactId>
</exclusion>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-chat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- json-simple, shaded -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- LuckPerms5 -->
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.2</version>
<scope>provided</scope>
</dependency>
<!-- YamlAssist -->
<dependency>
<groupId>com.github.NEZNAMY</groupId>
<artifactId>YamlAssist</artifactId>
<version>1.0.4</version>
</dependency>
</dependencies>
</project>
@@ -1,7 +1,7 @@
package me.neznamy.tab.api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -12,8 +12,8 @@ import java.util.Set;
*/
public class ArmorStandManager {
//list of registered armor stands
private Map<String, ArmorStand> armorStands = new LinkedHashMap<String, ArmorStand>();
//map of registered armor stands
private Map<String, ArmorStand> armorStands = Collections.synchronizedMap(new LinkedHashMap<String, ArmorStand>());
/**
* Adds armor stand into list
@@ -23,7 +23,7 @@ public class ArmorStandManager {
public void addArmorStand(String name, ArmorStand as) {
armorStands.put(name, as);
}
/**
* Removes armor stand from list. It is not automatically destroyed for players
* @param name - name of armor stand to remove
@@ -31,14 +31,16 @@ public class ArmorStandManager {
public void removeArmorStand(String name) {
armorStands.remove(name);
}
/**
* Spawns all armor stands for specified viewer and adds them into nearby players
* @param viewer - player to spawn armor stands for
*/
public void spawn(TabPlayer viewer) {
if (viewer.getVersion().getMinorVersion() < 8) return;
getArmorStands().forEach(a -> a.spawn(viewer));
synchronized (armorStands) {
armorStands.values().forEach(a -> a.spawn(viewer));
}
}
/**
@@ -46,14 +48,18 @@ public class ArmorStandManager {
* @param sneaking - new sneaking status
*/
public void sneak(boolean sneaking) {
getArmorStands().forEach(a -> a.sneak(sneaking));
synchronized (armorStands) {
armorStands.values().forEach(a -> a.sneak(sneaking));
}
}
/**
* Teleports armor stands to player's current location for all nearby players
*/
public void teleport() {
getArmorStands().forEach(a -> a.teleport());
synchronized (armorStands) {
armorStands.values().forEach(a -> a.teleport());
}
}
/**
@@ -61,14 +67,18 @@ public class ArmorStandManager {
* @param viewer - player to teleport armor stands for
*/
public void teleport(TabPlayer viewer) {
getArmorStands().forEach(a -> a.teleport(viewer));
synchronized (armorStands) {
armorStands.values().forEach(a -> a.teleport(viewer));
}
}
/**
* Refreshes name of all armor stands for all nearby players
*/
public void refresh() {
getArmorStands().forEach(a -> a.refresh());
synchronized (armorStands) {
armorStands.values().forEach(a -> a.refresh());
}
}
/**
@@ -76,7 +86,9 @@ public class ArmorStandManager {
* @param force - true if packets should be sent despite seemingly not needed
*/
public void updateVisibility(boolean force) {
getArmorStands().forEach(a -> a.updateVisibility(force));
synchronized (armorStands) {
armorStands.values().forEach(a -> a.updateVisibility(force));
}
}
/**
@@ -84,14 +96,18 @@ public class ArmorStandManager {
* @param viewer - player to remove
*/
public void unregisterPlayer(TabPlayer viewer) {
getArmorStands().forEach(a -> a.removeFromRegistered(viewer));
synchronized (armorStands) {
armorStands.values().forEach(a -> a.removeFromRegistered(viewer));
}
}
/**
* Sends destroy packet of all armor stands to everyone and clears nearby players list
*/
public void destroy() {
getArmorStands().forEach(a -> a.destroy());
synchronized (armorStands) {
armorStands.values().forEach(a -> a.destroy());
}
}
/**
@@ -99,7 +115,9 @@ public class ArmorStandManager {
* @param viewer - player to destroy armor stands for
*/
public void destroy(TabPlayer viewer) {
getArmorStands().forEach(a -> a.destroy(viewer));
synchronized (armorStands) {
armorStands.values().forEach(a -> a.destroy(viewer));
}
}
/**
@@ -108,28 +126,32 @@ public class ArmorStandManager {
* @return true if armor stand with specified entity id exists, false if not
*/
public boolean hasArmorStandWithID(int entityId) {
for (ArmorStand as : getArmorStands()) {
if (as.getEntityId() == entityId) {
return true;
synchronized (armorStands) {
for (ArmorStand as : armorStands.values()) {
if (as.getEntityId() == entityId) {
return true;
}
}
return false;
}
return false;
}
/**
* Returns a copy of list of all registered armor stands
* @return copy of armor stands
*/
public Collection<ArmorStand> getArmorStands(){
return new ArrayList<>(armorStands.values());
return armorStands.values();
}
/**
* Returns list of nearby players
* @return list of nearby players
*/
public Set<TabPlayer> getNearbyPlayers(){
if (armorStands.values().size() == 0) return new HashSet<>(); //not initialized yet
return armorStands.values().iterator().next().getNearbyPlayers();
synchronized (armorStands) {
return armorStands.values().iterator().next().getNearbyPlayers();
}
}
}
@@ -56,7 +56,6 @@ public class TABAPI {
/**
* Enables unlimited nametag mode permanently in config
* @throws IllegalStateException if called from a proxy
* @see isUnlimitedNameTagModeEnabled
*/
public static void enableUnlimitedNameTagModePermanently() {
@@ -78,6 +77,7 @@ public class TABAPI {
PlaceholderManager pl = TAB.getInstance().getPlaceholderManager();
pl.registerPlaceholder(placeholder);
pl.allUsedPlaceholderIdentifiers.add(placeholder.getIdentifier());
pl.refreshPlaceholderUsage();
}
/**
@@ -91,6 +91,7 @@ public class TABAPI {
PlaceholderManager pl = TAB.getInstance().getPlaceholderManager();
pl.registerPlaceholder(placeholder);
pl.allUsedPlaceholderIdentifiers.add(placeholder.getIdentifier());
pl.refreshPlaceholderUsage();
}
/**
@@ -102,6 +103,7 @@ public class TABAPI {
PlaceholderManager pl = TAB.getInstance().getPlaceholderManager();
pl.registerPlaceholder(placeholder);
pl.allUsedPlaceholderIdentifiers.add(placeholder.getIdentifier());
pl.refreshPlaceholderUsage();
}
/**
@@ -140,7 +142,7 @@ public class TABAPI {
* @return the bossbar
*/
public static BossBar createBossBar(String name, String title, float progress, BarColor color, BarStyle style) {
return createBossBar(name, title, progress+"", color.toString(), style.toString());
return createBossBar(name, title, String.valueOf(progress), color.toString(), style.toString());
}
/**
@@ -147,7 +147,7 @@ public interface TabPlayer {
/**
* Returns player's uuid used in tablist. This may only be different than real uuid if
* TAB is installed on velocity with some specific velocity setup
* @return
* @return player's uuid in tablist
*/
public UUID getTablistUUID();
@@ -232,7 +232,6 @@ public interface TabPlayer {
/**
* Toggles armor stands preview mode for the player
* @throws IllegalStateException - if unlimited nametag mode is not enabled
*/
public void toggleNametagPreview();
@@ -272,11 +271,6 @@ public interface TabPlayer {
*/
public boolean isLoaded();
/**
* Marks the player as loaded and makes isLoaded() return true
*/
public void markAsLoaded();
/**
* Returns true if player has bossbar visible, false if player hid bossbar(s) using bossbar
* toggle command
@@ -288,8 +282,9 @@ public interface TabPlayer {
* Sets bossbar visibility for the player and performs all packet sending if new value is
* different than previous value (true->false, false->true)
* @param visible - whether bossbar should be visible or not
* @param sendToggleMessage - if toggle message should be sent or not
*/
public void setBossbarVisible(boolean visible);
public void setBossbarVisible(boolean visible, boolean sendToggleMessage);
/**
* Returns list of all bossbars player can currently see
@@ -426,30 +421,6 @@ public interface TabPlayer {
* @param armorStandManager - new instance
*/
public void setArmorStandManager(ArmorStandManager armorStandManager);
/**
* Internal method to set world name
* @param name - name of new world
*/
public void setWorldName(String name);
/**
* Internal method to set team name
* @param name - name of team
*/
public void setTeamName(String name);
/**
* Internal method to set team name note
* @param note - team name note
*/
public void setTeamNameNote(String note);
/**
* Internal method to track currently active scoreboard
* @param board - new active scoreboard
*/
public void setActiveScoreboard(Scoreboard board);
/**
* Internal method that returns player's active scoreboard
@@ -458,22 +429,39 @@ public interface TabPlayer {
public Scoreboard getActiveScoreboard();
/**
* Internal method that sets player's group
* @param group - new group
* @param refreshIfChanged - whether to refresh properties if value changed or not
* 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 name - forced team name
*/
public void setGroup(String group, boolean refreshIfChanged);
public void forceTeamName(String name);
/**
* Internal method to track scoreboards from other plugins
* @param objectiveName - objective name of scoreboard from other plugins
* Returns forced team name or null if not forced
* @return forced team name or null if not forced
*/
public void setOtherPluginScoreboard(String objectiveName);
public String getForcedTeamName();
/**
* Internal method that returns scoreboard objective name of scoreboard from other plugin or null
* if not scoreboard from other plugin is set
* @return name of scoreboard objective from other plugin
* Returns gamemode of the player (0 for survival, 1 creative, 2 adventure, 3 spectator)
* @return gamemode of the player
*/
public String getOtherPluginScoreboard();
public int getGamemode();
/**
* 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();
/**
* Resumes team handling if it was before paused using pauseTeamHandling(), if not, nothing happens
*/
public void resumeTeamHandling();
/**
* 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();
}
@@ -57,4 +57,31 @@ public interface BossBar {
* @param style - new style
*/
public void setStyle(BarStyle style);
}
/**
* Returns title of the bossbar
* @return title of the bossbar
*/
public String getTitle();
/**
* Returns progress of the bossbar as string, which is either entered string
* containing placeholders or entered number converted to string
* @return entered progress as string
*/
public String getProgress();
/**
* Returns color of the bossbar as string, which is either entered string
* containing placeholders or entered enum value converted to string
* @return entered color as string
*/
public String getColor();
/**
* Returns style of the bossbar as string, which is either entered string
* containing placeholders or entered enum value converted to string
* @return entered style as string
*/
public String getStyle();
}
@@ -10,6 +10,7 @@ import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import me.neznamy.tab.api.bossbar.BarColor;
@@ -59,16 +60,6 @@ public class ErrorManager {
printError(message, null, false);
}
/**
* Prints an error message into errors.txt file and returns first argument
* @param defaultValue - value to return
* @param message - message to print
* @return first argument
*/
public <T> T printError(T defaultValue, String message) {
return printError(defaultValue, message, null);
}
/**
* Prints an error message and stack trace into errors.txt file and returns first argument
* @param defaultValue - value to return
@@ -117,12 +108,12 @@ public class ErrorManager {
if (file.length() > 1000000) return; //not going over 1 MB
BufferedWriter buf = new BufferedWriter(new FileWriter(file, true));
if (message != null) {
write(buf, "&c[TAB v" + tab.getPluginVersion() + (tab.isPremium() ? " Premium": "") + "] " + message, intoConsoleToo);
write(buf, "&c[TAB v" + tab.getPluginVersion() + (tab.isPremium() ? " Premium": "") + "] ", message.replace('\u00a7', '&'), intoConsoleToo);
}
if (error != null) {
write(buf, "&c" + error.getClass().getName() + ": " + error.getMessage(), intoConsoleToo);
write(buf, "&c", error.getClass().getName() + ": " + error.getMessage(), intoConsoleToo);
for (StackTraceElement ste : error.getStackTrace()) {
write(buf, "&c at " + ste.toString(), intoConsoleToo);
write(buf, "&c at ", ste.toString(), intoConsoleToo);
}
}
buf.close();
@@ -158,9 +149,9 @@ public class ErrorManager {
* @param forceConsole - send into console even without debug mode
* @throws IOException - if IO writer operation fails
*/
private void write(BufferedWriter buf, String message, boolean forceConsole) throws IOException {
buf.write(getCurrentTime() + removeColors(message) + System.getProperty("line.separator"));
if (tab.debugMode || forceConsole) tab.getPlatform().sendConsoleMessage(message, true);
private void write(BufferedWriter buf, String prefix, String message, boolean forceConsole) throws IOException {
buf.write(getCurrentTime() + removeColors(prefix) + message + System.getProperty("line.separator"));
if (tab.debugMode || forceConsole) tab.getPlatform().sendConsoleMessage(prefix.replace('&', '\u00a7') + message, false);
}
/**
@@ -420,7 +411,7 @@ public class ErrorManager {
*/
public SimpleDateFormat createDateFormat(String value, String defaultValue) {
try {
return new SimpleDateFormat(value);
return new SimpleDateFormat(value, Locale.ENGLISH);
} catch (Exception e) {
startupWarn("Format \"" + value + "\" is not a valid date/time format. Did you try to use color codes?");
return new SimpleDateFormat(defaultValue);
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -14,21 +15,16 @@ import me.neznamy.tab.shared.features.NameTag;
import me.neznamy.tab.shared.features.types.Feature;
import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.Refreshable;
import me.neznamy.tab.shared.features.types.event.ChatEventListener;
import me.neznamy.tab.shared.features.types.event.CommandListener;
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
import me.neznamy.tab.shared.features.types.event.RespawnEventListener;
import me.neznamy.tab.shared.features.types.event.SneakEventListener;
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
import me.neznamy.tab.shared.features.types.packet.DisplayObjectivePacketListener;
import me.neznamy.tab.shared.features.types.packet.HeaderFooterPacketListener;
import me.neznamy.tab.shared.features.types.packet.LoginPacketListener;
import me.neznamy.tab.shared.features.types.packet.ObjectivePacketListener;
import me.neznamy.tab.shared.features.types.packet.PlayerInfoPacketListener;
import me.neznamy.tab.shared.features.types.packet.RawPacketListener;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerListHeaderFooter;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardDisplayObjective;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardObjective;
@@ -95,7 +91,11 @@ public class FeatureManager {
* @return list of all loaded features
*/
public Collection<Feature> getAllFeatures(){
return new ArrayList<Feature>(features.values());
try {
return features.values();
} catch (ConcurrentModificationException e) {
return getAllFeatures();
}
}
/**
@@ -153,6 +153,7 @@ public class FeatureManager {
* @throws Exception
*/
public Object onPacketPlayOutPlayerInfo(TabPlayer receiver, Object packet) throws Exception {
if (receiver.getVersion().getMinorVersion() < 8) return packet;
List<PlayerInfoPacketListener> listeners = new ArrayList<PlayerInfoPacketListener>();
for (Feature f : getAllFeatures()) {
if (f instanceof PlayerInfoPacketListener) listeners.add((PlayerInfoPacketListener) f);
@@ -165,7 +166,7 @@ public class FeatureManager {
tab.getCPUManager().addTime(TabFeature.PACKET_DESERIALIZING, UsageType.PACKET_PLAYER_INFO, System.nanoTime()-time);
for (PlayerInfoPacketListener f : listeners) {
time = System.nanoTime();
((PlayerInfoPacketListener)f).onPacketSend(receiver, info);
f.onPacketSend(receiver, info);
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PACKET_READING_OUT, System.nanoTime()-time);
}
time = System.nanoTime();
@@ -208,7 +209,7 @@ public class FeatureManager {
((JoinEventListener)f).onJoin(connectedPlayer);
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PLAYER_JOIN_EVENT, System.nanoTime()-time);
}
connectedPlayer.markAsLoaded();
((ITabPlayer)connectedPlayer).markAsLoaded();
tab.debug("Player join of " + connectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
}
@@ -226,7 +227,7 @@ public class FeatureManager {
return;
}
String from = changed.getWorldName();
changed.setWorldName(to);
((ITabPlayer)changed).setWorldName(to);
for (Feature f : getAllFeatures()) {
if (!(f instanceof WorldChangeListener)) continue;
long time = System.nanoTime();
@@ -295,20 +296,6 @@ public class FeatureManager {
}
}
/**
* Calls onRespawn on all featurs that implement RespawnEventListener and measures how long it took them to process
* @param respawned - player who respawned
*/
public void onRespawn(TabPlayer respawned) {
if (respawned == null) return;
for (Feature f : getAllFeatures()) {
if (!(f instanceof RespawnEventListener)) continue;
long time = System.nanoTime();
((RespawnEventListener)f).onRespawn(respawned);
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PLAYER_RESPAWN_EVENT, System.nanoTime()-time);
}
}
/**
* Calls onLoginPacket on all featurs that implement LoginPacketListener and measures how long it took them to process
* @param packetReceiver - player who received the packet
@@ -359,59 +346,6 @@ public class FeatureManager {
}
}
/**
* Calls onPacketSend on all featurs that implement HeaderFooterPacketListener and measures how long it took them to process
* @param packetReceiver - player who received the packet
* @param packet - the packet
* @return true if packet should be cancelled, false if not
* @throws Exception - if something fails
*/
public boolean onHeaderFooter(TabPlayer packetReceiver, Object packet) throws Exception {
long time = System.nanoTime();
PacketPlayOutPlayerListHeaderFooter display = tab.getPacketBuilder().readHeaderFooter(packet, packetReceiver.getVersion());
tab.getCPUManager().addTime(TabFeature.PACKET_DESERIALIZING, UsageType.PACKET_HEADER_FOOTER, System.nanoTime()-time);
for (Feature f : getAllFeatures()) {
if (!(f instanceof HeaderFooterPacketListener)) continue;
time = System.nanoTime();
boolean cancel = ((HeaderFooterPacketListener)f).onPacketSend(packetReceiver, display);
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.ANTI_OVERRIDE, System.nanoTime()-time);
if (cancel) return true;
}
return false;
}
/**
* Calls onChat on all featurs that implement ChatEventListener and measures how long it took them to process
* @param sender - player who sent chat message
* @param message - the message
* @param cancelled - if event is cancelled or not
* @return true if event should be cancelled, false if not
*/
public boolean onChat(TabPlayer sender, String message, boolean cancelled) {
boolean cancel = false;
for (Feature f : getAllFeatures()) {
if (!(f instanceof ChatEventListener)) continue;
long time = System.nanoTime();
if (((ChatEventListener)f).onChat(sender, message, cancelled)) cancel = true;
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PLAYER_CHAT_EVENT, System.nanoTime()-time);
}
return cancel;
}
/**
* Calls onSneak on all featurs that implement SneakEventListener and measures how long it took them to process
* @param player - player who sneaked
* @param isSneaking - new sneak status
*/
public void onSneak(TabPlayer player, boolean isSneaking) {
for (Feature f : getAllFeatures()) {
if (!(f instanceof SneakEventListener)) continue;
long time = System.nanoTime();
((SneakEventListener)f).onSneak(player, isSneaking);
tab.getCPUManager().addTime(f.getFeatureType(), UsageType.PLAYER_TOGGLE_SNEAK_EVENT, System.nanoTime()-time);
}
}
/**
* Returns currently loaded nametag feature or null if disabled
* @return currently loaded nametag feature
@@ -1,5 +1,6 @@
package me.neznamy.tab.shared;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -15,6 +16,7 @@ import me.neznamy.tab.api.bossbar.BossBar;
import me.neznamy.tab.shared.command.level1.PlayerCommand;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.features.GroupRefresher;
import me.neznamy.tab.shared.features.NameTag;
import me.neznamy.tab.shared.features.bossbar.BossBarLine;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
import me.neznamy.tab.shared.packets.IChatBaseComponent;
@@ -32,6 +34,7 @@ public abstract class ITabPlayer implements TabPlayer {
private String permissionGroup = "<null>";
private String teamName;
private String teamNameNote;
private String forcedTeamName;
private Map<String, Property> properties = new HashMap<String, Property>();
private ArmorStandManager armorStandManager;
@@ -50,6 +53,7 @@ public abstract class ITabPlayer implements TabPlayer {
private boolean scoreboardVisible;
private Scoreboard forcedScoreboard;
private String otherPluginScoreboard;
private boolean teamHandlingPaused;
protected Map<String, String> attributes = new HashMap<String, String>();
@@ -101,19 +105,20 @@ public abstract class ITabPlayer implements TabPlayer {
@Override
public void setValueTemporarily(EnumProperty type, String value) {
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
TAB.getInstance().debug("Received API request to set property " + type + " of " + getName() + " temporarily to " + value + " by " + Thread.currentThread().getStackTrace()[2].toString());
Property pr = getProperty(type.toString());
if (pr == null) throw new IllegalStateException("Feature handling this property is not enabled");
if (pr == null) throw new IllegalStateException("Feature handling this property (" + type + ") is not enabled");
pr.setTemporaryValue(value);
if (TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx") && type.toString().contains("tag")) {
setProperty("nametag",getProperty("tagprefix").getCurrentRawValue() + getProperty("customtagname").getCurrentRawValue() + getProperty("tagsuffix").getCurrentRawValue(), null);
}
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
forceRefresh();
}
@Override
public void setValuePermanently(EnumProperty type, String value) {
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
TAB.getInstance().debug("Received API request to set property " + type + " of " + getName() + " permanently to " + value + " by " + Thread.currentThread().getStackTrace()[2].toString());
((PlayerCommand)TAB.getInstance().command.subcommands.get("player")).savePlayer(null, getName(), type.toString(), value);
if (TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx") && type.toString().contains("tag")) {
setProperty("nametag", getProperty("tagprefix").getCurrentRawValue() + getProperty("customtagname").getCurrentRawValue() + getProperty("tagsuffix").getCurrentRawValue(), null);
@@ -121,6 +126,7 @@ public abstract class ITabPlayer implements TabPlayer {
Property pr = getProperty(type.toString());
if (pr == null) return;
pr.changeRawValue(value);
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
forceRefresh();
}
@@ -212,19 +218,21 @@ public abstract class ITabPlayer implements TabPlayer {
return world;
}
@Override
public void setWorldName(String name) {
world = name;
}
@Override
public void sendCustomPacket(UniversalPacketPlayOut packet) {
sendPacket(packet.create(getVersion()));
Object p = packet.create(getVersion());
long time = System.nanoTime();
sendPacket(p);
TAB.getInstance().getCPUManager().addMethodTime("sendPacket", System.nanoTime()-time);
}
@Override
public void sendCustomPacket(UniversalPacketPlayOut packet, TabFeature feature) {
sendPacket(packet.create(getVersion()));
sendCustomPacket(packet);
TAB.getInstance().getCPUManager().packetSent(feature);
}
@@ -271,7 +279,6 @@ public abstract class ITabPlayer implements TabPlayer {
return onJoinFinished;
}
@Override
public void markAsLoaded() {
onJoinFinished = true;
}
@@ -282,8 +289,28 @@ public abstract class ITabPlayer implements TabPlayer {
}
@Override
public void setBossbarVisible(boolean visible) {
public void setBossbarVisible(boolean visible, boolean sendToggleMessage) {
bossbarVisible = visible;
me.neznamy.tab.shared.features.bossbar.BossBar feature = (me.neznamy.tab.shared.features.bossbar.BossBar) TAB.getInstance().getFeatureManager().getFeature("bossbar");
if (feature == null) return;
if (hasBossbarVisible()) {
feature.detectBossBarsAndSend(this);
if (sendToggleMessage) sendMessage(TAB.getInstance().getConfiguration().translation.getString("bossbar-toggle-on"), true);
if (feature.remember_toggle_choice) {
feature.bossbar_off_players.remove(getName());
TAB.getInstance().getConfiguration().playerdata.set("bossbar-off", feature.bossbar_off_players);
}
} else {
for (BossBar line : getActiveBossBars().toArray(new BossBar[0])) {
removeBossBar(line);
}
getActiveBossBars().clear();
if (sendToggleMessage) sendMessage(TAB.getInstance().getConfiguration().translation.getString("bossbar-toggle-off"), true);
if (feature.remember_toggle_choice && !feature.bossbar_off_players.contains(getName())) {
feature.bossbar_off_players.add(getName());
TAB.getInstance().getConfiguration().playerdata.set("bossbar-off", feature.bossbar_off_players);
}
}
}
@Override
@@ -351,17 +378,16 @@ public abstract class ITabPlayer implements TabPlayer {
this.armorStandManager = armorStandManager;
}
@Override
public void setTeamName(String name) {
teamName = name;
}
@Override
public String getTeamName() {
if (forcedTeamName != null) return forcedTeamName;
return teamName;
}
@Override
public void setTeamNameNote(String note) {
teamNameNote = note;
}
@@ -393,9 +419,13 @@ public abstract class ITabPlayer implements TabPlayer {
sendMessage(scoreboardManager.scoreboard_on, true);
}
if (scoreboardManager.remember_toggle_choice) {
scoreboardManager.sb_off_players.remove(getName());
if (scoreboardManager.hiddenByDefault) {
scoreboardManager.sb_off_players.add(getName());
} else {
scoreboardManager.sb_off_players.remove(getName());
}
synchronized (scoreboardManager.sb_off_players){
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", scoreboardManager.sb_off_players);
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", new ArrayList<>(scoreboardManager.sb_off_players));
}
}
} else {
@@ -404,9 +434,13 @@ public abstract class ITabPlayer implements TabPlayer {
sendMessage(scoreboardManager.scoreboard_off, true);
}
if (scoreboardManager.remember_toggle_choice) {
scoreboardManager.sb_off_players.add(getName());
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", scoreboardManager.sb_off_players);synchronized (scoreboardManager.sb_off_players){
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", scoreboardManager.sb_off_players);
if (scoreboardManager.hiddenByDefault) {
scoreboardManager.sb_off_players.remove(getName());
} else {
scoreboardManager.sb_off_players.add(getName());
}
synchronized (scoreboardManager.sb_off_players){
TAB.getInstance().getConfiguration().playerdata.set("scoreboard-off", new ArrayList<>(scoreboardManager.sb_off_players));
}
}
}
@@ -422,7 +456,6 @@ public abstract class ITabPlayer implements TabPlayer {
return scoreboardVisible;
}
@Override
public void setActiveScoreboard(Scoreboard board) {
activeScoreboard = board;
}
@@ -432,7 +465,6 @@ public abstract class ITabPlayer implements TabPlayer {
return activeScoreboard;
}
@Override
public void setGroup(String permissionGroup, boolean refreshIfChanged) {
if (this.permissionGroup.equals(permissionGroup)) return;
if (permissionGroup != null) {
@@ -451,11 +483,6 @@ public abstract class ITabPlayer implements TabPlayer {
return forcedScoreboard != null;
}
@Override
public boolean isVanished() {
return false;
}
@Override
public void showBossBar(BossBar bossbar) {
BossBarLine line = (BossBarLine) bossbar;
@@ -498,12 +525,10 @@ public abstract class ITabPlayer implements TabPlayer {
attributes.put(attribute, value);
}
@Override
public void setOtherPluginScoreboard(String objectiveName) {
this.otherPluginScoreboard = objectiveName;
}
@Override
public String getOtherPluginScoreboard() {
return otherPluginScoreboard;
}
@@ -513,4 +538,46 @@ public abstract class ITabPlayer implements TabPlayer {
sendPacket(nmsPacket);
TAB.getInstance().getCPUManager().packetSent(feature);
}
@Override
public void forceTeamName(String name) {
if (String.valueOf(forcedTeamName).equals(name)) return;
if (name != null && name.length() > 16) throw new IllegalArgumentException("Team name cannot be more than 16 characters long.");
NameTag nametags = TAB.getInstance().getFeatureManager().getNameTagFeature();
if (nametags == null) return;
nametags.unregisterTeam(this);
forcedTeamName = name;
nametags.registerTeam(this);
if (name != null) teamNameNote = "Set using API";
}
@Override
public String getForcedTeamName() {
return forcedTeamName;
}
@Override
public void pauseTeamHandling() {
if (teamHandlingPaused) return;
NameTag f = TAB.getInstance().getFeatureManager().getNameTagFeature();
if (f != null && !f.isDisabledWorld(getWorldName())) {
f.unregisterTeam(this);
}
teamHandlingPaused = true; //setting to true after, so unregisterTeam method runs
}
@Override
public void resumeTeamHandling() {
if (!teamHandlingPaused) return;
teamHandlingPaused = false; //setting to false before, so registerTeam method runs
NameTag f = TAB.getInstance().getFeatureManager().getNameTagFeature();
if (f != null && !f.isDisabledWorld(getWorldName())) {
f.registerTeam(this);
}
}
@Override
public boolean hasTeamHandlingPaused() {
return teamHandlingPaused;
}
}
@@ -27,7 +27,7 @@ public class PacketAPI {
* @param color - color field (1.13+)
*/
public static synchronized void registerScoreboardTeam(TabPlayer to, String teamName, String prefix, String suffix, boolean enumNameTagVisibility, boolean enumTeamPush, Collection<String> players, EnumChatFormat color, TabFeature feature) {
if (to.getVersion().getMinorVersion() >= 8 && (boolean) TAB.getInstance().getConfiguration().getSecretOption("unregister-before-register", true) && TAB.getInstance().getPlatform().getSeparatorType().equals("world")) {
if (to.getVersion().getMinorVersion() >= 8 && TAB.getInstance().getConfiguration().unregisterBeforeRegister && TAB.getInstance().getPlatform().getSeparatorType().equals("world")) {
to.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName).setTeamOptions(69), feature);
}
to.sendCustomPacket(new PacketPlayOutScoreboardTeam(teamName, prefix, suffix, enumNameTagVisibility?"always":"never", enumTeamPush?"always":"never", players, 69).setColor(color), feature);
@@ -42,7 +42,7 @@ public class PacketAPI {
* @param displayType - display type of the value (only supported in Playerlist)
*/
public static synchronized void registerScoreboardObjective(TabPlayer to, String objectiveName, String title, int position, EnumScoreboardHealthDisplay displayType, TabFeature feature) {
if (to.getVersion().getMinorVersion() >= 8 && (boolean) TAB.getInstance().getConfiguration().getSecretOption("unregister-before-register", true) && TAB.getInstance().getPlatform().getSeparatorType().equals("world")) {
if (to.getVersion().getMinorVersion() >= 8 && TAB.getInstance().getConfiguration().unregisterBeforeRegister && TAB.getInstance().getPlatform().getSeparatorType().equals("world")) {
to.sendCustomPacket(new PacketPlayOutScoreboardObjective(objectiveName), feature);
}
to.sendCustomPacket(new PacketPlayOutScoreboardObjective(0, objectiveName, title, displayType), feature);
@@ -1,25 +1,19 @@
package me.neznamy.tab.shared;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.config.ConfigurationFile;
import me.neznamy.tab.shared.features.AlignedSuffix;
import me.neznamy.tab.shared.features.BelowName;
import me.neznamy.tab.shared.features.GhostPlayerFix;
import me.neznamy.tab.shared.features.GroupRefresher;
import me.neznamy.tab.shared.features.HeaderFooter;
import me.neznamy.tab.shared.features.Playerlist;
import me.neznamy.tab.shared.features.PluginInfo;
import me.neznamy.tab.shared.features.SpectatorFix;
import me.neznamy.tab.shared.features.TabObjective;
import me.neznamy.tab.shared.features.UpdateChecker;
import me.neznamy.tab.shared.features.bossbar.BossBar;
import me.neznamy.tab.shared.features.layout.Layout;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
import me.neznamy.tab.shared.permission.PermissionPlugin;
import me.neznamy.tab.shared.placeholders.Placeholder;
import me.neznamy.tab.shared.placeholders.PlayerPlaceholder;
@@ -55,12 +49,6 @@ public interface Platform {
*/
public void registerUnknownPlaceholder(String identifier);
/**
* Converts configuration file into the latest version by removing old options, adding new ones or renaming
* @param config - the configuration file to be converted
*/
public void convertConfig(ConfigurationFile config);
/**
* Returns server's version
* @return server's version
@@ -91,6 +79,12 @@ public interface Platform {
*/
public int getMaxPlayers();
/**
* Returns name of config file in the jar file on specific platform
* @return name of config file of the platform
*/
public String getConfigName();
/**
* Removes an old config option that is not present anymore
* @param config - configuration file
@@ -141,88 +135,11 @@ public interface Platform {
return replaced;
}
/**
* Converts premiumconfig variables to latest version
* @param config - the file
*/
public default void convertPremiumConfig(ConfigurationFile config) {
removeOld(config, "scoreboard.refresh-interval-ticks");
if (!config.hasConfigOption("placeholder-output-replacements")) {
Map<String, Map<String, String>> replacements = new HashMap<String, Map<String, String>>();
Map<String, String> essVanished = new HashMap<String, String>();
essVanished.put("Yes", "&7| Vanished");
essVanished.put("No", "");
replacements.put("%essentials_vanished%", essVanished);
Map<String, String> tps = new HashMap<String, String>();
tps.put("20", "&aPerfect");
replacements.put("%tps%", tps);
config.set("placeholder-output-replacements", replacements);
TAB.getInstance().print('2', "Added new missing \"placeholder-output-replacements\" premiumconfig.yml section.");
}
boolean scoreboardsConverted = false;
for (Object scoreboard : config.getConfigurationSection("scoreboards").keySet()) {
Boolean permReq = config.getBoolean("scoreboards." + scoreboard + ".permission-required");
if (permReq != null) {
if (permReq) {
config.set("scoreboards." + scoreboard + ".display-condition", "permission:tab.scoreboard." + scoreboard);
}
config.set("scoreboards." + scoreboard + ".permission-required", null);
scoreboardsConverted = true;
}
String childBoard = config.getString("scoreboards." + scoreboard + ".if-permission-missing");
if (childBoard != null) {
config.set("scoreboards." + scoreboard + ".if-permission-missing", null);
config.set("scoreboards." + scoreboard + ".if-condition-not-met", childBoard);
scoreboardsConverted = true;
}
}
if (scoreboardsConverted) {
TAB.getInstance().print('2', "Converted old premiumconfig.yml scoreboard display condition system to new one.");
}
removeOld(config, "scoreboard.refresh-interval-milliseconds");
rename(config, "allign-tabsuffix-on-the-right", "align-tabsuffix-on-the-right");
}
/**
* Converts configuration options same on all platforms
* @param file - file to convert
*/
public default void convertUniversalOptions(ConfigurationFile file) {
if (file.getName().equals("config.yml")) {
removeOld(file, "nametag-refresh-interval-milliseconds");
removeOld(file, "tablist-refresh-interval-milliseconds");
removeOld(file, "header-footer-refresh-interval-milliseconds");
removeOld(file, "classic-vanilla-belowname.refresh-interval-milliseconds");
rename(file, "belowname", "classic-vanilla-belowname");
rename(file, "papi-placeholder-cooldowns", "placeholderapi-refresh-intervals");
rename(file, "safe-team-register", "unregister-before-register");
rename(file, "disable-features-in-worlds.tablist-objective", "disable-features-in-worlds.yellow-number");
rename(file, "ntx-space", "unlimited-nametag-prefix-suffix-mode.space-between-lines");
if (!file.hasConfigOption("placeholderapi-refresh-intervals")) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("default-refresh-interval", 100);
Map<String, Integer> server = new HashMap<String, Integer>();
server.put("%server_uptime%", 1000);
server.put("%server_tps_1_colored%", 1000);
map.put("server", server);
Map<String, Integer> player = new HashMap<String, Integer>();
player.put("%player_health%", 200);
player.put("%player_ping%", 1000);
player.put("%vault_prefix%", 1000);
map.put("player", player);
Map<String, Integer> relational = new HashMap<String, Integer>();
relational.put("%rel_factionsuuid_relation_color%", 500);
map.put("relational", relational);
file.set("placeholderapi-refresh-intervals", map);
TAB.getInstance().print('2', "Added new missing \"placeholderapi-refresh-intervals\" config.yml section.");
}
}
if (file.getName().equals("premiumconfig.yml")) {
convertPremiumConfig(file);
}
if (file.getName().equals("bossbar.yml")) {
removeOld(file, "refresh-interval-milliseconds");
}
if (file.getName().equals("animations.yml")) {
Map<String, Object> values = file.getValues();
if (values.size() == 1 && values.containsKey("animations")) {
@@ -239,11 +156,7 @@ public interface Platform {
public default void loadUniversalFeatures() {
TAB tab = TAB.getInstance();
if (tab.getConfiguration().config.getBoolean("enable-header-footer", true)) tab.getFeatureManager().registerFeature("headerfooter", new HeaderFooter(tab));
if (tab.getConfiguration().config.getBoolean("do-not-move-spectators", false)) tab.getFeatureManager().registerFeature("spectatorfix", new SpectatorFix());
if (tab.getConfiguration().config.getBoolean("classic-vanilla-belowname.enabled", true)) tab.getFeatureManager().registerFeature("belowname", new BelowName(tab));
if (tab.getConfiguration().premiumconfig != null && tab.getConfiguration().premiumconfig.getBoolean("scoreboard.enabled", false)) tab.getFeatureManager().registerFeature("scoreboard", new ScoreboardManager(tab));
if ((boolean)tab.getConfiguration().getSecretOption("remove-ghost-players", false)) tab.getFeatureManager().registerFeature("ghostplayerfix", new GhostPlayerFix());
if (tab.getConfiguration().config.getString("yellow-number-in-tablist", "%ping%").length() > 0) tab.getFeatureManager().registerFeature("tabobjective", new TabObjective(tab));
if (tab.getConfiguration().removeGhostPlayers) tab.getFeatureManager().registerFeature("ghostplayerfix", new GhostPlayerFix());
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 8 && tab.getConfiguration().config.getBoolean("change-tablist-prefix-suffix", true)) {
Playerlist playerlist = new Playerlist(tab);
tab.getFeatureManager().registerFeature("playerlist", playerlist);
@@ -252,9 +165,7 @@ public interface Platform {
tab.getFeatureManager().registerFeature("group", new GroupRefresher(tab));
tab.getFeatureManager().registerFeature("info", new PluginInfo());
new UpdateChecker(tab);
if ((boolean)tab.getConfiguration().getSecretOption("layout", false)) {
tab.getFeatureManager().registerFeature("layout", new Layout(tab));
}
if (tab.getConfiguration().BossBarEnabled) tab.getFeatureManager().registerFeature("bossbar", new BossBar(tab));
if (tab.getConfiguration().layout) tab.getFeatureManager().registerFeature("layout", new Layout(tab));
if (tab.getConfiguration().bossbar.getBoolean("bossbar-enabled", false)) tab.getFeatureManager().registerFeature("bossbar", new BossBar(tab));
}
}
@@ -6,6 +6,7 @@ import java.util.List;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.placeholders.Placeholder;
import me.neznamy.tab.shared.placeholders.RelationalPlaceholder;
import me.neznamy.tab.shared.rgb.RGBUtils;
/**
* A string with placeholders
@@ -40,18 +41,18 @@ public class Property {
public Property(TabPlayer owner, String rawValue, String source) {
this.owner = owner;
this.source = source;
this.rawValue = (rawValue == null ? "" : rawValue);
this.rawValue = RGBUtils.getInstance().applyFormats((rawValue == null ? "" : rawValue), true);
analyze(this.rawValue);
update();
}
/**
* Finds all placeholders used in the value
* @param value - raw value to be checked
*/
private void analyze(String value) {
placeholders = new ArrayList<String>();
relPlaceholders = new ArrayList<String>();
List<String> placeholders = new ArrayList<String>();
List<String> relPlaceholders = new ArrayList<String>();
for (String identifier : TAB.getInstance().getPlaceholderManager().getUsedPlaceholderIdentifiersRecursive(value)) {
if (identifier.startsWith("%rel_")) {
relPlaceholders.add(identifier);
@@ -59,6 +60,9 @@ public class Property {
placeholders.add(identifier);
}
}
//avoiding rare concurrent modification in #update
this.placeholders = placeholders;
this.relPlaceholders = relPlaceholders;
}
/**
@@ -66,10 +70,11 @@ public class Property {
* @param temporaryValue - temporary value to be assigned
*/
public void setTemporaryValue(String temporaryValue) {
this.temporaryValue = temporaryValue;
if (temporaryValue != null) {
analyze(temporaryValue);
this.temporaryValue = RGBUtils.getInstance().applyFormats(temporaryValue, true);
analyze(this.temporaryValue);
} else {
this.temporaryValue = null;
analyze(rawValue);
}
update();
@@ -81,7 +86,7 @@ public class Property {
*/
public void changeRawValue(String newValue) {
if (rawValue.equals(newValue)) return;
rawValue = newValue;
rawValue = RGBUtils.getInstance().applyFormats(newValue, true);
if (temporaryValue == null) {
analyze(rawValue);
update();
@@ -142,6 +147,7 @@ public class Property {
* @return if updating changed value or not
*/
public boolean update() {
long time = System.nanoTime();
String string = getCurrentRawValue();
for (String identifier : placeholders) {
Placeholder pl = TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
@@ -151,8 +157,10 @@ public class Property {
string = applyRemoveStrings(string);
if (lastReplacedValue == null || !lastReplacedValue.equals(string)) {
lastReplacedValue = string;
TAB.getInstance().getCPUManager().addMethodTime("Property#update", System.nanoTime()-time);
return true;
}
TAB.getInstance().getCPUManager().addMethodTime("Property#update", System.nanoTime()-time);
return false;
}
@@ -181,11 +189,10 @@ public class Property {
* @return format for the viewer
*/
public String getFormat(TabPlayer viewer) {
if (viewer == null) return lastReplacedValue;
String format = lastReplacedValue;
for (String identifier : relPlaceholders) {
RelationalPlaceholder pl = (RelationalPlaceholder) TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
if (pl != null) format = format.replace(pl.getIdentifier(), pl.getLastValue(viewer, owner));
if (pl != null) format = format.replace(pl.getIdentifier(), viewer == null ? "" : pl.getLastValue(viewer, owner));
}
return format;
}
@@ -6,6 +6,7 @@ package me.neznamy.tab.shared;
public enum ProtocolVersion {
UNKNOWN (999),
v1_17 (755),
v1_16_5 (754),
v1_16_4 (754),
v1_16_3 (753),
@@ -60,6 +61,12 @@ public enum ProtocolVersion {
//version's network id found at https://wiki.vg/Protocol_version_numbers
private int networkId;
//minor version, such as 16
private int minorVersion;
//friendly name displayed in %player-version% placeholder
private String friendlyName;
/**
* Constructs new instance with given network id
@@ -67,6 +74,13 @@ public enum ProtocolVersion {
*/
private ProtocolVersion(int networkId){
this.networkId = networkId;
if (toString().equals("UNKNOWN")) {
this.minorVersion = 17;
this.friendlyName = "Unknown";
} else {
this.minorVersion = Integer.parseInt(toString().split("_")[1]);
this.friendlyName = toString().substring(1).replace("_", ".");
}
}
/**
@@ -82,8 +96,7 @@ public enum ProtocolVersion {
* @return
*/
public String getFriendlyName() {
if (this == UNKNOWN) return "Unknown";
return toString().substring(1).replace("_", ".");
return friendlyName;
}
/**
@@ -91,8 +104,7 @@ public enum ProtocolVersion {
* @return version's minor version
*/
public int getMinorVersion() {
if (this == UNKNOWN) return 16;
return Integer.parseInt(toString().split("_")[1]);
return minorVersion;
}
/**
@@ -25,14 +25,14 @@ public class TAB {
private static TAB instance;
//version of plugin
public static final String pluginVersion = "2.8.11-pre9";
public static final String pluginVersion = "2.9.0";
//player data
public final Map<UUID, TabPlayer> data = new ConcurrentHashMap<UUID, TabPlayer>();
//the command
public final TabCommand command;
public TabCommand command;
//command used if plugin is disabled due to a broken configuration file
public final DisabledCommand disabledCommand = new DisabledCommand();
@@ -68,7 +68,6 @@ public class TAB {
public TAB(Platform platform, PacketBuilder packetBuilder) {
this.platform = platform;
this.packetBuilder = packetBuilder;
this.command = new TabCommand(this);
}
/**
@@ -115,7 +114,7 @@ public class TAB {
*/
public TabPlayer getPlayerByTablistUUID(UUID tablistId) {
for (TabPlayer p : data.values()) {
if (p.getTablistUUID().toString().equals(tablistId.toString())) return p;
if (p.getTablistUUID().equals(tablistId)) return p;
}
return null;
}
@@ -152,8 +151,9 @@ public class TAB {
placeholderManager = new PlaceholderManager(this);
featureManager.registerFeature("placeholders", placeholderManager);
platform.loadFeatures();
command = new TabCommand(this);
featureManager.load();
getPlayers().forEach(p -> p.markAsLoaded());
getPlayers().forEach(p -> ((ITabPlayer)p).markAsLoaded());
errorManager.printConsoleWarnCount();
print('a', "Enabled in " + (System.currentTimeMillis()-time) + "ms");
platform.callLoadEvent();
@@ -13,7 +13,7 @@ import me.neznamy.tab.shared.TAB;
public abstract class SubCommand {
//all properties assignable with a command
protected final String[] allProperties = {"tabprefix", "tabsuffix", "tagprefix", "tagsuffix", "customtabname", "abovename", "belowname", "customtagname"};
public static String[] allProperties = {"tabprefix", "tabsuffix", "tagprefix", "tagsuffix", "customtabname", "abovename", "belowname", "customtagname"};
//properties that require unlimited nametag mode
protected final List<String> extraProperties = Arrays.asList("abovename", "belowname", "customtagname");
@@ -3,6 +3,9 @@ package me.neznamy.tab.shared.command;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Sets;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
@@ -20,7 +23,6 @@ import me.neznamy.tab.shared.command.level1.ReloadCommand;
import me.neznamy.tab.shared.command.level1.ScoreboardCommand;
import me.neznamy.tab.shared.command.level1.SendCommand;
import me.neznamy.tab.shared.command.level1.SetCollisionCommand;
import me.neznamy.tab.shared.command.level1.WidthCommand;
/**
* The core command handler
@@ -52,8 +54,12 @@ public class TabCommand extends SubCommand {
registerSubCommand(new SetCollisionCommand());
if (tab.isPremium()) {
registerSubCommand(new ScoreboardCommand());
registerSubCommand(new WidthCommand());
}
Set<String> properties = Sets.newHashSet("tabprefix", "tabsuffix", "tagprefix", "tagsuffix", "customtabname", "abovename", "belowname", "customtagname");
for (Object line : ((DebugCommand)subcommands.get("debug")).getExtraLines()) {
properties.add(line.toString());
}
SubCommand.allProperties = properties.toArray(new String[0]);
}
@Override
@@ -106,7 +112,7 @@ public class TabCommand extends SubCommand {
@Override
public List<String> complete(TabPlayer sender, String[] arguments) {
if (!(boolean) tab.getConfiguration().getSecretOption("auto-command-complete", true)) return new ArrayList<String>();
if (!hasPermission(sender, "tab.tabcomplete")) return new ArrayList<String>();
return super.complete(sender, arguments);
}
}
@@ -0,0 +1,37 @@
package me.neznamy.tab.shared.command.level1;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.command.SubCommand;
import me.neznamy.tab.shared.features.bossbar.BossBar;
/**
* Handler for "/tab bossbar" subcommand
*/
public class BossBarCommand extends SubCommand{
/**
* Constructs new instance
*/
public BossBarCommand() {
super("bossbar", null);
}
@Override
public void execute(TabPlayer sender, String[] args) {
BossBar bossbar = (BossBar) TAB.getInstance().getFeatureManager().getFeature("bossbar");
if (bossbar == null) {
sendMessage(sender, "&cBossbar feature is not enabled, therefore toggle command cannot be used.");
return;
}
if (sender == null) {
sendMessage(sender, "&cThis command must be ran from the game");
return;
}
if (!bossbar.permToToggle || sender.hasPermission("tab.togglebar")) {
sender.setBossbarVisible(!sender.hasBossbarVisible(), true);
} else {
sender.sendMessage(getTranslation("no_permission"), true);
}
}
}
@@ -5,6 +5,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
@@ -53,20 +54,26 @@ public class CpuCommand extends SubCommand {
sendMessage(sender, "&8&l" + LINE_CHAR + "&8&m &r&8&l[ &bTAB CPU Stats &8&l]&r&8&l&m ");
sendMessage(sender, "&8&l" + LINE_CHAR + " &6CPU stats from the last 10 seconds");
sendMessage(sender, SEPARATOR);
sendMessage(sender, "&8&l" + LINE_CHAR + " &6Placeholders using more than 0.1%:");
sendMessage(sender, "&8&l" + LINE_CHAR + " &6Top 5 placeholders:");
int printCounter = 0;
for (Entry<String, Float> entry : placeholders.entrySet()) {
if (entry.getValue() < 0.1) continue;
if (printCounter++ == 5) break;
String refresh = "";
Placeholder p = TAB.getInstance().getPlaceholderManager().getPlaceholder(entry.getKey()+"");
Placeholder p = TAB.getInstance().getPlaceholderManager().getPlaceholder(entry.getKey().toString());
if (p != null) refresh = " &8(" + p.getRefresh() + ")&7";
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", entry.getKey() + refresh, colorizePlaceholder(decimal3.format(entry.getValue()))));
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", entry.getKey() + refresh, colorize(decimal3.format(entry.getValue()), 1, 0.3f)));
}
sendMessage(sender, SEPARATOR);
sendMessage(sender, "&8&l" + LINE_CHAR + " &6Some internal separately measured methods:");
for (Entry<String, Float> entry : tab.getCPUManager().getMethodUsage().entrySet()) {
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7%s: %s%%", entry.getKey(), colorize(decimal3.format(entry.getValue()), 5, 2)));
}
sendMessage(sender, SEPARATOR);
if (tab.getPlatform().getSeparatorType().equals("server")) {
sendMessage(sender, "&8&l" + LINE_CHAR + " &6Placeholder usage on Bukkit servers:");
for (Entry<String, Float> entry : bridgeplaceholders.entrySet()) {
if (entry.getValue() < 0.1) continue;
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", entry.getKey(), colorizePlaceholder(decimal3.format(entry.getValue()))));
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", entry.getKey(), colorize(decimal3.format(entry.getValue()), 1, 0.3f)));
}
sendMessage(sender, SEPARATOR);
}
@@ -82,12 +89,12 @@ public class CpuCommand extends SubCommand {
} else {
sendPacketCountToConsole();
}
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lPlaceholders Total: &a&l%s%%", colorizeTotalUsage(decimal3.format(placeholdersTotal))));
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lPlaceholders Total: &a&l%s%%", colorize(decimal3.format(placeholdersTotal), 10, 5)));
if (tab.getPlatform().getSeparatorType().equals("server")) {
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lBukkit bridge placeholders Total: &a&l%s%%", colorizeTotalUsage(decimal3.format(bridgeplaceholdersTotal))));
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lBukkit bridge placeholders Total: &a&l%s%%", colorize(decimal3.format(bridgeplaceholdersTotal), 10, 5)));
}
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lPlugin internals: &a&l%s%%", colorizeTotalUsage(decimal3.format(featuresTotal-placeholdersTotal))));
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lTotal: &e&l%s%%", colorizeTotalUsage(decimal3.format(featuresTotal + bridgeplaceholdersTotal))));
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lPlugin internals: &a&l%s%%", colorize(decimal3.format(featuresTotal-placeholdersTotal), 10, 5)));
sendMessage(sender, String.format("&8&l" + LINE_CHAR + " &6&lTotal: &e&l%s%%", colorize(decimal3.format(featuresTotal + bridgeplaceholdersTotal), 10, 5)));
sendMessage(sender, "&8&l" + LINE_CHAR + "&8&m &r&8&l[ &bTAB CPU Stats &8&l]&r&8&l&m ");
sendMessage(sender, " ");
}
@@ -99,10 +106,10 @@ public class CpuCommand extends SubCommand {
for (Float f : entry.getValue().values()) {
featureTotal += f;
}
String core = String.format("&8&l" + LINE_CHAR + " &7%s (%s%%&7):", entry.getKey(), colorizeFeature(decimal3.format(featureTotal)));
String core = String.format("&8&l" + LINE_CHAR + " &7%s (%s%%&7):", entry.getKey(), colorize(decimal3.format(featureTotal), 5, 1));
List<String> messages = new ArrayList<String>();
for (Entry<UsageType, Float> type : entry.getValue().entrySet()){
messages.add(String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", type.getKey(), colorizeFeature(decimal3.format(type.getValue()))));
messages.add(String.format("&8&l" + LINE_CHAR + " &7%s - %s%%", type.getKey(), colorize(decimal3.format(type.getValue()), 5, 1)));
}
TAB.getInstance().getPlatform().sendConsoleMessage(core, true);
for (String message : messages) {
@@ -118,23 +125,23 @@ public class CpuCommand extends SubCommand {
for (Float f : entry.getValue().values()) {
featureTotal += f;
}
String core = String.format("&8&l" + LINE_CHAR + " &7%s (%s%%&7):", entry.getKey(), colorizeFeature(decimal3.format(featureTotal)));
String core = String.format("&8&l" + LINE_CHAR + " &7%s (%s%%&7):", entry.getKey(), colorize(decimal3.format(featureTotal), 5, 1));
List<String> messages = new ArrayList<String>();
for (Entry<UsageType, Float> type : entry.getValue().entrySet()){
messages.add("&3" + type.getKey().toString() + " - " + colorizeFeature(decimal3.format(type.getValue())) + "%");
messages.add("&3" + type.getKey().toString() + " - " + colorize(decimal3.format(type.getValue()), 5, 1) + "%");
}
IChatBaseComponent message = new IChatBaseComponent(TAB.getInstance().getPlaceholderManager().color(core));
message.onHoverShowText(TAB.getInstance().getPlaceholderManager().color(String.join("\n", messages)));
IChatBaseComponent message = new IChatBaseComponent(core.replace('&', '\u00a7'));
message.onHoverShowText(String.join("\n", messages).replace('&', '\u00a7'));
sender.sendMessage(message);
}
}
public void sendPacketCountToConsole() {
Map<Object, Integer> packets = TAB.getInstance().getCPUManager().getSentPackets();
Map<Object, AtomicInteger> packets = TAB.getInstance().getCPUManager().getSentPackets();
int count = 0;
List<String> messages = new ArrayList<String>();
for (Entry<Object, Integer> entry : packets.entrySet()) {
count += entry.getValue();
for (Entry<Object, AtomicInteger> entry : packets.entrySet()) {
count += entry.getValue().get();
messages.add("&8&l" + LINE_CHAR + " &7" + entry.getKey().toString() + " - " + entry.getValue());
}
TAB.getInstance().getPlatform().sendConsoleMessage("&8&l" + LINE_CHAR + " &r&7Packets sent by the plugin: " + count, true);
@@ -144,15 +151,15 @@ public class CpuCommand extends SubCommand {
}
public void sendPacketCountToPlayer(TabPlayer sender) {
Map<Object, Integer> packets = TAB.getInstance().getCPUManager().getSentPackets();
Map<Object, AtomicInteger> packets = TAB.getInstance().getCPUManager().getSentPackets();
int count = 0;
List<String> messages = new ArrayList<String>();
for (Entry<Object, Integer> entry : packets.entrySet()) {
count += entry.getValue();
for (Entry<Object, AtomicInteger> entry : packets.entrySet()) {
count += entry.getValue().get();
messages.add("&3" + entry.getKey().toString() + " - " + entry.getValue());
}
IChatBaseComponent message = new IChatBaseComponent(TAB.getInstance().getPlaceholderManager().color("&8&l" + LINE_CHAR + " &r&7Packets sent by the plugin (hover for more info): " + count));
message.onHoverShowText(TAB.getInstance().getPlaceholderManager().color(String.join("\n", messages)));
IChatBaseComponent message = new IChatBaseComponent(("&8&l" + LINE_CHAR + " &r&7Packets sent by the plugin (hover for more info): " + count).replace('&', '\u00a7'));
message.onHoverShowText(String.join("\n", messages).replace('&', '\u00a7'));
sender.sendMessage(message);
}
@@ -161,34 +168,10 @@ public class CpuCommand extends SubCommand {
* @param usage - usage
* @return colored usage
*/
private String colorizePlaceholder(String usage) {
private String colorize(String usage, float threshold1, float threshold2) {
float percent = Float.parseFloat(usage.replace(",", "."));
if (percent > 1) return "&c" + usage;
if (percent > 0.3) return "&e" + usage;
return "&a" + usage;
}
/**
* Returns colored usage from provided usage
* @param usage - usage
* @return colored usage
*/
private String colorizeFeature(String usage) {
float percent = Float.parseFloat(usage.replace(",", "."));
if (percent > 5) return "&c" + usage;
if (percent > 1) return "&e" + usage;
return "&a" + usage;
}
/**
* Returns colored usage from provided usage
* @param usage - usage
* @return colored usage
*/
private String colorizeTotalUsage(String usage) {
float percent = Float.parseFloat(usage.replace(",", "."));
if (percent > 10) return "&c" + usage;
if (percent > 5) return "&e" + usage;
if (percent > threshold1) return "&c" + usage;
if (percent > threshold2) return "&e" + usage;
return "&a" + usage;
}
}
@@ -51,14 +51,15 @@ public class DebugCommand extends SubCommand {
TAB tab = TAB.getInstance();
sendMessage(sender, "&3[TAB] &a&lShowing debug information");
sendMessage(sender, "&7&m>-------------------------------<");
sendMessage(sender, "&6Server version: &a" + tab.getPlatform().getServerVersion());
sendMessage(sender, "&6Plugin version: &a" + tab.getPluginVersion() + (tab.isPremium() ? " Premium" : ""));
String c2 = tab.isPremium() ? "&b" : "&a";
sendMessage(sender, "&6Server version: " + c2 + tab.getPlatform().getServerVersion());
sendMessage(sender, "&6Plugin version: " + c2 + tab.getPluginVersion() + (tab.isPremium() ? " Premium" : ""));
if (tab.getErrorManager().errorLog.exists()) {
sendMessage(sender, "&6" + tab.getErrorManager().errorLog.getPath() + " size: &c" + tab.getErrorManager().errorLog.length()/1024 + "KB");
}
sendMessage(sender, "&6Permission plugin: &a" + tab.getPermissionPlugin().getName());
sendMessage(sender, getGroupChoiceLogic());
sendMessage(sender, "&6Sorting system: &a" + getSortingType());
sendMessage(sender, "&6Permission plugin: " + c2 + tab.getPermissionPlugin().getName());
sendMessage(sender, "&6Permission group choice logic: " + c2 + getGroupChoiceLogic());
sendMessage(sender, "&6Sorting system: " + c2 + getSortingType());
sendMessage(sender, "&7&m>-------------------------------<");
if (analyzed == null) return;
sendMessage(sender, "&ePlayer: &a" + analyzed.getName());
@@ -80,7 +81,7 @@ public class DebugCommand extends SubCommand {
showProperty(sender, analyzed, "tagprefix", disabledNametags);
showProperty(sender, analyzed, "tagsuffix", disabledNametags);
for (Object line : getExtraLines()) {
showProperty(sender, analyzed, line+"", disabledNametags);
showProperty(sender, analyzed, line.toString(), disabledNametags);
}
} else {
sendMessage(sender, "&atagprefix: &cDisabled");
@@ -96,11 +97,11 @@ public class DebugCommand extends SubCommand {
private String getGroupChoiceLogic() {
GroupRefresher group = (GroupRefresher) TAB.getInstance().getFeatureManager().getFeature("group");
if (group.groupsByPermissions) {
return "&6Permission group choice logic: &8&mPrimary group&8 / &r&8&mChoose from list&8 / &aPermissions";
return "Permissions";
} else if (group.usePrimaryGroup) {
return "&6Permission group choice logic: &aPrimary group&8 / &r&8&mChoose from list&8 / &r&8&mPermissions";
return "Primary group";
} else {
return "&6Permission group choice logic: &8&mPrimary group&r&8 / &aChoose from list&8 / &r&8&mPermissions";
return "Choose from list";
}
}
@@ -170,7 +171,7 @@ public class DebugCommand extends SubCommand {
* @return list of extra properties
*/
@SuppressWarnings("unchecked")
private List<Object> getExtraLines(){
public List<Object> getExtraLines(){
if (!TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx")) return new ArrayList<Object>();
if (TAB.getInstance().isPremium()) {
List<Object> lines = Lists.newArrayList((List<Object>) TAB.getInstance().getConfiguration().premiumconfig.getObject("unlimited-nametag-mode-dynamic-lines"));
@@ -196,7 +197,8 @@ public class DebugCommand extends SubCommand {
} else {
Property pr = analyzed.getProperty(property);
String rawValue = pr.getCurrentRawValue().replace('\u00a7', '&');
String value = TAB.getInstance().getPlaceholderManager().color("&a" + property + ": &e\"&r%rawValue%&r&e\" &7(" + rawValue.length() + ") &9(Source: " + pr.getSource() + ")").replace("%rawValue%", rawValue);
String c = TAB.getInstance().isPremium() ? "&7" : "&9";
String value = String.format(("&a%s: &e\"&r%s&r&e\" &7(%s) " + c + "(Source: %s)").replace('&', '\u00a7'), property, rawValue, rawValue.length(), pr.getSource());
sendRawMessage(sender, value);
}
}
@@ -68,6 +68,12 @@ public class GroupCommand extends SubCommand {
* @param value - new value
*/
private void saveGroup(TabPlayer sender, String group, String type, String value){
if (value.length() > 0){
sendMessage(sender, getTranslation("value_assigned").replace("%type%", type).replace("%value%", value).replace("%unit%", group).replace("%category%", "group"));
} else {
sendMessage(sender, getTranslation("value_removed").replace("%type%", type).replace("%unit%", group).replace("%category%", "group"));
}
if (String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(TAB.getInstance().getConfiguration().config.getObject("Groups." + group.replace(".", "@#@") + "." + type)))) return;
TAB.getInstance().getConfiguration().config.set("Groups." + group.replace(".", "@#@") + "." + type, value.length() == 0 ? null : value);
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
for (TabPlayer pl : TAB.getInstance().getPlayers()) {
@@ -75,11 +81,6 @@ public class GroupCommand extends SubCommand {
pl.forceRefresh();
}
}
if (value.length() > 0){
sendMessage(sender, getTranslation("value_assigned").replace("%type%", type).replace("%value%", value).replace("%unit%", group).replace("%category%", "group"));
} else {
sendMessage(sender, getTranslation("value_removed").replace("%type%", type).replace("%unit%", group).replace("%category%", "group"));
}
}
@Override
@@ -21,10 +21,10 @@ public class ParseCommand extends SubCommand{
public void execute(TabPlayer sender, String[] args) {
if (args.length > 0) {
String replaced = String.join(" ", args);
String message = TAB.getInstance().getPlaceholderManager().color("&6Replacing placeholder &e%placeholder%" + (sender == null ? "" : "&6 for player &e" + sender.getName())).replace("%placeholder%", replaced);
String message = ("&6Replacing placeholder &e%placeholder%" + (sender == null ? "" : "&6 for player &e" + sender.getName())).replace('&', '\u00a7').replace("%placeholder%", replaced);
sendRawMessage(sender, message);
replaced = TAB.getInstance().getPlatform().replaceAllPlaceholders(replaced, sender);
IChatBaseComponent colored = IChatBaseComponent.fromColoredText("With colors: " + replaced);
IChatBaseComponent colored = IChatBaseComponent.optimizedComponent("With colors: " + replaced);
if (sender != null) {
sender.sendMessage(colored);
} else {
@@ -67,17 +67,18 @@ public class PlayerCommand extends SubCommand {
* @param value - new value
*/
public void savePlayer(TabPlayer sender, String player, String type, String value){
TabPlayer pl = TAB.getInstance().getPlayer(player);
TAB.getInstance().getConfiguration().config.set("Users." + player + "." + type, value.length() == 0 ? null : value);
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
if (pl != null) {
pl.forceRefresh();
}
if (value.length() > 0){
sendMessage(sender, getTranslation("value_assigned").replace("%type%", type).replace("%value%", value).replace("%unit%", player).replace("%category%", "player"));
} else {
sendMessage(sender, getTranslation("value_removed").replace("%type%", type).replace("%unit%", player).replace("%category%", "player"));
}
if (String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(TAB.getInstance().getConfiguration().config.getObject("Users." + player + "." + type)))) return;
TAB.getInstance().getConfiguration().config.set("Users." + player + "." + type, value.length() == 0 ? null : value);
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
TabPlayer pl = TAB.getInstance().getPlayer(player);
if (pl != null) {
pl.forceRefresh();
}
}
@Override
@@ -69,14 +69,15 @@ public class PlayerUUIDCommand extends SubCommand {
* @param value - new value
*/
public void savePlayer(TabPlayer sender, TabPlayer player, String type, String value){
TAB.getInstance().getConfiguration().config.set("Users." + player.getUniqueId() + "." + type, value.length() == 0 ? null : value);
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
player.forceRefresh();
if (value.length() > 0){
sendMessage(sender, getTranslation("value_assigned").replace("%type%", type).replace("%value%", value).replace("%unit%", player.getName() + "(" + player.getUniqueId().toString() + ")").replace("%category%", "UUID"));
} else {
sendMessage(sender, getTranslation("value_removed").replace("%type%", type).replace("%unit%", player.getName() + "(" + player.getUniqueId().toString() + ")").replace("%category%", "UUID"));
}
if (String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(TAB.getInstance().getConfiguration().config.getObject("Users." + player.getUniqueId() + "." + type)))) return;
TAB.getInstance().getConfiguration().config.set("Users." + player.getUniqueId() + "." + type, value.length() == 0 ? null : value);
TAB.getInstance().getPlaceholderManager().checkForRegistration(value);
player.forceRefresh();
}
@Override
@@ -1,6 +1,7 @@
package me.neznamy.tab.shared.command.level1;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.command.SubCommand;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManager;
@@ -21,7 +22,7 @@ public class ScoreboardCommand extends SubCommand {
public void execute(TabPlayer sender, String[] args) {
ScoreboardManager scoreboard = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
if (scoreboard == null) {
sendMessage(sender, TAB.getInstance().getPlaceholderManager().color("&cScoreboard feature is not enabled, therefore toggle command cannot be used."));
sendMessage(sender, "&cScoreboard feature is not enabled, therefore toggle command cannot be used.");
return;
}
if (scoreboard.permToToggle && !hasPermission(sender, "tab.togglescoreboard")) {
@@ -45,17 +46,22 @@ public class ScoreboardCommand extends SubCommand {
return;
}
}
if (p.getOtherPluginScoreboard() != null) return; //not overriding other plugins
if (((ITabPlayer)p).getOtherPluginScoreboard() != null) return; //not overriding other plugins
boolean silent = args.length >= 3 && args[2].equals("-s");
if (args.length >= 1) {
if (args[0].equalsIgnoreCase("on"))
switch(args[0]) {
case "on":
p.setScoreboardVisible(true, !silent);
if (args[0].equalsIgnoreCase("off"))
break;
case "off":
p.setScoreboardVisible(false, !silent);
if (args[0].equalsIgnoreCase("toggle"))
break;
case "toggle":
p.toggleScoreboard(!silent);
break;
default:
break;
}
}
}
}
@@ -38,12 +38,12 @@ public class AnnounceBarCommand extends SubCommand{
try {
duration = Integer.parseInt(args[1]);
} catch (Exception e) {
sender.sendMessage(args[1] + " is not a number!", false);
sendMessage(sender, args[1] + " is not a number!");
return;
}
BossBarLine bar = feature.lines.get(barname);
if (bar == null) {
sender.sendMessage("Bar not found", false);
sendMessage(sender, "Bar not found");
return;
}
new Thread(() -> {
@@ -40,12 +40,12 @@ public class AnnounceScoreboardCommand extends SubCommand{
try {
duration = Integer.parseInt(args[1]);
} catch (Exception e) {
sender.sendMessage(args[1] + " is not a number!", false);
sendMessage(sender, args[1] + " is not a number!");
return;
}
Scoreboard sb = feature.getScoreboards().get(scoreboard);
if (sb == null) {
sender.sendMessage("Scoreboard not found", false);
sendMessage(sender, "Scoreboard not found");
return;
}
announce(feature, sb, duration);
@@ -48,7 +48,7 @@ public class SendBarCommand extends SubCommand{
}
BossBarLine bar = feature.lines.get(barname);
if (bar == null) {
sender.sendMessage("Bar not found", false);
sendMessage(sender, "No bar found with name \"" + bar + "\"");
return;
}
new Thread(() -> {
@@ -30,15 +30,23 @@ public class Configs {
//remove-strings option
public List<String> removeStrings;
//hidden config options
public boolean rgbSupport;
public boolean unregisterBeforeRegister;
public String multiWorldSeparator;
public String essentialsNickPrefix;
public boolean armorStandsAlwaysVisible; //paid private addition
public boolean removeGhostPlayers;
public boolean layout;
public boolean pipelineInjection;
public boolean bukkitPermissions;
//animations.yml file
public ConfigurationFile animation;
//bossbar.yml file
public ConfigurationFile bossbar;
//used for check & warn in case of old bossbar file format
public boolean BossBarEnabled;
//translation.yml file
public ConfigurationFile translation;
@@ -69,14 +77,16 @@ public class Configs {
removeAdvancedConfig();
loadConfig();
animation = new YamlConfigurationFile(loader.getResourceAsStream("animations.yml"), new File(tab.getPlatform().getDataFolder(), "animations.yml"));
tab.getPlatform().convertConfig(animation);
loadBossbar();
Map<String, Object> values = animation.getValues();
if (values.size() == 1 && values.containsKey("animations")) {
animation.setValues(animation.getConfigurationSection("animations"));
animation.save();
TAB.getInstance().print('2', "Converted animations.yml to new format.");
}
bossbar = new YamlConfigurationFile(loader.getResourceAsStream("bossbar.yml"), new File(tab.getPlatform().getDataFolder(), "bossbar.yml"));
translation = new YamlConfigurationFile(loader.getResourceAsStream("translation.yml"), new File(tab.getPlatform().getDataFolder(), "translation.yml"));
reloadFailed = translation.getString("reload-failed", "&4Failed to reload, file %file% has broken syntax. Check console for more info.");
if (tab.isPremium()) {
premiumconfig = new YamlConfigurationFile(loader.getResourceAsStream("premiumconfig.yml"), new File(tab.getPlatform().getDataFolder(), "premiumconfig.yml"));
tab.getPlatform().convertConfig(premiumconfig);
}
if (tab.isPremium()) premiumconfig = new YamlConfigurationFile(loader.getResourceAsStream("premiumconfig.yml"), new File(tab.getPlatform().getDataFolder(), "premiumconfig.yml"));
}
/**
@@ -101,22 +111,31 @@ public class Configs {
*/
@SuppressWarnings("unchecked")
public void loadConfig() throws Exception {
String source = tab.getPlatform().getSeparatorType().equals("world") ? "bukkitconfig.yml" : "bungeeconfig.yml";
config = new YamlConfigurationFile(Configs.class.getClassLoader().getResourceAsStream(source), new File(tab.getPlatform().getDataFolder(), "config.yml"), Arrays.asList("# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml", ""));
tab.getPlatform().convertConfig(config);
config = new YamlConfigurationFile(Configs.class.getClassLoader().getResourceAsStream(tab.getPlatform().getConfigName()), new File(tab.getPlatform().getDataFolder(), "config.yml"), Arrays.asList("# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml", ""));
removeStrings = new ArrayList<>();
for (String s : config.getStringList("placeholders.remove-strings", Arrays.asList("[] ", "< > "))) {
removeStrings.add(s.replace('&', '\u00a7'));
}
tab.debugMode = (boolean) getSecretOption("debug", false);
rgbSupport = (boolean) getSecretOption("rgb-support", true);
unregisterBeforeRegister = (boolean) getSecretOption("unregister-before-register", true);
multiWorldSeparator = (String) getSecretOption("multi-world-separator", "-");
essentialsNickPrefix = (String) getSecretOption("essentials-nickname-prefix", "");
armorStandsAlwaysVisible = (boolean) getSecretOption("unlimited-nametag-prefix-suffix-mode.always-visible", false);
removeGhostPlayers = (boolean) getSecretOption("remove-ghost-players", false);
layout = (boolean) getSecretOption("layout", false);
pipelineInjection = (boolean) getSecretOption("pipeline-injection", true);
if (tab.getPlatform().getSeparatorType().equals("server")) {
bukkitPermissions = config.getBoolean("use-bukkit-permissions-manager", false);
}
//checking for unnecessary copypaste in config
Set<Object> groups = config.getConfigurationSection("Groups").keySet();
if (groups.size() < 2) return;
Map<Object, Object> sharedProperties = new HashMap<>(config.getConfigurationSection("Groups." + groups.toArray()[0])); //cloning to not delete from original one
for (Object groupSettings : config.getConfigurationSection("Groups").values()) {
if (!(groupSettings instanceof Map)) continue;
Map<String, Object> group = (Map<String, Object>) groupSettings;
if (group == null) continue; // #261
for (Entry<Object, Object> sharedProperty : new HashSet<>(sharedProperties.entrySet())) {
String property = sharedProperty.getKey().toString();
if (!group.containsKey(property) || !String.valueOf(group.get(property)).equals(sharedProperty.getValue())) {
@@ -129,28 +148,13 @@ public class Configs {
}
}
/**
* Loads bossbar.yml and sends a warn if file is old
* @throws Exception - if file has broken syntax or I/O file operation fails
*/
public void loadBossbar() throws Exception {
bossbar = new YamlConfigurationFile(Configs.class.getClassLoader().getResourceAsStream("bossbar.yml"), new File(tab.getPlatform().getDataFolder(), "bossbar.yml"));
tab.getPlatform().convertConfig(bossbar);
if (bossbar.hasConfigOption("enabled")) {
tab.getErrorManager().startupWarn("You are using old bossbar config, please make a backup of the file and delete it to get new file.");
BossBarEnabled = false;
return;
}
BossBarEnabled = bossbar.getBoolean("bossbar-enabled", false);
}
/**
* Returns value of hidden config option with specified path if it exists, defaultValue otherwise
* @param path - path to value
* @param defaultValue - value to return if option is not present in file
* @return value with specified path or default value if not present
*/
public Object getSecretOption(String path, Object defaultValue) {
private Object getSecretOption(String path, Object defaultValue) {
if (config == null) return defaultValue;
Object value = config.getObject(path);
return value == null ? defaultValue : value;
@@ -185,7 +189,7 @@ public class Configs {
Map<String, Object> worlds = config.getConfigurationSection("per-" + tab.getPlatform().getSeparatorType() + "-settings");
if (worlds.isEmpty()) return world;
for (String worldGroup : worlds.keySet()) {
for (String localWorld : worldGroup.split((String)getSecretOption("multi-world-separator", "-"))) {
for (String localWorld : worldGroup.split(multiWorldSeparator)) {
if (localWorld.equalsIgnoreCase(world)) return worldGroup;
}
}
@@ -150,7 +150,7 @@ public abstract class ConfigurationFile {
public String getString(String path, String defaultValue) {
Object value = getObject(path, defaultValue);
if (value == null) return defaultValue;
return value+"";
return String.valueOf(value);
}
/**
@@ -178,7 +178,7 @@ public abstract class ConfigurationFile {
}
List<String> fixedList = new ArrayList<String>();
for (Object key : (List<Object>)value) {
fixedList.add(key+"");
fixedList.add(key.toString());
}
return fixedList;
}
@@ -212,7 +212,7 @@ public abstract class ConfigurationFile {
Object value = getObject(path, defaultValue);
if (value == null) return defaultValue;
try{
return Integer.parseInt(value+"");
return Integer.parseInt(value.toString());
} catch (Exception e) {
dataMismatch(path, Integer.class, value.getClass());
return defaultValue;
@@ -239,7 +239,7 @@ public abstract class ConfigurationFile {
Object value = getObject(path, defaultValue);
if (value == null) return defaultValue;
try{
return Boolean.parseBoolean(value+"");
return Boolean.parseBoolean(value.toString());
} catch (Exception e) {
dataMismatch(path, Boolean.class, value.getClass());
return defaultValue;
@@ -257,7 +257,7 @@ public abstract class ConfigurationFile {
Object value = getObject(path, defaultValue);
if (value == null) return defaultValue;
try{
return Double.parseDouble(value+"");
return Double.parseDouble(value.toString());
} catch (Exception e) {
dataMismatch(path, Double.class, value.getClass());
return defaultValue;
@@ -65,7 +65,7 @@ public class YamlConfigurationFile extends ConfigurationFile {
} catch (YAMLException e) {
input.close();
TAB tab = TAB.getInstance();
tab.getErrorManager().startupWarn("File " + destination + " has broken formatting.");
tab.getErrorManager().startupWarn("File " + destination + " has broken syntax.");
tab.brokenFile = file.getPath();
tab.getPlatform().sendConsoleMessage("&6[TAB] Error message from yaml parser: " + e.getMessage(), true);
List<String> suggestions = YamlAssist.getSuggestions(file);
@@ -12,6 +12,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import me.neznamy.tab.shared.ErrorManager;
@@ -24,20 +26,22 @@ public class CPUManager {
private final int bufferSizeMillis = 10000;
//nanoseconds worked in the current 10 seconds
private Map<Object, Map<UsageType, Long>> featureUsageCurrent = new ConcurrentHashMap<Object, Map<UsageType, Long>>();
private Map<String, Long> placeholderUsageCurrent = new ConcurrentHashMap<String, Long>();
private Map<String, Long> bridgePlaceholderUsageCurrent = new ConcurrentHashMap<String, Long>();
private Map<Object, Map<UsageType, AtomicLong>> featureUsageCurrent = new ConcurrentHashMap<Object, Map<UsageType, AtomicLong>>();
private Map<String, AtomicLong> placeholderUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
private Map<String, AtomicLong> bridgePlaceholderUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
private Map<String, AtomicLong> methodUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
//packets sent in the current 10 seconds
private Map<Object, Integer> packetsCurrent = new ConcurrentHashMap<Object, Integer>();
private Map<Object, AtomicInteger> packetsCurrent = new ConcurrentHashMap<Object, AtomicInteger>();
//nanoseconds worked in the previous 10 seconds
private Map<Object, Map<UsageType, Long>> featureUsagePrevious = new HashMap<Object, Map<UsageType, Long>>();
private Map<String, Long> placeholderUsagePrevious = new HashMap<String, Long>();
private Map<String, Long> bridgePlaceholderUsagePrevious = new HashMap<String, Long>();
private Map<Object, Map<UsageType, AtomicLong>> featureUsagePrevious = new HashMap<Object, Map<UsageType, AtomicLong>>();
private Map<String, AtomicLong> placeholderUsagePrevious = new HashMap<String, AtomicLong>();
private Map<String, AtomicLong> bridgePlaceholderUsagePrevious = new HashMap<String, AtomicLong>();
private Map<String, AtomicLong> methodUsagePrevious = new HashMap<String, AtomicLong>();
//packets sent in the previous 10 seconds
private Map<Object, Integer> packetsPrevious = new ConcurrentHashMap<Object, Integer>();
private Map<Object, AtomicInteger> packetsPrevious = new ConcurrentHashMap<Object, AtomicInteger>();
//thread pool
private ThreadPoolExecutor exe = (ThreadPoolExecutor) Executors.newCachedThreadPool();
@@ -62,12 +66,14 @@ public class CPUManager {
featureUsagePrevious = featureUsageCurrent;
placeholderUsagePrevious = placeholderUsageCurrent;
bridgePlaceholderUsagePrevious = bridgePlaceholderUsageCurrent;
methodUsagePrevious = methodUsageCurrent;
packetsPrevious = packetsCurrent;
featureUsageCurrent = new ConcurrentHashMap<Object, Map<UsageType, Long>>();
placeholderUsageCurrent = new ConcurrentHashMap<String, Long>();
bridgePlaceholderUsageCurrent = new ConcurrentHashMap<String, Long>();
packetsCurrent = new ConcurrentHashMap<Object, Integer>();
featureUsageCurrent = new ConcurrentHashMap<Object, Map<UsageType, AtomicLong>>();
placeholderUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
bridgePlaceholderUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
methodUsageCurrent = new ConcurrentHashMap<String, AtomicLong>();
packetsCurrent = new ConcurrentHashMap<Object, AtomicInteger>();
}
} catch (InterruptedException pluginDisabled) {
@@ -211,12 +217,20 @@ public class CPUManager {
return getUsage(bridgePlaceholderUsagePrevious);
}
/**
* Returns cpu usage map of methods previous 10 seconds
* @return cpu usage map of methods
*/
public Map<String, Float> getMethodUsage(){
return getUsage(methodUsagePrevious);
}
/**
* Returns map of sent packets per feature in previous 10 seconds
* @return map of sent packets per feature
*/
public Map<Object, Integer> getSentPackets(){
return sortByValue(packetsPrevious);
public Map<Object, AtomicInteger> getSentPackets(){
return sortByValue1(packetsPrevious);
}
/**
@@ -224,13 +238,13 @@ public class CPUManager {
* @param map - map to converted
* @return converted and sorted map
*/
private Map<String, Float> getUsage(Map<String, Long> map){
private Map<String, Float> getUsage(Map<String, AtomicLong> map){
Map<String, Long> nanoMap = new HashMap<String, Long>();
String key;
for (Entry<String, Long> nanos : map.entrySet()) {
for (Entry<String, AtomicLong> nanos : map.entrySet()) {
key = nanos.getKey();
if (!nanoMap.containsKey(key)) nanoMap.put(key, 0L);
nanoMap.put(key, nanoMap.get(key)+nanos.getValue());
nanoMap.put(key, nanoMap.get(key)+nanos.getValue().get());
}
Map<String, Float> percentMap = new HashMap<String, Float>();
for (Entry<String, Long> entry : nanoMap.entrySet()) {
@@ -245,17 +259,17 @@ public class CPUManager {
*/
public Map<Object, Map<UsageType, Float>> getFeatureUsage(){
Map<Object, Map<UsageType, Long>> total = new HashMap<Object, Map<UsageType, Long>>();
for (Entry<Object, Map<UsageType, Long>> nanos : featureUsagePrevious.entrySet()) {
for (Entry<Object, Map<UsageType, AtomicLong>> nanos : featureUsagePrevious.entrySet()) {
Object key = nanos.getKey();
if (!total.containsKey(key)) {
total.put(key, new HashMap<UsageType, Long>());
}
Map<UsageType, Long> usage = total.get(key);
for (Entry<UsageType, Long> entry : nanos.getValue().entrySet()) {
for (Entry<UsageType, AtomicLong> entry : nanos.getValue().entrySet()) {
if (!usage.containsKey(entry.getKey())) {
usage.put(entry.getKey(), 0L);
}
usage.put(entry.getKey(), usage.get(entry.getKey()) + entry.getValue());
usage.put(entry.getKey(), usage.get(entry.getKey()) + entry.getValue().get());
}
}
Map<Object, Map<UsageType, Float>> sorted = new LinkedHashMap<Object, Map<UsageType, Float>>();
@@ -300,6 +314,26 @@ public class CPUManager {
sortedByValues.putAll(map);
return sortedByValues;
}
/**
* Sorts map by value from higest to lowest
* @param <K> - map key
* @param <V> - map value
* @param map - map to sort
* @return sorted map
*/
private <K> Map<K, AtomicInteger> sortByValue1(Map<K, AtomicInteger> map) {
Comparator<K> valueComparator = new Comparator<K>() {
public int compare(K k1, K k2) {
int compare = ((Comparable<Integer>)map.get(k2).get()).compareTo(map.get(k1).get());
if (compare == 0) return 1;
else return compare;
}
};
Map<K, AtomicInteger> sortedByValues = new TreeMap<K, AtomicInteger>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
/**
* Sorts keys by map nested values from highest to lowest and returns sorted list of keys
@@ -341,14 +375,14 @@ public class CPUManager {
* @param nanoseconds - time to add
*/
public void addTime(Object feature, UsageType type, long nanoseconds) {
Map<UsageType, Long> usage = featureUsageCurrent.get(feature);
Map<UsageType, AtomicLong> usage = featureUsageCurrent.get(feature);
if (usage == null) {
usage = new ConcurrentHashMap<UsageType, Long>();
usage = new ConcurrentHashMap<UsageType, AtomicLong>();
featureUsageCurrent.put(feature, usage);
}
try {
if (!usage.containsKey(type)) {
usage.put(type, 0L);
usage.put(type, new AtomicLong());
}
} catch (NullPointerException e) {
//java.lang.NullPointerException: null
@@ -356,11 +390,11 @@ public class CPUManager {
//at java.base/java.util.concurrent.ConcurrentHashMap.containsKey(ConcurrentHashMap.java:964)
return;
}
Long current = usage.get(type);
AtomicLong current = usage.get(type);
if (current == null) {
usage.put(type, nanoseconds);
usage.put(type, new AtomicLong(nanoseconds));
} else {
usage.put(type, current + nanoseconds);
current.addAndGet(nanoseconds);
}
}
@@ -370,12 +404,7 @@ public class CPUManager {
* @param nanoseconds - time to add
*/
public void addPlaceholderTime(String placeholder, long nanoseconds) {
Long current = placeholderUsageCurrent.get(placeholder);
if (current == null) {
placeholderUsageCurrent.put(placeholder, nanoseconds);
} else {
placeholderUsageCurrent.put(placeholder, current + nanoseconds);
}
addTime(placeholderUsageCurrent, placeholder, nanoseconds);
}
/**
@@ -384,11 +413,30 @@ public class CPUManager {
* @param nanoseconds - time to add
*/
public void addBridgePlaceholderTime(String placeholder, long nanoseconds) {
Long current = bridgePlaceholderUsageCurrent.get(placeholder);
addTime(bridgePlaceholderUsageCurrent, placeholder, nanoseconds);
}
/**
* Adds method time to defined method
* @param method - method to add time to
* @param nanoseconds - time to add
*/
public void addMethodTime(String method, long nanoseconds) {
addTime(methodUsageCurrent, method, nanoseconds);
}
/**
* Adds used time to specified key into specified map
* @param map - map to add usage to
* @param key - usage key
* @param time - nanoseconds the task took
*/
private void addTime(Map<String, AtomicLong> map, String key, long time) {
AtomicLong current = map.get(key);
if (current == null) {
bridgePlaceholderUsageCurrent.put(placeholder, nanoseconds);
map.put(key, new AtomicLong(time));
} else {
bridgePlaceholderUsageCurrent.put(placeholder, current + nanoseconds);
current.addAndGet(time);
}
}
@@ -397,11 +445,11 @@ public class CPUManager {
* @param feature - feature to increment packet counter of
*/
public void packetSent(Object feature) {
Integer current = packetsCurrent.get(feature);
AtomicInteger current = packetsCurrent.get(feature);
if (current == null) {
packetsCurrent.put(feature, 1);
packetsCurrent.put(feature, new AtomicInteger(1));
} else {
packetsCurrent.put(feature, current + 1);
current.addAndGet(1);
}
}
}
@@ -28,6 +28,7 @@ public enum TabFeature {
PACKET_SERIALIZING("Packet serializing"),
TABLIST_LAYOUT("Tablist layout"),
PLUGIN_MESSAGE_HANDLING("Plugin message handling"),
PING_SPOOF("Ping spoof"),
OTHER("Other");
//user-friendly name to be used in /tab cpu
@@ -12,7 +12,6 @@ public enum UsageType {
WORLD_SWITCH_EVENT("PlayerChangedWorldEvent"),
PLAYER_TOGGLE_SNEAK_EVENT("PlayerToggleSneakEvent"),
PLAYER_MOVE_EVENT("PlayerMoveEvent"),
PLAYER_TELEPORT_EVENT("PlayerTeleportEvent"),
PLAYER_RESPAWN_EVENT("PlayerRespawnEvent"),
COMMAND_PREPROCESS("PlayerCommandPreprocessEvent"),
PLUGIN_MESSAGE_EVENT("PluginMessageEvent"),
@@ -28,11 +27,12 @@ public enum UsageType {
PACKET_LOGIN("PacketLogin"),
PACKET_DISPLAY_OBJECTIVE("PacketPlayOutScoreboardDisplayObjective"),
PACKET_OBJECTIVE("PacketPlayOutScoreboardObjective"),
PACKET_HEADER_FOOTER("PacketPlayOutPlayerListHeaderFooter"),
PACKET_NAMED_ENTITY_SPAWN("PacketPlayOutNamedEntitySpawn"),
PACKET_ENTITY_DESTROY("PacketPlayOutEntityDestroy"),
//nametags
REFRESHING_COLLISION("Refreshing collision rule"),
REFRESHING_NAMETAG_VISIBILITY("Refreshing nametag visibility"),
REFRESHING_NAMETAG_VISIBILITY_AND_COLLISION("Refreshing nametag visibility and collision"),
REFRESHING_TEAM_NAME("Refreshing team name"),
//bossbar
@@ -41,7 +41,7 @@ public enum UsageType {
//other
REFRESHING("Refreshing"),
REPEATING_TASK("Repeating task"),
TICKING_UNLIMITED_NAMETAGS("Ticking unlimited nametag mode"),
TICKING_VEHICLES("Ticking vehicles"),
v1_8_0_BUG_COMPENSATION("Compensating for 1.8.0 bugs");
//user-friendly name to be used in /tab cpu
@@ -4,14 +4,11 @@ import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
import me.neznamy.tab.shared.packets.IChatBaseComponent;
@@ -19,7 +16,7 @@ import me.neznamy.tab.shared.packets.IChatBaseComponent;
/**
* Additional code for Playerlist class to secure alignment
*/
public class AlignedSuffix implements Loadable, JoinEventListener, QuitEventListener, WorldChangeListener{
public class AlignedSuffix implements QuitEventListener, WorldChangeListener {
private TAB tab;
private int maxWidth;
@@ -31,39 +28,28 @@ public class AlignedSuffix implements Loadable, JoinEventListener, QuitEventList
this.tab = tab;
this.playerlist = playerlist;
loadWidthsFromFile();
boolean save = false;
Map<Integer, Integer> extraWidths = tab.getConfiguration().premiumconfig.getConfigurationSection("extra-character-widths");
for (Integer entry : new HashSet<>(extraWidths.keySet())) {
char c = (char)(int)entry;
int width = (int)extraWidths.get(entry);
if (widths.containsKey(c)) {
extraWidths.remove((int)c);
tab.print('2', "Deleting character width of " + (int)c + " from extra-character-widths because it already exists inside the plugin.");
save = true;
} else {
widths.put(c, (byte)width);
}
}
if (save) tab.getConfiguration().premiumconfig.save();
tab.debug("Loaded " + widths.size() + " character widths.");
}
/**
* Loads all predefined widths from included widths.txt file
*/
private void loadWidthsFromFile() {
try {
InputStream input = getClass().getClassLoader().getResourceAsStream("widths.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(input));
String line;
int characterId = 1;
while ((line = br.readLine()) != null) {
if (line.isEmpty()) continue;
String[] arr = line.split(":");
widths.put((char)Integer.parseInt(arr[0]), (byte)Integer.parseInt(arr[1]));
widths.put((char)characterId++, (byte)Float.parseFloat(line));
}
br.close();
} catch (Exception ex) {
tab.getErrorManager().criticalError("Failed to read character widths from file", ex);
}
}
public String formatNameAndUpdateLeader(TabPlayer player, String prefixAndName, String suffix) {
int playerNameWidth = getTextWidth(IChatBaseComponent.fromColoredText(prefixAndName + suffix));
public String formatNameAndUpdateLeader(TabPlayer player, TabPlayer viewer) {
int playerNameWidth = getTextWidth(IChatBaseComponent.fromColoredText(player.getProperty("tabprefix").getFormat(null) + player.getProperty("customtabname").getFormat(null) + player.getProperty("tabsuffix").getFormat(null)));
if (player == maxPlayer && playerNameWidth < maxWidth) {
maxWidth = playerNameWidth;
for (TabPlayer all : tab.getPlayers()) {
@@ -79,7 +65,7 @@ public class AlignedSuffix implements Loadable, JoinEventListener, QuitEventList
maxPlayer = player;
updateAllNames(player);
}
return formatName(prefixAndName, suffix);
return formatName(player.getProperty("tabprefix").getFormat(viewer) + player.getProperty("customtabname").getFormat(viewer), player.getProperty("tabsuffix").getFormat(viewer));
}
public String formatName(String prefixAndName, String suffix) {
@@ -94,101 +80,74 @@ public class AlignedSuffix implements Loadable, JoinEventListener, QuitEventList
newFormat += tab.getPlaceholderManager().getLastColors(prefixAndName) + suffix;
return newFormat;
}
/**
* Returns text width of characters in given component
* @param component - component to get width of
* @return text width of characters in given component
*/
private int getTextWidth(IChatBaseComponent component) {
int width = 0;
if (component.getText() != null) {
for (Character c : component.getText().toCharArray()) {
if (widths.containsKey(c)) {
int localWidth = widths.get(c);
if (component.isBold()) {
width += localWidth+1;
} else {
width += localWidth;
}
if (component.isBold()) {
width += widths.get(c)+2;
} else {
tab.getErrorManager().oneTimeConsoleError("Unknown character " + c + " (" + ((int)c) + ") found when aligning tabsuffix. Configure it using /tab width <character|ID>.");
width += widths.get(c)+1;
}
}
//there is 1 pixel space between characters, but not after last one
width += component.getText().length()-1;
}
if (component.getExtra() != null) {
for (IChatBaseComponent extra : component.getExtra()) {
int extraWidth = getTextWidth(extra);
//ignoring empty components
if (extraWidth > 0) {
width += extraWidth + 1; //1 pixel space between characters
}
}
for (IChatBaseComponent extra : component.getExtra()) {
width += getTextWidth(extra);
}
return width;
}
/**
* Returns width of player's tablist name format
* @param p - player to get width for
* @return width of player's tablist name format
*/
private int getPlayerNameWidth(TabPlayer p) {
String format = p.getProperty("tabprefix").get() + p.getProperty("customtabname").get() + p.getProperty("tabsuffix").get();
String format = p.getProperty("tabprefix").getFormat(null) + p.getProperty("customtabname").getFormat(null) + p.getProperty("tabsuffix").getFormat(null);
return getTextWidth(IChatBaseComponent.fromColoredText(format));
}
/**
* Returns a combination of normal and bold spaces to build exactly the requested amount of pixels.
* Must be at least 12 as lower numbers cannot always be built using numbers 4 (normal space + 1 pixel) and 5 (bold space + 1 pixel)
* Returns the result string with normal then bold spaces, such as " &l "
* Returns the result string with normal then bold spaces, such as " &l &r"
* @param pixelWidth - amount of pixels to be built
* @return string consisting of spaces and &l &r (if needed)
* @return string consisting of spaces and &l &r
* @throws IllegalArgumentException if pixelWidth is < 12
*/
private String buildSpaces(int pixelWidth) {
if (pixelWidth < 12) throw new IllegalArgumentException("Cannot build space lower than 12 pixels wide");
int pixelsLeft = pixelWidth;
int boldSpaces = 0;
int normalSpaces = 0;
while (pixelsLeft % 4 != 0) {
pixelsLeft -= 5;
boldSpaces++;
}
while (pixelsLeft > 0) {
StringBuilder output = new StringBuilder();
while (pixelsLeft % 5 != 0) {
pixelsLeft -= 4;
normalSpaces++;
output.append(" ");
}
String output = "";
for (int i=0; i<normalSpaces; i++) {
output += " ";
}
if (boldSpaces > 0) {
output += "&l";
for (int i=0; i<boldSpaces; i++) {
output += " ";
}
output += "&r";
}
return tab.getPlaceholderManager().color(output);
}
@Override
public void load() {
recalculateMaxWidth(null);
}
@Override
public void unload() {
//nothing to do here, Playerlist feature handles unloading
}
@Override
public void onJoin(TabPlayer p) {
int width = getPlayerNameWidth(p);
if (width > maxWidth) {
maxWidth = width;
maxPlayer = p;
updateAllNames(null);
output.append("\u00a7l");
while (pixelsLeft > 0) {
pixelsLeft -= 5;
output.append(" ");
}
output.append("\u00a7r");
return output.toString();
}
@Override
public void onQuit(TabPlayer p) {
if (maxPlayer == p && recalculateMaxWidth(p)) {
updateAllNames(null);
if (recalculateMaxWidth(p)) {
updateAllNames(p);
}
}
@Override
public void onWorldChange(TabPlayer p, String from, String to) {
if (maxPlayer == p && recalculateMaxWidth(null)) {
if (recalculateMaxWidth(null)) {
updateAllNames(null);
}
}
@@ -42,6 +42,7 @@ public class BelowName implements Loadable, JoinEventListener, WorldChangeListen
rawText = tab.getConfiguration().config.getString("classic-vanilla-belowname.text", "Health");
disabledWorlds = tab.getConfiguration().config.getStringList("disable-features-in-"+tab.getPlatform().getSeparatorType()+"s.belowname", Arrays.asList("disabled" + tab.getPlatform().getSeparatorType()));
refreshUsedPlaceholders();
tab.debug(String.format("Loaded BelowName feature with parameters number=%s, text=%s, disabledWorlds=%s", rawNumber, rawText, disabledWorlds));
tab.getFeatureManager().registerFeature("belowname-text", new Refreshable() {
private List<String> usedPlaceholders;
@@ -122,7 +123,7 @@ public class BelowName implements Loadable, JoinEventListener, WorldChangeListen
}
private int getValue(TabPlayer p) {
return tab.getErrorManager().parseInteger(p.getProperty(numberPropertyName).updateAndGet(), 0, "belowname");
return tab.getErrorManager().parseInteger(p.getProperty(numberPropertyName).updateAndGet(), 0, "belowname number");
}
@Override
@@ -14,12 +14,17 @@ import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
*/
public class GhostPlayerFix implements QuitEventListener {
public GhostPlayerFix() {
TAB.getInstance().debug("Loaded GhostPlayerFix feature");
}
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
TAB.getInstance().getCPUManager().runTaskLater(200, "removing players", getFeatureType(), UsageType.PLAYER_QUIT_EVENT, new Runnable() {
TAB.getInstance().getCPUManager().runTaskLater(500, "removing players", getFeatureType(), UsageType.PLAYER_QUIT_EVENT, new Runnable() {
@Override
public void run() {
if (TAB.getInstance().getPlayer(disconnectedPlayer.getName()) != null) return; //player reconnected meanwhile, not removing then
for (TabPlayer all : TAB.getInstance().getPlayers()) {
if (all == disconnectedPlayer) continue;
all.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, new PlayerInfoData(disconnectedPlayer.getUniqueId())), getFeatureType());
@@ -1,5 +1,6 @@
package me.neznamy.tab.shared.features;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -26,12 +27,14 @@ import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
public class GlobalPlayerlist implements Loadable, JoinEventListener, QuitEventListener, WorldChangeListener, PlayerInfoPacketListener{
private TAB tab;
public final String PREMIUMVANISH_SEE_VANISHED_PERMISSION = "pv.see";
private List<String> spyServers;
private Map<String, List<String>> sharedServers;
private boolean displayAsSpectators;
public boolean vanishedAsSpectators;
private boolean isolateUnlistedServers;
private List<TabPlayer> vanishedPlayers = new ArrayList<TabPlayer>();
public GlobalPlayerlist(TAB tab) {
this.tab = tab;
@@ -50,11 +53,40 @@ public class GlobalPlayerlist implements Loadable, JoinEventListener, QuitEventL
if (shouldSee(viewer, displayed)) viewer.sendCustomPacket(getAddPacket(displayed, viewer), getFeatureType());
}
}
tab.debug(String.format("Loaded GlobalPlayerlist feature with parameters spyServers=%s, sharedServers=%s, displayAsSpectators=%s, vanishedAsSpectators=%s, isolateUnlistedServers=%s",
spyServers, sharedServers, displayAsSpectators, vanishedAsSpectators, isolateUnlistedServers));
startTask();
}
private void startTask() {
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing vanished players", getFeatureType(), UsageType.REPEATING_TASK, () -> {
for (TabPlayer p : tab.getPlayers()) {
if (vanishedPlayers.contains(p) && !p.isVanished()) {
vanishedPlayers.remove(p);
for (TabPlayer viewer : tab.getPlayers()) {
if (viewer == p) continue;
if (shouldSee(viewer, p)) {
viewer.sendCustomPacket(getAddPacket(p, viewer), getFeatureType());
}
}
}
if (!vanishedPlayers.contains(p) && p.isVanished()) {
vanishedPlayers.add(p);
for (TabPlayer all : tab.getPlayers()) {
if (all == p) continue;
if (!shouldSee(all, p)) {
all.sendCustomPacket(getRemovePacket(p), getFeatureType());
}
}
}
}
});
}
public boolean shouldSee(TabPlayer viewer, TabPlayer displayed) {
if (displayed == viewer) return true;
if (displayed.isVanished() && !viewer.hasPermission(PREMIUMVANISH_SEE_VANISHED_PERMISSION)) return false;
if (displayed.isVanished() && !viewer.hasPermission("tab.seevanished")) return false;
if (spyServers.contains(viewer.getWorldName())) return true;
return getServerGroup(viewer.getWorldName()).equals(getServerGroup(displayed.getWorldName()));
}
@@ -1,9 +1,11 @@
package me.neznamy.tab.shared.features;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.cpu.UsageType;
@@ -23,13 +25,16 @@ public class GroupRefresher implements Feature {
this.tab = tab;
usePrimaryGroup = tab.getConfiguration().config.getBoolean("use-primary-group", true);
groupsByPermissions = tab.getConfiguration().config.getBoolean("assign-groups-by-permissions", false);
primaryGroupFindingList = tab.getConfiguration().config.getStringList("primary-group-finding-list", Arrays.asList("Owner", "Admin", "Helper", "default"));
primaryGroupFindingList = new ArrayList<String>();
for (Object group : tab.getConfiguration().config.getStringList("primary-group-finding-list", Arrays.asList("Owner", "Admin", "Helper", "default"))){
primaryGroupFindingList.add(group.toString());
}
tab.getCPUManager().startRepeatingMeasuredTask(1000, "refreshing permission groups", getFeatureType(), UsageType.REPEATING_TASK, new Runnable() {
@Override
public void run() {
for (TabPlayer p : tab.getPlayers()) {
p.setGroup(detectPermissionGroup(p), true);
((ITabPlayer) p).setGroup(detectPermissionGroup(p), true);
}
}
});
@@ -57,9 +62,9 @@ public class GroupRefresher implements Feature {
try {
String[] playerGroups = tab.getPermissionPlugin().getAllGroups(p);
if (playerGroups != null && playerGroups.length > 0) {
for (Object groupFromList : primaryGroupFindingList) {
for (String groupFromList : primaryGroupFindingList) {
for (String playerGroup : playerGroups) {
if (playerGroup.equalsIgnoreCase(groupFromList + "")) {
if (playerGroup.equalsIgnoreCase(groupFromList)) {
return playerGroup;
}
}
@@ -11,14 +11,13 @@ import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.Refreshable;
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
import me.neznamy.tab.shared.features.types.packet.HeaderFooterPacketListener;
import me.neznamy.tab.shared.packets.IChatBaseComponent;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerListHeaderFooter;
/**
* Feature handler for header and footer
*/
public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeListener, Refreshable, HeaderFooterPacketListener {
public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeListener, Refreshable {
private TAB tab;
private List<String> usedPlaceholders;
@@ -28,6 +27,7 @@ public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeLis
this.tab = tab;
disabledWorlds = tab.getConfiguration().config.getStringList("disable-features-in-"+tab.getPlatform().getSeparatorType()+"s.header-footer", Arrays.asList("disabled" + tab.getPlatform().getSeparatorType()));
refreshUsedPlaceholders();
tab.debug(String.format("Loaded HeaderFooter feature with parameters disabledWorlds=%s", disabledWorlds));
}
@Override
@@ -56,7 +56,26 @@ public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeLis
if (isDisabledWorld(disabledWorlds, p.getWorldName())) {
if (!isDisabledWorld(disabledWorlds, from)) p.sendCustomPacket(new PacketPlayOutPlayerListHeaderFooter("", ""), getFeatureType());
} else {
refresh(p, true);
boolean refresh = false;
String headerAppend = getValue(p, "headerappend");
if (headerAppend.length() > 0) headerAppend = "\n\u00a7r" + headerAppend;
String header = getValue(p, "header") + headerAppend;
if (!p.getProperty("header").getOriginalRawValue().equals(header)) {
p.setProperty("header", header);
refresh = true;
}
String footerAppend = getValue(p, "footerappend");
if (footerAppend.length() > 0) footerAppend = "\n\u00a7r" + footerAppend;
String footer = getValue(p, "footer") + footerAppend;
if (!p.getProperty("footer").getOriginalRawValue().equals(footer)) {
p.setProperty("footer", footer);
refresh = true;
}
if (refresh) {
p.sendCustomPacket(new PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent.optimizedComponent(p.getProperty("header").updateAndGet()),
IChatBaseComponent.optimizedComponent(p.getProperty("footer").updateAndGet())), getFeatureType());
}
}
}
@@ -72,9 +91,8 @@ public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeLis
p.setProperty("footer", getValue(p, "footer") + footerAppend);
}
if (isDisabledWorld(disabledWorlds, p.getWorldName()) || p.getVersion().getMinorVersion() < 8) return;
IChatBaseComponent header = IChatBaseComponent.optimizedComponent(p.getProperty("header").updateAndGet());
if (header.getText() != null) header.setText("\u00a70\u00a71\u00a72\u00a7r" + header.getText());
p.sendCustomPacket(new PacketPlayOutPlayerListHeaderFooter(header, IChatBaseComponent.optimizedComponent(p.getProperty("footer").updateAndGet())), getFeatureType());
p.sendCustomPacket(new PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent.optimizedComponent(p.getProperty("header").updateAndGet()),
IChatBaseComponent.optimizedComponent(p.getProperty("footer").updateAndGet())), getFeatureType());
}
@Override
@@ -112,14 +130,4 @@ public class HeaderFooter implements Loadable, JoinEventListener, WorldChangeLis
public TabFeature getFeatureType() {
return TabFeature.HEADER_FOOTER;
}
@Override
public boolean onPacketSend(TabPlayer packetReciver, PacketPlayOutPlayerListHeaderFooter packet) {
if (!isDisabledWorld(disabledWorlds, packetReciver.getWorldName()) && packet.header.getText() != null &&
!packet.header.getText().startsWith("\u00a70\u00a71\u00a72\u00a7r")) {
tab.getErrorManager().printError("Some plugin just tried to send header " + packet.header.toString() + " and footer " + packet.footer.toString(), null, false, tab.getErrorManager().antiOverrideLog);
return true;
}
return false;
}
}
@@ -1,5 +1,6 @@
package me.neznamy.tab.shared.features;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -8,6 +9,7 @@ import java.util.Map;
import java.util.Set;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.PacketAPI;
import me.neznamy.tab.shared.Property;
import me.neznamy.tab.shared.ProtocolVersion;
@@ -15,17 +17,22 @@ import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.cpu.UsageType;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.features.types.Feature;
import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.Refreshable;
import me.neznamy.tab.shared.features.types.event.JoinEventListener;
import me.neznamy.tab.shared.features.types.event.QuitEventListener;
import me.neznamy.tab.shared.features.types.event.WorldChangeListener;
import me.neznamy.tab.shared.features.types.packet.LoginPacketListener;
import me.neznamy.tab.shared.packets.PacketPlayOutScoreboardTeam;
public abstract class NameTag implements Feature, Refreshable {
public class NameTag implements Loadable, Refreshable, LoginPacketListener, QuitEventListener, WorldChangeListener, JoinEventListener {
protected TAB tab;
protected List<String> usedPlaceholders;
protected Set<String> usedPlaceholders;
protected List<String> disabledWorlds;
private boolean collisionRule;
private List<String> revertedCollision;
private boolean invisibleNametags;
protected Set<String> invisiblePlayers = new HashSet<String>();
public Sorting sorting;
protected Map<TabPlayer, Boolean> collision = new HashMap<TabPlayer, Boolean>();
@@ -35,12 +42,40 @@ public abstract class NameTag implements Feature, Refreshable {
disabledWorlds = tab.getConfiguration().config.getStringList("disable-features-in-"+tab.getPlatform().getSeparatorType()+"s.nametag", Arrays.asList("disabled" + tab.getPlatform().getSeparatorType()));
collisionRule = tab.getConfiguration().config.getBoolean("enable-collision", true);
revertedCollision = tab.getConfiguration().config.getStringList("revert-collision-rule-in-" + tab.getPlatform().getSeparatorType()+"s", Arrays.asList("reverted" + tab.getPlatform().getSeparatorType()));
invisibleNametags = tab.getConfiguration().config.getBoolean("invisible-nametags", false);
sorting = new Sorting(tab, this);
usedPlaceholders = new HashSet<>(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive("tagprefix", "tagsuffix"));
for (TabPlayer p : tab.getPlayers()) {
usedPlaceholders.addAll(tab.getPlaceholderManager().getUsedPlaceholderIdentifiersRecursive(
p.getProperty("tagprefix").getCurrentRawValue(), p.getProperty("tagsuffix").getCurrentRawValue()));
}
tab.debug(String.format("Loaded NameTag feature with parameters collisionRule=%s, revertedCollision=%s, disabledWorlds=%s, invisibleNametags=%s",
collisionRule, revertedCollision, disabledWorlds, invisibleNametags));
}
@Override
public void load(){
for (TabPlayer all : tab.getPlayers()) {
((ITabPlayer) all).setTeamName(sorting.getTeamName(all));
updateProperties(all);
collision.put(all, true);
if (all.hasInvisibilityPotion()) invisiblePlayers.add(all.getName());
if (isDisabledWorld(all.getWorldName())) continue;
registerTeam(all);
}
startRefreshingTasks();
}
@Override
public void unload() {
for (TabPlayer p : tab.getPlayers()) {
if (!isDisabledWorld(p.getWorldName())) unregisterTeam(p);
}
}
public void startRefreshingTasks() {
//workaround for a 1.8.x client-sided bug
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing nametag visibility", TabFeature.NAMETAGS, UsageType.REFRESHING_NAMETAG_VISIBILITY, new Runnable() {
tab.getCPUManager().startRepeatingMeasuredTask(500, "refreshing nametag visibility", TabFeature.NAMETAGS, UsageType.REFRESHING_NAMETAG_VISIBILITY_AND_COLLISION, new Runnable() {
public void run() {
//nametag visibility
for (TabPlayer p : tab.getPlayers()) {
@@ -54,15 +89,8 @@ public abstract class NameTag implements Feature, Refreshable {
invisiblePlayers.remove(p.getName());
updateTeamData(p);
}
}
//collision rule
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) {
//cannot control collision rule on <1.9 servers in any way
for (TabPlayer p : tab.getPlayers()) {
if (!p.isLoaded() || isDisabledWorld(p.getWorldName())) continue;
updateCollision(p);
}
if (ProtocolVersion.SERVER_VERSION.getMinorVersion() >= 9) updateCollision(p);
}
}
});
@@ -78,12 +106,11 @@ public abstract class NameTag implements Feature, Refreshable {
@Override
public List<String> getUsedPlaceholders() {
return usedPlaceholders;
return new ArrayList<>(usedPlaceholders);
}
public abstract boolean getTeamVisibility(TabPlayer p, TabPlayer viewer);
public void unregisterTeam(TabPlayer p) {
if (p.hasTeamHandlingPaused()) return;
if (p.getTeamName() == null) return;
for (TabPlayer viewer : tab.getPlayers()) {
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName()).setTeamOptions(69), TabFeature.NAMETAGS);
@@ -91,10 +118,12 @@ public abstract class NameTag implements Feature, Refreshable {
}
public void unregisterTeam(TabPlayer p, TabPlayer viewer) {
if (p.hasTeamHandlingPaused()) return;
viewer.sendCustomPacket(new PacketPlayOutScoreboardTeam(p.getTeamName()).setTeamOptions(69), TabFeature.NAMETAGS);
}
public void registerTeam(TabPlayer p) {
if (p.hasTeamHandlingPaused()) return;
Property tagprefix = p.getProperty("tagprefix");
Property tagsuffix = p.getProperty("tagsuffix");
for (TabPlayer viewer : tab.getPlayers()) {
@@ -105,6 +134,7 @@ public abstract class NameTag implements Feature, Refreshable {
}
public void registerTeam(TabPlayer p, TabPlayer viewer) {
if (p.hasTeamHandlingPaused()) return;
Property tagprefix = p.getProperty("tagprefix");
Property tagsuffix = p.getProperty("tagsuffix");
String replacedPrefix = tagprefix.getFormat(viewer);
@@ -119,7 +149,7 @@ public abstract class NameTag implements Feature, Refreshable {
updateTeamData(p);
} else {
unregisterTeam(p);
p.setTeamName(newName);
((ITabPlayer) p).setTeamName(newName);
registerTeam(p);
}
}
@@ -167,4 +197,90 @@ public abstract class NameTag implements Feature, Refreshable {
}
return collision.get(p);
}
@Override
public void onLoginPacket(TabPlayer packetReceiver) {
for (TabPlayer all : tab.getPlayers()) {
if (!all.isLoaded()) continue;
if (!isDisabledWorld(all.getWorldName())) registerTeam(all, packetReceiver);
}
}
@Override
public void refresh(TabPlayer refreshed, boolean force) {
if (isDisabledWorld(refreshed.getWorldName())) return;
boolean refresh;
if (force) {
updateProperties(refreshed);
refresh = true;
} else {
boolean prefix = refreshed.getProperty("tagprefix").update();
boolean suffix = refreshed.getProperty("tagsuffix").update();
refresh = prefix || suffix;
}
if (refresh) updateTeam(refreshed);
}
@Override
public void onJoin(TabPlayer connectedPlayer) {
((ITabPlayer) connectedPlayer).setTeamName(sorting.getTeamName(connectedPlayer));
updateProperties(connectedPlayer);
collision.put(connectedPlayer, true);
for (TabPlayer all : tab.getPlayers()) {
if (!all.isLoaded()) continue; //avoiding NPE when 2 players join at once
if (all == connectedPlayer) continue; //already registered 3 lines above
if (!isDisabledWorld(all.getWorldName())) registerTeam(all, connectedPlayer);
}
if (isDisabledWorld(connectedPlayer.getWorldName())) return;
registerTeam(connectedPlayer);
}
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
if (!isDisabledWorld(disconnectedPlayer.getWorldName())) unregisterTeam(disconnectedPlayer);
invisiblePlayers.remove(disconnectedPlayer.getName());
collision.remove(disconnectedPlayer);
for (TabPlayer all : tab.getPlayers()) {
if (all == disconnectedPlayer) continue;
all.showNametag(disconnectedPlayer.getUniqueId()); //clearing memory from API method
}
}
@Override
public void onWorldChange(TabPlayer p, String from, String to) {
updateProperties(p);
if (isDisabledWorld(to) && !isDisabledWorld(from)) {
unregisterTeam(p);
} else if (!isDisabledWorld(to) && isDisabledWorld(from)) {
registerTeam(p);
} else {
updateTeam(p);
}
}
@Override
public void refreshUsedPlaceholders() {
usedPlaceholders = new HashSet<>(tab.getConfiguration().config.getUsedPlaceholderIdentifiersRecursive("tagprefix", "tagsuffix"));
for (TabPlayer p : tab.getPlayers()) {
usedPlaceholders.addAll(tab.getPlaceholderManager().getUsedPlaceholderIdentifiersRecursive(
p.getProperty("tagprefix").getCurrentRawValue(), p.getProperty("tagsuffix").getCurrentRawValue()));
}
}
public void updateProperties(TabPlayer p) {
p.loadPropertyFromConfig("tagprefix");
p.loadPropertyFromConfig("tagsuffix");
}
public boolean getTeamVisibility(TabPlayer p, TabPlayer viewer) {
return !p.hasHiddenNametag() && !p.hasHiddenNametag(viewer.getUniqueId()) &&
!invisibleNametags && !invisiblePlayers.contains(p.getName());
}
@Override
public TabFeature getFeatureType() {
return TabFeature.NAMETAGS;
}
}
@@ -0,0 +1,63 @@
package me.neznamy.tab.shared.features;
import java.util.ArrayList;
import java.util.List;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.cpu.TabFeature;
import me.neznamy.tab.shared.features.types.Loadable;
import me.neznamy.tab.shared.features.types.packet.PlayerInfoPacketListener;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import me.neznamy.tab.shared.packets.PacketPlayOutPlayerInfo.PlayerInfoData;
/**
* Sets ping of all players in the packet to configured value to prevent hacked clients from seeing exact ping value of each player
*/
public class PingSpoof implements PlayerInfoPacketListener, Loadable {
//fake ping value
private int value;
/**
* Constructs new instance and loads config options
*/
public PingSpoof() {
value = TAB.getInstance().getConfiguration().config.getInt("ping-spoof.value", 0);
TAB.getInstance().debug(String.format("Loaded PingSpoof feature with parameters value=%s", value));
}
@Override
public void onPacketSend(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
if (info.action != EnumPlayerInfoAction.UPDATE_LATENCY && info.action != EnumPlayerInfoAction.ADD_PLAYER) return;
for (PlayerInfoData playerInfoData : info.entries) {
playerInfoData.latency = value;
}
}
@Override
public TabFeature getFeatureType() {
return TabFeature.PING_SPOOF;
}
@Override
public void load() {
updateAll(false);
}
@Override
public void unload() {
updateAll(true);
}
private void updateAll(boolean realPing) {
List<PlayerInfoData> list = new ArrayList<PlayerInfoData>();
for (TabPlayer p : TAB.getInstance().getPlayers()) {
list.add(new PlayerInfoData(p.getUniqueId(), realPing ? (int) p.getPing() : 0));
}
for (TabPlayer p : TAB.getInstance().getPlayers()) {
p.sendCustomPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.UPDATE_LATENCY, list), getFeatureType());
}
}
}
Loaded 100 of 233 files, more files were not shown because too many files have changed in this diff. Show more