Compare commits

...
174 Commits
Author SHA1 Message Date
NEZNAMY 9a7a105e7b fix %serveronline% and %worldonline% inaccuracies 2022-01-28 11:25:13 +01:00
NEZNAMY f54d024d97 3.0.2 2022-01-27 20:43:29 +01:00
NEZNAMY 94338bd53f update nested placeholders immediately on plugin message 2022-01-27 13:17:24 +01:00
NEZNAMY 1a9ecc0313 fix belowname update packets being sent in disabled worlds / servers 2022-01-26 22:04:13 +01:00
NEZNAMY 4baeb7b945 properly cancel delayed tasks on shutdown / reload 2022-01-26 18:24:24 +01:00
NEZNAMY baee479fb5 3.0.2-pre6 2022-01-26 01:03:29 +01:00
NEZNAMY 5ad4b2e9c4 so many issues from one wrong method call 2022-01-25 22:57:31 +01:00
NEZNAMY 26657cddfc code style suggestions by intellij 2022-01-25 22:56:52 +01:00
NEZNAMY 947a922f8c reduce amount of logged warns in console 2022-01-25 16:08:46 +01:00
NEZNAMY a2be76184e use tab's thread manager for scoreboard and bossbar announcing 2022-01-25 00:43:57 +01:00
NEZNAMY 8ddb733dc3 do not freeze tab's thread with delayed tasks 2022-01-25 00:33:44 +01:00
NEZNAMY 94e016129b run repeating task for the first time with initial delay 2022-01-25 00:33:21 +01:00
NEZNAMY 5e653ded5d process events in a single thread on velocity as well 2022-01-23 22:04:47 +01:00
NEZNAMY 1bf8097c3f 3.0.2-pre5 2022-01-23 21:39:16 +01:00
NEZNAMY 3d2f11ffd2 move enable call just in case 2022-01-23 18:28:08 +01:00
NEZNAMY 5e77a22d7a further javadoc progress 2022-01-23 18:27:46 +01:00
NEZNAMY 1dffcb1dc3 remove all thread safety checks since they are no longer needed 2022-01-23 17:24:26 +01:00
NEZNAMY edc18cbafb run all tasks in the same thread 2022-01-23 16:49:50 +01:00
NEZNAMY 2f23194432 3.0.2-pre4 2022-01-23 11:06:42 +01:00
NEZNAMY 20709472be fix vehicle / passenger handling 2022-01-23 11:06:16 +01:00
NEZNAMY 7a8c6a0cec some more velocity fixes 2022-01-22 23:11:51 +01:00
NEZNAMY 88558e40a7 round health even when copying max health 2022-01-21 14:49:27 +01:00
NEZNAMY 79386b2b6b allow 64 character scoreboard on <1.13 using "Long|" line prefix 2022-01-21 13:12:03 +01:00
NEZNAMY 3aad8d3cb5 allow per-server / per-world properties through commands 2022-01-21 10:49:06 +01:00
NEZNAMY 272a6f37ad fix NPE 2022-01-20 18:49:33 +01:00
NEZNAMY e10081025d fix load() method not executing methods in correct order resulting in enormous load times with large player counts 2022-01-20 18:31:04 +01:00
NEZNAMY e03a6d8f11 attempt to fix rare error probably caused by concurrent access 2022-01-20 18:29:37 +01:00
NEZNAMY 98e7dc0321 match velocity fixes in AlignedPlayerList as well 2022-01-20 18:24:52 +01:00
NEZNAMY 74438c5d74 so this is why someone was getting a NPE 2022-01-20 18:02:14 +01:00
NEZNAMY 0c4e548565 further increase event processing delay on velocity 2022-01-20 17:06:03 +01:00
NEZNAMY cc70e01e96 merge classes together 2022-01-20 14:34:19 +01:00
NEZNAMY 7c635748af simplify 2022-01-20 13:58:05 +01:00
NEZNAMY 0c874ad56d only keep lowercase entries in sorting map 2022-01-20 13:51:24 +01:00
NEZNAMY 2fb7f80b71 always display bossbars in defined order 2022-01-20 13:14:40 +01:00
NEZNAMY 3f7c3414ce increase server switch processing delay on velocity 2022-01-18 11:23:04 +01:00
NEZNAMY 97165db44d fix bridge placeholders rarely not working in scoreboard 2022-01-18 11:11:10 +01:00
NEZNAMY 82cdf04cf0 add feature override check for onServerChange 2022-01-18 11:10:34 +01:00
NEZNAMY 171d899609 3.0.2-pre3 2022-01-14 20:51:37 +01:00
NEZNAMY 6432278d5d fix scoreboard detection from other plugins not resetting on server switch 2022-01-14 20:41:15 +01:00
NEZNAMY ccaff12451 fix NPE caused by concurrent access 2022-01-14 20:40:54 +01:00
NEZNAMY 11347c080b more debug messages 2022-01-14 15:50:13 +01:00
NEZNAMY 553676c3f9 catch LinkageError instead of more specific ones 2022-01-14 15:49:49 +01:00
NEZNAMY 40b70e7418 fix compatibility with forks that set version field to "Unknown" 2022-01-14 14:22:52 +01:00
NEZNAMY 0ea75019ae catch NoSuchMethodError as well 2022-01-14 14:21:46 +01:00
NEZNAMY dbc092df77 add some missing bossbar command feedback (#462) 2022-01-14 11:09:15 +01:00
NEZNAMY 68049f0dca extract duplicated code into method (and also cancel tasks if plugin fails to load) 2022-01-13 20:54:58 +01:00
NEZNAMY 9db5dfc175 fix NPE when getting last value but placeholder threw error 2022-01-13 20:48:49 +01:00
NEZNAMY 039a316b3b clear players if plugin fails to load 2022-01-13 20:48:16 +01:00
NEZNAMY f4b529f1ce update README.md 2022-01-11 18:50:44 +01:00
NEZNAMY 9c6d606103 code cleanup 2022-01-11 17:57:15 +01:00
NEZNAMY 6cdc06aae7 fix config option path 2022-01-11 17:45:54 +01:00
NEZNAMY 2684654e7b drop redundant remove-strings option 2022-01-11 17:45:07 +01:00
NEZNAMY 25bcc33204 remove harming check 2022-01-10 20:44:41 +01:00
Callum Seabrook e5b3f6215b Added relocation for event 2022-01-10 15:03:16 +00:00
NEZNAMY 9306eadd90 a couple of config conversion fixes 2022-01-09 18:42:13 +01:00
NEZNAMY a27fbb5b8b MessageFile now extends YamlConfigurationFile 2022-01-08 17:27:28 +01:00
NEZNAMY cd4c82b0b8 Merge pull request #477 from JannesStroehlein/master
Fixed localization issue when dealing with floats numbers
2022-01-08 14:45:57 +01:00
Jannes Ströhlein aa6179ee9e made all decimal seperators points instead of some being commas. The commas made the placeholder replacement logic malfunction (2,43 -> 243 instead of 2.43) 2022-01-08 14:39:11 +01:00
NEZNAMY 6594b8c2dc fix replacements being applied twice for bridge placeholders (#476) 2022-01-07 10:47:19 +01:00
NEZNAMY d4c2cdf003 fix startup error when using placeholders with colors inside 2022-01-06 18:41:05 +01:00
NEZNAMY 2b529c37f5 fix rare concurrent modification 2022-01-06 17:55:16 +01:00
NEZNAMY 8599cd925b fix %vanished% refreshing with a much higher delay than it should on bungeecord 2022-01-05 14:32:22 +01:00
NEZNAMY 89f37b4b9e add synchronized modifier to stay safe 2022-01-04 16:46:40 +01:00
NEZNAMY f3136e85ee use equals instead of equalsIgnoreCase for better performance 2022-01-04 16:21:18 +01:00
NEZNAMY 845743073c fix wrong mysql checks 2022-01-04 15:33:36 +01:00
NEZNAMY b2c7bacaf9 fix errors when eventbus fails to load 2022-01-04 15:16:34 +01:00
NEZNAMY ed1673f032 fix #472 2022-01-04 15:15:53 +01:00
NEZNAMY bac9d443ea process all bungee events async 2022-01-03 20:32:28 +01:00
NEZNAMY 90e3868c08 use WeakHashMap for player mysql data & fix playeruuid not working 2022-01-03 15:53:07 +01:00
NEZNAMY c428572e01 add null check that doesn't really make sense, but someone needs it 2022-01-03 14:33:16 +01:00
NEZNAMY 884b5755ec 3.0.2-pre2 2022-01-02 22:47:51 +01:00
NEZNAMY 32d7366795 several fixes in redisbungee support when global playerlist is disabled 2022-01-02 22:47:11 +01:00
NEZNAMY 21bf4a5450 fix using % symbol without any placeholders resulting in %% being displayed 2022-01-02 22:38:59 +01:00
NEZNAMY 8042b02cf5 update README.md 2022-01-02 21:59:08 +01:00
NEZNAMY ae7cdd3982 no longer sending redundant packets on server/world switch 2022-01-01 22:03:19 +01:00
NEZNAMY 9ba945ad76 cache components on velocity as well 2022-01-01 21:51:18 +01:00
NEZNAMY 9cd04c3660 small optimization 2022-01-01 19:12:01 +01:00
NEZNAMY 444bc19781 fix pipeline injection detection on velocity 2022-01-01 18:23:46 +01:00
NEZNAMY eb08c4ba04 convert %afk% replacement when upgrading from free version as well 2022-01-01 14:34:57 +01:00
NEZNAMY 5846e24a29 improve placeholder API 2022-01-01 01:33:03 +01:00
NEZNAMY 9c2c09e433 remove debug messages 2021-12-31 14:20:08 +01:00
NEZNAMY 3c4329697b add PlayerLoadEvent#isJoin() method 2021-12-31 14:12:36 +01:00
NEZNAMY af9951d993 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-31 13:54:52 +01:00
NEZNAMY 630f6df2f5 Merge pull request #467 from NamiUni/patch-1
Correctly display the number of players online.
2021-12-31 13:54:38 +01:00
Namiu/うにたろう fcf07b8e7b Correctly display the number of players online. 2021-12-31 21:49:12 +09:00
NEZNAMY 4824c2f626 send remove packet always, just to stay safe 2021-12-30 14:01:05 +01:00
NEZNAMY 2b6b2e1b63 create a constant to reduce memory allocations 2021-12-29 18:02:22 +01:00
NEZNAMY 6926c1e57f redisbungee should not add/remove players when global playerlist is disabled 2021-12-29 17:12:16 +01:00
NEZNAMY 6a00a7f1ca fix wrong map key type 2021-12-29 00:32:51 +01:00
NEZNAMY 288c3d4c87 add onClickCopyToClipBoard method to ChatModifier class 2021-12-29 00:27:11 +01:00
NEZNAMY ac497bcc41 ignore vanished players in layout 2021-12-28 22:31:45 +01:00
NEZNAMY 7b8d0d6845 fix startup error when fixes slots list is null 2021-12-28 22:05:44 +01:00
NEZNAMY a9a4589fae disable logging of invalid numbers due to false positive on BungeeCord before bridge placeholders are initialized 2021-12-28 18:53:29 +01:00
NEZNAMY b8f972a774 disable scoreboard score packet check due to problems it causes 2021-12-28 18:44:14 +01:00
NEZNAMY 305e388478 translate to a more user-friendly exception when bossbar.yml is missing when trying to convert configuration 2021-12-26 15:18:42 +01:00
NEZNAMY 97e7848a78 add catch for IllegalStateException which may be thrown when using plugman 2021-12-26 15:15:16 +01:00
NEZNAMY d33058a924 fix rare error caused by concurrent access 2021-12-26 15:14:53 +01:00
NEZNAMY eda3cc1f17 fix yellow number & belowname not appearing after client reset packet (on server switch) 2021-12-26 14:47:02 +01:00
NEZNAMY 47043ad4ad fix check if injection is loaded by external plugin 2021-12-24 01:10:26 +01:00
NEZNAMY acf20088cc do not send packet if not needed 2021-12-24 01:09:43 +01:00
NEZNAMY 374cb2766c initialize array just in case 2021-12-24 00:54:31 +01:00
NEZNAMY 31685b9ba5 unregister-before-register should always be disabled on proxy 2021-12-24 00:35:38 +01:00
NEZNAMY 722ff4ce52 3.0.2-pre1 2021-12-23 21:05:45 +01:00
NEZNAMY 161b375043 add floodgate as softdepend to avoid md5 message 2021-12-23 21:04:17 +01:00
NEZNAMY 2c14191cde do not send yellow number to players in disabled worlds/servers 2021-12-23 20:48:45 +01:00
NEZNAMY 486546918f (probably) fix #455 2021-12-23 19:18:09 +01:00
NEZNAMY 23d08e58a4 make help menu text configurable (#448) 2021-12-23 19:15:55 +01:00
NEZNAMY 4c4c34ecaa fix #453 2021-12-23 18:55:29 +01:00
NEZNAMY bd3f2fd70a initialize EventBus in declaration 2021-12-23 18:52:59 +01:00
NEZNAMY b80e42a635 fix NPE in custom lines 2021-12-23 15:32:19 +01:00
NEZNAMY 9446a034d0 fix layout resetting on server switch on velocity 2021-12-23 13:41:13 +01:00
NEZNAMY da0c6ab3fd remove unused import 2021-12-23 11:58:57 +01:00
NEZNAMY c1adb27ee5 remove PacketAPI class as it's no longer needed 2021-12-22 20:54:03 +01:00
NEZNAMY 980d3027e3 use defined constant 2021-12-22 19:23:21 +01:00
NEZNAMY 53ba533f9b small optimizations in reducing amount of sent packets 2021-12-22 19:20:27 +01:00
NEZNAMY e40827da65 don't send empty packets 2021-12-22 18:45:15 +01:00
NEZNAMY b9848b6a96 add debug message for player quit processing time 2021-12-22 18:09:03 +01:00
NEZNAMY 147593b11a use WeakHashMap to store player data in features to not need to remove it on quit 2021-12-22 18:08:47 +01:00
NEZNAMY 21652f928b remove online placeholder update on join/leave 2021-12-22 16:05:07 +01:00
NEZNAMY 5b0352aff0 only unregister teams, no need for scores as well 2021-12-22 15:18:21 +01:00
NEZNAMY b2da2fa096 update README.md 2021-12-22 15:04:12 +01:00
NEZNAMY 0cbb01a2a8 fix #447 2021-12-22 15:02:55 +01:00
NEZNAMY 179840968d Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-22 15:00:53 +01:00
NEZNAMY 3b9c1649a1 Merge pull request #452 from kacperleague9/v3
Setup workflow artifact
2021-12-22 14:47:41 +01:00
NEZNAMY 5750c7dbe1 Merge pull request #440 from NEZNAMY/common-events
Added platform agnostic event system
2021-12-22 14:01:16 +01:00
Callum Seabrook 7ceca359be Let's not recreate the event bus on reload 2021-12-22 12:42:48 +00:00
Callum Seabrook 0f8152a6ed Remove priorities, make events interfaces, and expose bus to API 2021-12-22 11:44:56 +00:00
Callum Seabrook b3258bb020 Added platform agnostic event system 2021-12-22 11:43:30 +00:00
Kacper Smoliński 4b2e705cbc Update build.yml 2021-12-22 08:43:48 +01:00
Kacper Smoliński 31e41382f8 Update build.yml 2021-12-22 08:39:30 +01:00
Kacper Smoliński ecda7192dc Update build.yml 2021-12-22 08:22:32 +01:00
Kacper Smoliński 9a48562b9f test gradle workflow 2021-12-22 08:20:18 +01:00
NEZNAMY c181b58bc1 fix NPE that makes no sense 2021-12-21 17:18:32 +01:00
NEZNAMY 2624afa2ed update README.md 2021-12-21 12:04:18 +01:00
Callum Seabrook 3690af1e35 Removed vanished placeholder from Krypton registry 2021-12-20 18:09:13 +00:00
NEZNAMY 98966d3b38 update README.md 2021-12-20 19:06:09 +01:00
NEZNAMY 41fdd16ce1 fix error when running /tab scoreboard show 2021-12-19 18:52:25 +01:00
NEZNAMY 1bc94859c0 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-19 18:42:23 +01:00
Callum Seabrook 60f6f36437 Error manager good print stack trace bad 2021-12-19 13:16:43 +00:00
Callum Seabrook 82267ba16a Updated dependencies 2021-12-19 13:10:02 +00:00
NEZNAMY 14260deef8 make method synchronized to avoid rare bug caused by misconfiguration 2021-12-18 23:41:11 +01:00
NEZNAMY 8ba7342ccf register vanish placeholder in universal since implementation is same 2021-12-18 12:19:32 +01:00
NEZNAMY f6b935d78e fix double vanish status request on placeholder refresh 2021-12-18 12:17:39 +01:00
NEZNAMY 8cf3ae2091 ping spoof should only affect real players (#444) 2021-12-17 14:55:40 +01:00
NEZNAMY e02b997521 do not use %tps% in default config on bungeecord 2021-12-17 14:50:20 +01:00
NEZNAMY 229cef13c9 add cooldowns for attribute refreshing to prevent massive packet spam with many online players 2021-12-17 14:37:14 +01:00
NEZNAMY f503d6f896 move initialization to declaration to try to fix #442 2021-12-17 14:36:33 +01:00
NEZNAMY d851166bb7 add check for duplicated objectives and score updates without objectives 2021-12-17 13:56:44 +01:00
NEZNAMY 638a861350 display plugin message content in sent packets logging 2021-12-16 20:41:09 +01:00
NEZNAMY 20d7e4cbb0 fix entries with same value skipped in /tab cpu 2021-12-16 20:40:27 +01:00
NEZNAMY 36ccedd7c6 remove unused import 2021-12-16 18:08:38 +01:00
NEZNAMY 31854ba7a6 fix world being requested from bridge twice on reload 2021-12-16 18:07:22 +01:00
NEZNAMY 2639f07f5c recode and fixed 11 issues in config conversion 2021-12-16 17:56:29 +01:00
NEZNAMY b6e13f557f small fix in default config 2021-12-16 17:02:41 +01:00
NEZNAMY 6333d90059 remove redundant check 2021-12-16 17:02:27 +01:00
NEZNAMY fea5d0ec56 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2021-12-16 11:07:50 +01:00
NEZNAMY c77f02d1fd disable LD hook until they fix the repository 2021-12-16 11:04:43 +01:00
NEZNAMY c498965cae add API to return player's active scoreboard 2021-12-16 11:03:54 +01:00
Callum Seabrook cdaa77071d Updated Krypton to 0.59.2 2021-12-14 11:48:25 +00:00
NEZNAMY 6a72678c8d simplify 2021-12-14 10:19:47 +01:00
NEZNAMY bb67fc622f fix errors when redisbungee is not enabled correctly 2021-12-13 18:22:16 +01:00
NEZNAMY 5b50c02d93 fix replacements not working in some placeholders 2021-12-13 16:36:54 +01:00
NEZNAMY ffec7e3266 fix world/server groups loading in header/footer 2021-12-13 14:09:56 +01:00
NEZNAMY 039c1e7027 fix velocity compilation 2021-12-11 21:27:29 +01:00
NEZNAMY b27a21b3b8 remove unused variable and imports 2021-12-11 20:03:44 +01:00
NEZNAMY 2bcb5f617e create constants for feature names 2021-12-11 19:51:11 +01:00
NEZNAMY 7bf5294f9f remove redundant check 2021-12-11 17:43:49 +01:00
NEZNAMY c2fd1940f0 separate character width file loading into another method 2021-12-11 17:41:35 +01:00
NEZNAMY 2d095ce6db fix compilation 2021-12-11 17:24:57 +01:00
NEZNAMY e88bc36fca remove unused field 2021-12-11 17:23:20 +01:00
NEZNAMY 5613ea4f77 fix placeholders not updating in replacements 2021-12-11 17:12:58 +01:00
NEZNAMY 9c41599d60 remove redundant escape (again) 2021-12-11 16:04:40 +01:00
NEZNAMY 5f8dd869e0 remove redundant escape in RGB patterns 2021-12-11 15:58:45 +01:00
NEZNAMY fb17df0b7e add registered placeholders to command complete in parse command 2021-12-11 14:53:38 +01:00
153 changed files with 2639 additions and 1969 deletions

No files matched your search

+5
View File
@@ -37,3 +37,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
run: ./gradlew clean build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v2.3.1
with:
name: Jar
path: jar/build/libs/TAB-**.jar
+34 -11
View File
@@ -1,18 +1,41 @@
# About
This is where you can download the plugin for free without needing to buy me a coffee or just grab some code inspiration for your plugin. God bless opensource.
# Compiling from source
TAB aims to be a superior all-in-one minecraft plugin for displaying information that outperforms all
similar plugins in terms of features, performance, compatibility and support.
More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/Why-TAB%3F) wiki page.
# Distribution
As of TAB 3.0.0, released with MC 1.18 (November 30th, 2021), there is no free / premium
version anymore. Only one version of the plugin exists now, which contains all implemented features
and is available to everyone for free. However, official support on discord is only provided to
those who have purchased the plugin.
# Compiling
Compilation requires JDK 8 and up.
To compile the plugin, run `./gradlew build` from the terminal.
Once the plugin compiles, you can grab the jar from `jar/build/libs/` folder.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
# Links
See [releases](https://github.com/NEZNAMY/TAB/releases) for precompiled jar.
Has the plugin helped you in any way, and you wish to support the development?
You can purchase the plugin on [MC-Market](https://www.mc-market.org/resources/20631/), [Polymart](https://polymart.org/resource/484) or just [send a donation](https://paypal.me/neznamy1).
The precompiled JAR can be downloaded for free from
[github releases](https://github.com/NEZNAMY/TAB/releases)
and [MC-Market](https://www.mc-market.org/resources/20631/).
Supporters have access to high-quality [discord support](https://discord.gg/EaSvdk6).
Want to know more about the plugin's functions? Check out the [wiki](https://github.com/NEZNAMY/TAB/wiki).
If you want to show your appreciation for my work, you can purchase the plugin on
[MC-Market](https://www.mc-market.org/resources/14009/),
[Polymart](https://polymart.org/resource/484) or just [send a donation](https://paypal.me/neznamy1).
Support is provided to buyers on [discord](https://discord.gg/EaSvdk6).
A detailed explanation of all the plugin's functions can be found on the
[wiki](https://github.com/NEZNAMY/TAB/wiki).
# Issues
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is **only** available for **bug reports**
or other issues which are not directly a bug (performance issues, errors, etc.).
Support is provided on discord and feature requests are currently not accepted due to high demand and
being busy with bug reports, which take priority over new features.
We are not looking to add any new major features anymore.
We would rather spend time improving existing features instead.
When opening a bug report, make sure you provide all necessary information to reproduce the bug,
otherwise we will not able to fix it. Saying "it only occurs randomly" or "I don't know how to
reproduce it" has no value and cannot be converted into actual code.
@@ -1,7 +1,6 @@
package me.neznamy.tab.api;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -15,7 +14,7 @@ public class ArmorStandManager {
private final Map<String, ArmorStand> armorStands = new LinkedHashMap<>();
//players in entity tracking range
private final List<TabPlayer> nearbyPlayers = Collections.synchronizedList(new ArrayList<>());
private final List<TabPlayer> nearbyPlayers = new ArrayList<>();
//array to iterate over to avoid concurrent modification and slightly boost performance & memory
private ArmorStand[] armorStandArray = new ArmorStand[0];
@@ -27,7 +26,7 @@ public class ArmorStandManager {
* @param name - key name of the armor stand
* @param as - armor stand to add
*/
public synchronized void addArmorStand(String name, ArmorStand as) {
public void addArmorStand(String name, ArmorStand as) {
armorStands.put(name, as);
armorStandArray = armorStands.values().toArray(new ArmorStand[0]);
for (TabPlayer p : nearbyPlayerArray) as.spawn(p);
@@ -57,10 +56,8 @@ public class ArmorStandManager {
* @param viewer - player to spawn armor stands for
*/
public void spawn(TabPlayer viewer) {
synchronized (nearbyPlayers) {
nearbyPlayers.add(viewer);
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
}
nearbyPlayers.add(viewer);
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
if (viewer.getVersion().getMinorVersion() < 8) return;
for (ArmorStand a : armorStandArray) a.spawn(viewer);
}
@@ -108,10 +105,7 @@ public class ArmorStandManager {
* @param viewer - player to remove
*/
public void unregisterPlayer(TabPlayer viewer) {
synchronized (nearbyPlayers) {
nearbyPlayers.remove(viewer);
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
}
if (nearbyPlayers.remove(viewer)) nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
}
/**
@@ -119,10 +113,8 @@ public class ArmorStandManager {
*/
public void destroy() {
for (ArmorStand a : armorStandArray) a.destroy();
synchronized (nearbyPlayers) {
nearbyPlayers.clear();
nearbyPlayerArray = new TabPlayer[0];
}
nearbyPlayers.clear();
nearbyPlayerArray = new TabPlayer[0];
}
/**
@@ -64,7 +64,7 @@ public enum ProtocolVersion {
private final int networkId;
//minor version, such as 16
private final int minorVersion;
private int minorVersion;
//friendly name displayed in %player-version% placeholder
private final String friendlyName;
@@ -113,6 +113,17 @@ public enum ProtocolVersion {
return minorVersion;
}
/**
* Overrides minor version of the version instance. This is used internally to
* set minor version of UNKNOWN value to value from package to fix compatibility
* with server forks that set bukkit version field value to "Unknown".
* @param minorVersion
* minor version to change value to
*/
public void setMinorVersion(int minorVersion) {
this.minorVersion = minorVersion;
}
/**
* Returns enum constant of entered version or UNKNOWN if unknown version
* @param friendlyName - friendly name of the version
@@ -4,6 +4,7 @@ import java.util.UUID;
import me.neznamy.tab.api.bossbar.BossBarManager;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.event.EventBus;
import me.neznamy.tab.api.placeholder.PlaceholderManager;
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
import me.neznamy.tab.api.task.ThreadManager;
@@ -78,6 +79,13 @@ public abstract class TabAPI {
public abstract TablistFormatManager getTablistFormatManager();
/**
* Gets the event bus for registering listeners for TAB events.
*
* @return the event bus
*/
public abstract EventBus getEventBus();
/**
* Returns server version. On proxy installation returns PROXY.
* @return server version
@@ -104,4 +112,4 @@ public abstract class TabAPI {
public abstract void debug(String message);
public abstract void logError(String message, Throwable t);
}
}
@@ -1,8 +1,6 @@
package me.neznamy.tab.api;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.*;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
@@ -16,7 +14,7 @@ public abstract class TabFeature {
private final boolean serverWhitelistMode;
protected final String[] disabledWorlds;
private final boolean worldWhitelistMode;
private final List<TabPlayer> disabledPlayers = new ArrayList<>();
private final Set<TabPlayer> disabledPlayers = Collections.newSetFromMap(new WeakHashMap<>());
private final List<String> methodOverrides = new ArrayList<>();
protected TabFeature(String featureName, String refreshDisplayName) {
@@ -49,6 +47,8 @@ public abstract class TabFeature {
methodOverrides.add("onQuit");
if (getClass().getMethod("onWorldChange", TabPlayer.class, String.class, String.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onWorldChange");
if (getClass().getMethod("onServerChange", TabPlayer.class, String.class, String.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onServerChange");
if (getClass().getMethod("onDisplayObjective", TabPlayer.class, PacketPlayOutScoreboardDisplayObjective.class).getDeclaringClass() != TabFeature.class)
methodOverrides.add("onDisplayObjective");
if (getClass().getMethod("onLoginPacket", TabPlayer.class).getDeclaringClass() != TabFeature.class)
@@ -105,7 +105,6 @@ public abstract class TabFeature {
* @param disconnectedPlayer - player who disconnected
*/
public void onQuit(TabPlayer disconnectedPlayer) {
disabledPlayers.remove(disconnectedPlayer);
}
/**
@@ -125,7 +124,7 @@ public abstract class TabFeature {
* @param to - server player changed to
*/
public void onServerChange(TabPlayer changed, String from, String to) {
onWorldChange(changed, null, null);
//empty by default
}
/**
@@ -184,16 +184,18 @@ public interface TabPlayer {
/**
* Loads property from config using standard property loading algorithm
* @param property - property name to load
* @return true if value did not exist or changed, false otherwise
*/
void loadPropertyFromConfig(TabFeature feature, String property);
boolean loadPropertyFromConfig(TabFeature feature, String property);
/**
* Loads property from config using standard property loading algorithm. If the property is
* not set in config, sets it to ifNotSet value
* @param property - property name to load
* @param ifNotSet - value to use if property is not defined in config
* @return true if value did not exist or changed, false otherwise
*/
void loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
boolean loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet);
/**
* Returns name of player's scoreboard team or null if NameTag feature is disabled
@@ -178,6 +178,14 @@ public class ChatModifier {
if (TabAPI.getInstance().getServerVersion().getMinorVersion() < 8) throw new UnsupportedOperationException("change_page click action is not supported on <1.8");
clickEvent = new ChatClickable(EnumClickAction.CHANGE_PAGE, String.valueOf(newPage));
}
/**
* Sets click action to COPY_TO_CLIPBOARD and text to provided value
* @param text - text to copy to clipboard on click
*/
public void onClickCopyToClipBoard(String text) {
clickEvent = new ChatClickable(EnumClickAction.COPY_TO_CLIPBOARD, text);
}
public void onClick(EnumClickAction action, String value) {
clickEvent = new ChatClickable(action, value);
@@ -30,6 +30,9 @@ public enum EnumChatFormat {
ITALIC('o'),
RESET('r');
/** Creating a constant to avoid memory allocations on each request */
private static final EnumChatFormat[] VALUES = values();
/** The symbol minecraft uses to colorize text */
public static final char COLOR_CHAR = 0x00a7;
@@ -116,7 +119,7 @@ public enum EnumChatFormat {
* @return instance from the character or null if character is not valid
*/
public static EnumChatFormat getByChar(char c) {
for (EnumChatFormat format : values()) {
for (EnumChatFormat format : VALUES) {
if (format.character == c) return format;
}
return null;
@@ -135,7 +138,7 @@ public enum EnumChatFormat {
String last = getLastColors(legacyText);
if (last.length() > 0) {
char c = last.toCharArray()[1];
for (EnumChatFormat e : values()) {
for (EnumChatFormat e : VALUES) {
if (e.character == c) return e;
}
}
@@ -177,7 +180,7 @@ public enum EnumChatFormat {
* @return enum value or null if no such combination exists
*/
public static EnumChatFormat fromRGBExact(int red, int green, int blue){
for (EnumChatFormat format : values()) {
for (EnumChatFormat format : VALUES) {
if (format.red == red && format.green == green && format.blue == blue) return format;
}
return null;
@@ -1,11 +1,8 @@
package me.neznamy.tab.api.chat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import me.neznamy.tab.api.util.Preconditions;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
@@ -14,6 +11,7 @@ import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.chat.ChatClickable.EnumClickAction;
import me.neznamy.tab.api.chat.ChatHoverable.EnumHoverAction;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import org.json.simple.parser.ParseException;
/**
* A class representing the n.m.s.IChatBaseComponent class to make work with it much easier
@@ -21,21 +19,21 @@ import me.neznamy.tab.api.chat.rgb.RGBUtils;
@SuppressWarnings("unchecked")
public class IChatBaseComponent {
/**
* Component cache maps to avoid large memory allocations as well as
* higher CPU usage when using animations which send the same text on repeat.
*/
private static final Map<String, IChatBaseComponent> componentCache = new HashMap<>();
private static final Map<IChatBaseComponent, String> serializeCacheModern = new HashMap<>();
private static final Map<IChatBaseComponent, String> serializeCacheLegacy = new HashMap<>();
//constants
private static final String EMPTY_TRANSLATABLE = "{\"translate\":\"\"}";
private static final String EMPTY_TEXT = "{\"text\":\"\"}";
private static final List<IChatBaseComponent> EMPTY_LIST = new ArrayList<>(0);
//component text
/** Text of the component */
private String text;
/** Chat modifier containing color, magic codes, hover and click event */
private ChatModifier modifier = new ChatModifier();
//extra components
/** Extra components used in "extra" field */
private List<IChatBaseComponent> extra;
/**
@@ -46,7 +44,8 @@ public class IChatBaseComponent {
/**
* Constructs a new component which is a clone of provided component
* @param component - component to clone
* @param component
* component to clone
*/
public IChatBaseComponent(IChatBaseComponent component) {
this.text = component.text;
@@ -58,25 +57,29 @@ public class IChatBaseComponent {
/**
* Constructs new instance with given text
* @param text - text to display
* @param text
* text to display
*/
public IChatBaseComponent(String text) {
this.text = text;
}
/**
* Returns list of extra components or null if none are defined
* @return list of extras
* Returns list of extra components. If no extra components are defined, returns empty list.
* @return list of extra components
*/
public List<IChatBaseComponent> getExtra(){
if (extra == null) return EMPTY_LIST;
if (extra == null) return Collections.emptyList();
return extra;
}
/**
* Sets full list of extra components to given list
* @param components - components to use as extra
* @return self
* Sets full list of extra components to given list. Does not allow empty list.
* @param components
* components to use as extra
* @return self
* @throws IllegalArgumentException
* if {@code components} is an empty list
*/
public IChatBaseComponent setExtra(List<IChatBaseComponent> components){
if (components.isEmpty()) throw new IllegalArgumentException("Unexpected empty array of components"); //exception taken from minecraft
@@ -86,105 +89,126 @@ public class IChatBaseComponent {
/**
* Appends provided component as extra component
* @param child - component to append
* @return self
* @param child
* component to append
*/
public IChatBaseComponent addExtra(IChatBaseComponent child) {
public void addExtra(IChatBaseComponent child) {
if (extra == null) extra = new ArrayList<>();
extra.add(child);
return this;
}
/**
* Returns text of this component
* @return text of this component
* @return text of this component
*/
public String getText() {
return text;
}
/**
* Returns chat modifier of this component
* @return chat modifier of this component
*/
public ChatModifier getModifier() {
return modifier;
}
/**
* Sets modifier to provided value
* @param modifier
* modifier to set value to
* @throws IllegalArgumentException
* if {@code modifier} is null
*/
public void setModifier(ChatModifier modifier) {
Preconditions.checkNotNull(modifier, "modifier");
this.modifier = modifier;
}
/**
* Sets text of this component
* @param text - text to show
* @return self
* Changes text of this component
* @param text
* text to show
*/
public IChatBaseComponent setText(String text) {
public void setText(String text) {
this.text = text;
return this;
}
/**
* Deserializes string and returns created component
* @param json - serialized string
* @return Deserialized component
* Deserializes string and returns created component. If provided string is null, returns null.
* @param json
* serialized string
* @return Deserialized component or null if input is null
*/
public static IChatBaseComponent deserialize(String json) {
if (json == null) return null;
if (json.startsWith("\"") && json.endsWith("\"") && json.length() > 1) {
//simple component with only text used, minecraft serializer outputs the text in quotes instead of full json
return new IChatBaseComponent(json.substring(1, json.length()-1));
}
JSONObject jsonObject;
try {
if (json == null) return null;
if (json.startsWith("\"") && json.endsWith("\"") && json.length() > 1) {
//simple component with only text used, minecraft serializer outputs the text in quotes instead of full json
return new IChatBaseComponent(json.substring(1, json.length()-1));
}
JSONObject jsonObject = (JSONObject) new JSONParser().parse(json);
IChatBaseComponent component;
if (jsonObject.containsKey("type")) {
return new ChatComponentEntity((String) jsonObject.get("type"), UUID.fromString((String) jsonObject.get("id")), IChatBaseComponent.deserialize(jsonObject.get("name").toString()).toFlatText());
}
component = new IChatBaseComponent();
component.setText((String) jsonObject.get("text"));
component.modifier.setBold(getBoolean(jsonObject, "bold"));
component.modifier.setItalic(getBoolean(jsonObject, "italic"));
component.modifier.setUnderlined(getBoolean(jsonObject, "underlined"));
component.modifier.setStrikethrough(getBoolean(jsonObject, "strikethrough"));
component.modifier.setObfuscated(getBoolean(jsonObject, "obfuscated"));
component.modifier.setColor(TextColor.fromString(((String) jsonObject.get("color"))));
if (jsonObject.containsKey("clickEvent")) {
JSONObject clickEvent = (JSONObject) jsonObject.get("clickEvent");
String action = (String) clickEvent.get("action");
String value = clickEvent.get("value").toString();
component.modifier.onClick(EnumClickAction.valueOf(action.toUpperCase()), value);
}
if (jsonObject.containsKey("hoverEvent")) {
JSONObject hoverEvent = (JSONObject) jsonObject.get("hoverEvent");
String action = (String) hoverEvent.get("action");
String value = (String) hoverEvent.get("value");
component.modifier.onHover(EnumHoverAction.valueOf(action.toUpperCase()), deserialize(value));
}
if (jsonObject.containsKey("extra")) {
List<Object> list = (List<Object>) jsonObject.get("extra");
for (Object extra : list) {
String string = extra.toString();
//reverting .toString() removing "" for simple text
if (!string.startsWith("{")) string = "\"" + string + "\"";
component.addExtra(deserialize(string));
}
}
return component;
} catch (Exception e) {
jsonObject = (JSONObject) new JSONParser().parse(json);
} catch (ParseException e) {
TabAPI.getInstance().logError("Failed to deserialize json component " + json, e);
return null;
}
IChatBaseComponent component;
if (jsonObject.containsKey("type")) {
return new ChatComponentEntity((String) jsonObject.get("type"), UUID.fromString((String) jsonObject.get("id")), IChatBaseComponent.deserialize(jsonObject.get("name").toString()).toFlatText());
}
component = new IChatBaseComponent();
component.setText((String) jsonObject.get("text"));
component.modifier.setBold(getBoolean(jsonObject, "bold"));
component.modifier.setItalic(getBoolean(jsonObject, "italic"));
component.modifier.setUnderlined(getBoolean(jsonObject, "underlined"));
component.modifier.setStrikethrough(getBoolean(jsonObject, "strikethrough"));
component.modifier.setObfuscated(getBoolean(jsonObject, "obfuscated"));
component.modifier.setColor(TextColor.fromString(((String) jsonObject.get("color"))));
if (jsonObject.containsKey("clickEvent")) {
JSONObject clickEvent = (JSONObject) jsonObject.get("clickEvent");
String action = (String) clickEvent.get("action");
String value = clickEvent.get("value").toString();
component.modifier.onClick(EnumClickAction.valueOf(action.toUpperCase()), value);
}
if (jsonObject.containsKey("hoverEvent")) {
JSONObject hoverEvent = (JSONObject) jsonObject.get("hoverEvent");
String action = (String) hoverEvent.get("action");
String value = (String) hoverEvent.get("value");
component.modifier.onHover(EnumHoverAction.valueOf(action.toUpperCase()), deserialize(value));
}
if (jsonObject.containsKey("extra")) {
List<Object> list = (List<Object>) jsonObject.get("extra");
for (Object extra : list) {
String string = extra.toString();
//reverting .toString() removing "" for simple text
if (!string.startsWith("{")) string = "\"" + string + "\"";
component.addExtra(deserialize(string));
}
}
return component;
}
/**
* Returns boolean value of requested key
* @param jsonObject - object to get value from
* @param key - name of key
* @return value from json object or null if not present
* Returns boolean value of requested key from map
* @param jsonObject
* map to get value from
* @param key
* name of key
* @return value from json object or null if not present
*/
private static Boolean getBoolean(JSONObject jsonObject, String key) {
String value = String.valueOf(jsonObject.getOrDefault(key, null));
return "null".equals(value) ? null : Boolean.parseBoolean(value);
}
/**
* Converts the component to a string representing the serialized component.
* This method is only used internally by json library since it's missing
* protocol version field used by the method.
* @return serialized component in string form
* @see #toString(ProtocolVersion)
*/
@Override
public String toString() {
JSONObject json = new JSONObject();
@@ -196,28 +220,33 @@ public class IChatBaseComponent {
}
/**
* Serializes this component with colors based on client version
* @param clientVersion - client version
* @return Serialized string
* Serializes this component with colors based on client version.
* If client version is <1.16, HEX colors will be converted to legacy colors.
* @param clientVersion
* client version to adapt component for
* @return serialized string
*/
public String toString(ProtocolVersion clientVersion) {
return toString(clientVersion, false);
}
/**
* Serializes this component with colors based on client version
* @param clientVersion - client version
* @param sendTranslatableIfEmpty - if empty translatable should be sent if text is empty or not
* @return Serialized string
* Serializes this component with colors based on client version.
* If client version is <1.16, HEX colors will be converted to legacy colors.
* @param clientVersion
* client version to adapt component for
* @param sendTranslatableIfEmpty
* whether empty translatable should be sent if text is empty or not
* @return serialized string
*/
public String toString(ProtocolVersion clientVersion, boolean sendTranslatableIfEmpty) {
if (extra == null) {
if (text == null) return null;
if (text.length() == 0) {
if (sendTranslatableIfEmpty) {
return EMPTY_TRANSLATABLE;
return "{\"translate\":\"\"}";
} else {
return EMPTY_TEXT;
return "{\"text\":\"\"}";
}
}
}
@@ -242,8 +271,9 @@ public class IChatBaseComponent {
/**
* Returns organized component from colored text
* @param originalText - text to convert
* @return organized component from colored text
* @param originalText
* text to convert
* @return organized component from colored text
*/
public static IChatBaseComponent fromColoredText(String originalText){
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText), false);
@@ -328,9 +358,11 @@ public class IChatBaseComponent {
/**
* Returns true if text contains legacy color request at defined RGB index start
* @param text - text to check
* @param i - current index start
* @return true if legacy color is defined, false if not
* @param text
* text to check
* @param i
* current index start
* @return true if legacy color is defined, false if not
*/
private static boolean containsLegacyCode(String text, int i) {
if (text.length() - i < 9 || text.charAt(i+7) != '|') return false;
@@ -339,7 +371,7 @@ public class IChatBaseComponent {
/**
* Converts this component into a simple text with legacy colors (the closest match if color is set to RGB)
* @return The simple text format
* @return The simple text format
*/
public String toLegacyText() {
StringBuilder builder = new StringBuilder();
@@ -348,11 +380,13 @@ public class IChatBaseComponent {
}
/**
* Appends text to string builder, might also add color and magic codes if different
* from previous component in chain
* @param builder - builder to append text to
* @param previousFormatting - colors and magic codes in previous component
* @return New formatting, might be identical to previous one
* Appends text to string builder, might also add color and magic codes if they are different
* from previous component in chain.
* @param builder
* builder to append text to
* @param previousFormatting
* colors and magic codes in previous component
* @return wew formatting, might be identical to previous one
*/
private String append(StringBuilder builder, String previousFormatting) {
String formatting = previousFormatting;
@@ -372,7 +406,7 @@ public class IChatBaseComponent {
/**
* Returns colors and magic codes of this component
* @return used colors and magic codes
* @return used colors and magic codes
*/
private String getFormatting() {
StringBuilder builder = new StringBuilder();
@@ -390,7 +424,7 @@ public class IChatBaseComponent {
/**
* Returns raw text without colors, only works correctly when component is organized
* @return raw text in this component
* @return raw text in this component and all child components
*/
public String toRawText() {
StringBuilder builder = new StringBuilder();
@@ -403,7 +437,7 @@ public class IChatBaseComponent {
/**
* Converts the component into flat text with used colors (including rgb) and magic codes
* @return converted text
* @return converted text
*/
public String toFlatText() {
StringBuilder builder = new StringBuilder();
@@ -420,8 +454,9 @@ public class IChatBaseComponent {
* Returns the most optimized component based on text. Returns null if text is null,
* organized component if RGB colors are used or simple component with only text field
* containing the whole text when no RGB colors are used
* @param text - text to create component from
* @return The most performance-optimized component based on text
* @param text
* text to create component from
* @return The most performance-optimized component based on text
*/
public static IChatBaseComponent optimizedComponent(String text){
if (text == null) return null;
@@ -8,13 +8,11 @@ import me.neznamy.tab.api.util.Preconditions;
public class TextColor {
/**
* RGB values from 0 to 255.
* They are only initialized if they are actually used to avoid
* RGB values as a single number of 3 8-bit numbers (0-255).
* It is only initialized if colors are actually used to avoid
* unnecessary memory allocations with string operations.
*/
private short red = -1;
private short green = -1;
private short blue = -1;
private int rgb = -1;
/** Closest legacy color to this color object. */
private EnumChatFormat legacyColor;
@@ -37,9 +35,7 @@ public class TextColor {
*/
public TextColor(TextColor color) {
Preconditions.checkNotNull(color, "color cannot be null");
red = color.red;
green = color.green;
blue = color.blue;
rgb = color.rgb;
legacyColor = color.legacyColor;
hexCode = color.hexCode;
legacyColorForced = color.legacyColorForced;
@@ -83,9 +79,7 @@ public class TextColor {
*/
public TextColor(EnumChatFormat legacyColor) {
Preconditions.checkNotNull(legacyColor, "legacy color cannot be null");
this.red = legacyColor.getRed();
this.green = legacyColor.getGreen();
this.blue = legacyColor.getBlue();
this.rgb = (legacyColor.getRed() << 16) + (legacyColor.getGreen() << 8) + legacyColor.getBlue();
this.hexCode = legacyColor.getHexCode();
}
@@ -101,12 +95,10 @@ public class TextColor {
* if {@code red}, {@code green} or {@code blue} is out of range ({@code 0-255})
*/
public TextColor(int red, int green, int blue) {
Preconditions.checkRange(red, 0, 255);
Preconditions.checkRange(green, 0, 255);
Preconditions.checkRange(blue, 0, 255);
this.red = (short) red;
this.green = (short) green;
this.blue = (short) blue;
Preconditions.checkRange(red, 0, 255, "red color");
Preconditions.checkRange(green, 0, 255, "green color");
Preconditions.checkRange(blue, 0, 255, "blue color");
this.rgb = (red << 16) + (green << 8) + blue;
}
/**
@@ -136,8 +128,8 @@ public class TextColor {
* @return red value
*/
public int getRed() {
if (red == -1) loadColors();
return red;
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
return (rgb >> 16) & 0xFF;
}
/**
@@ -145,8 +137,8 @@ public class TextColor {
* @return green value
*/
public int getGreen() {
if (green == -1) loadColors();
return green;
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
return (rgb >> 8) & 0xFF;
}
/**
@@ -154,20 +146,10 @@ public class TextColor {
* @return blue value
*/
public int getBlue() {
if (blue == -1) loadColors();
return blue;
if (rgb == -1) rgb = Integer.parseInt(hexCode, 16);
return rgb & 0xFF;
}
/**
* Loads red, green and blue values based on current hex code string
*/
private void loadColors(){
int hexColor = Integer.parseInt(hexCode, 16);
red = (short) ((hexColor >> 16) & 0xFF);
green = (short) ((hexColor >> 8) & 0xFF);
blue = (short) (hexColor & 0xFF);
}
/**
* Returns the closest legacy color of this color object.
* If the color was defined in constructor, it's returned.
@@ -175,9 +157,7 @@ public class TextColor {
* @return closest legacy color
*/
public EnumChatFormat getLegacyColor() {
if (legacyColor == null) {
legacyColor = loadClosestColor();
}
if (legacyColor == null) legacyColor = loadClosestColor();
return legacyColor;
}
@@ -186,9 +166,7 @@ public class TextColor {
* @return the rgb combination as a 6-digit hex code string
*/
public String getHexCode() {
if (hexCode == null) {
hexCode = String.format("%06X", (red << 16) + (green << 8) + blue);
}
if (hexCode == null) hexCode = String.format("%06X", rgb);
return hexCode;
}
@@ -10,7 +10,7 @@ import me.neznamy.tab.api.chat.EnumChatFormat;
*/
public class BukkitFormat implements RGBFormatter {
private final Pattern pattern = Pattern.compile("[" + EnumChatFormat.COLOR_CHAR + "&]x[[" + EnumChatFormat.COLOR_CHAR + "&]\\p{XDigit}]{12}");
private final Pattern pattern = Pattern.compile("[" + EnumChatFormat.COLOR_CHAR + "&]x[" + EnumChatFormat.COLOR_CHAR + "&\\p{XDigit}]{12}");
@Override
public String reformat(String text) {
@@ -8,7 +8,7 @@ import java.util.regex.Pattern;
*/
public class CMIFormat implements RGBFormatter {
private final Pattern pattern = Pattern.compile("\\{#[0-9a-fA-F]{6}\\}");
private final Pattern pattern = Pattern.compile("\\{#[0-9a-fA-F]{6}}");
@Override
public String reformat(String text) {
@@ -6,11 +6,11 @@ import java.util.regex.Pattern;
public class CMIGradient extends CommonGradient {
//pattern for {#RRGGBB<>}
private final Pattern shortcutPattern = Pattern.compile("\\{#[0-9a-fA-F]{6}<>\\}");
private final Pattern shortcutPattern = Pattern.compile("\\{#[0-9a-fA-F]{6}<>}");
public CMIGradient() {
super(Pattern.compile("\\{#[0-9a-fA-F]{6}>\\}[^\\{]*\\{#[0-9a-fA-F]{6}<\\}"),
Pattern.compile("\\{#[0-9a-fA-F]{6}\\|.>\\}[^\\{]*\\{#[0-9a-fA-F]{6}<\\}"),
super(Pattern.compile("\\{#[0-9a-fA-F]{6}>}[^{]*\\{#[0-9a-fA-F]{6}<}"),
Pattern.compile("\\{#[0-9a-fA-F]{6}\\|.>}[^{]*\\{#[0-9a-fA-F]{6}<}"),
"{#", 9, 2, 10, 8);
}
@@ -51,7 +51,7 @@ public abstract class ConfigurationFile {
protected ConfigurationFile(InputStream source, File destination) throws IOException {
if (destination == null) throw new IllegalArgumentException("Destination cannot be null");
this.file = destination;
if (file.getParentFile() != null) file.getParentFile().mkdirs();
if (file.getParentFile() != null) Files.createDirectories(file.getParentFile().toPath());
if (!file.exists() && source == null) throw new IllegalStateException("File does not exist and source is null");
if (file.createNewFile()) {
// Files.copy(source, file.toPath());
@@ -308,7 +308,6 @@ public abstract class ConfigurationFile {
public <K, V> Map<K, V> getConfigurationSection(String path) {
if (path == null || path.length() == 0) return (Map<K, V>) values;
Object value = getObject(path, null);
if (value == null) return new LinkedHashMap<>();
if (value instanceof Map) {
return (Map<K, V>) value;
} else {
@@ -0,0 +1,53 @@
package me.neznamy.tab.api.event;
/**
* The event bus. This is used to register event listeners/handlers for
* handling events that may be fired by TAB.
*/
public interface EventBus {
/**
* Scans the given listener for any method that is annotated with
* {@link Subscribe} and registers it as a listener to this event bus.
*
* All methods in this class that are annotated with {@link Subscribe}
* must meet the following criteria:
* <ul>
* <li>It must be <strong>public</strong>.</li>
* <li>It must <strong>not</strong> be abstract.</li>
* <li>It must have exactly <strong>one</strong> parameter.</li>
* <li>Its single parameter must be <strong>an event</strong>.</li>
* </ul>
* If a method does not meet the above criteria, an exception <strong>will</strong>
* be thrown.
*
* @param listener the listener to register
* @throws RuntimeException if a method does not meet the criteria
*/
void register(Object listener);
/**
* Registers the given handler to this event bus.
*
* @param type the class type of the event
* @param handler the handler to register
* @param <E> the type of the event
*/
<E extends TabEvent> void register(Class<E> type, EventHandler<E> handler);
/**
* Scans the given listener for any method that is annotated with
* {@link Subscribe} and unregisters it from this event bus.
*
* @param listener the listener to unregister
*/
void unregister(Object listener);
/**
* Unregisters the given handler from this event bus.
*
* @param handler the handler to unregister
* @param <E> the type of the event
*/
<E extends TabEvent> void unregister(EventHandler<E> handler);
}
@@ -0,0 +1,18 @@
package me.neznamy.tab.api.event;
/**
* A handler for an event. This allows listening for events in a more
* functional style.
*
* @param <E> the type of event this handler handles
*/
@FunctionalInterface
public interface EventHandler<E> {
/**
* Called when the given event is fired from an event bus.
*
* @param event the event to handle
*/
void handle(E event);
}
@@ -0,0 +1,14 @@
package me.neznamy.tab.api.event;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Subscribes to a {@link TabEvent}.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
}
@@ -0,0 +1,10 @@
package me.neznamy.tab.api.event;
/**
* An event that may be fired by TAB when something occurs.
*
* This is a marker interface. All events <strong>must</strong> implement this
* interface.
*/
public interface TabEvent {
}
@@ -0,0 +1,24 @@
package me.neznamy.tab.api.event.player;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.event.TabEvent;
/**
* Called when the player has been fully loaded by TAB.
*/
public interface PlayerLoadEvent extends TabEvent {
/**
* Gets the player that was loaded.
*
* @return the player that was loaded
*/
TabPlayer getPlayer();
/**
* Returns true if player was loaded because they joined,
* false if the player was loaded because tab reload command was executed
* @return true if player joined, false if plugin was reloaded
*/
boolean isJoin();
}
@@ -0,0 +1,11 @@
package me.neznamy.tab.api.event.plugin;
import me.neznamy.tab.api.event.TabEvent;
import me.neznamy.tab.api.placeholder.Placeholder;
public interface PlaceholderRegisterEvent extends TabEvent {
String getIdentifier();
void setPlaceholder(Placeholder placeholder);
}
@@ -0,0 +1,9 @@
package me.neznamy.tab.api.event.plugin;
import me.neznamy.tab.api.event.TabEvent;
/**
* Called when TAB is fully loaded.
*/
public interface TabLoadEvent extends TabEvent {
}
@@ -41,4 +41,6 @@ public interface PlaceholderManager {
void addUsedPlaceholder(String identifier, TabFeature feature);
String findReplacement(String placeholder, String output);
List<String> getUsedPlaceholders();
}
@@ -152,4 +152,14 @@ public interface ScoreboardManager {
* is < 0.
*/
void announceScoreboard(String scoreboard, int duration);
/**
* Returns player's currently displayed scoreboard. This can be either with
* configuration, overridden with commands or the API. Will return {@code null}
* if player does not see any scoreboard due to not meeting any display condition.
* @param player
* player to get active scoreboard of
* @return player's active scoreboard or {@code null} if player has no scoreboard
*/
Scoreboard getActiveScoreboard(TabPlayer player);
}
@@ -14,6 +14,15 @@ public interface ThreadManager {
* @param task - the task
*/
Future<Void> runMeasuredTask(String errorDescription, TabFeature feature, String type, Runnable task);
/**
* Starts a task in new thread and measures how long it took to process
* @param errorDescription - description to use if this task throws an error
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
*/
Future<Void> runMeasuredTask(String errorDescription, String feature, String type, Runnable task);
/**
* Runs task in a new thread
@@ -39,18 +48,9 @@ public interface ThreadManager {
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
* @return future allowing to cancel the task
*/
Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task);
/**
* Runs task with a delay and measures how long it took to process
* @param delayMilliseconds - how long to run the task after
* @param errorDescription - description to use if this task throws an error
* @param feature - feature to add cpu usage to
* @param type - usage type to add cpu usage to
* @param task - the task
*/
Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, String feature, String type, Runnable task);
Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, Runnable task);
Future<?> runTaskLater(int delayMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task);
Future<?> runTaskLater(int delayMilliseconds, String errorDescription, Runnable task);
}
@@ -6,8 +6,8 @@ public class Preconditions {
if (obj == null) throw new IllegalArgumentException(exceptionMessage);
}
public static void checkRange(Number number, Number min, Number max){
public static void checkRange(Number number, Number min, Number max, String variable){
if (number.doubleValue() < min.doubleValue() || number.doubleValue() > max.doubleValue())
throw new IllegalArgumentException("Number index out of range (" + min + " - " + max + ")");
throw new IllegalArgumentException("Number index out of range (" + min + " - " + max + ") for " + variable);
}
}
@@ -8,5 +8,6 @@ tasks {
archiveFileName.set("TAB-${project.name}-${project.version}.jar")
relocate("org.bstats", "me.neznamy.tab.libs.org.bstats")
relocate("org.json.simple", "me.neznamy.tab.libs.org.json.simple")
relocate("net.kyori.event", "me.neznamy.tab.libs.net.kyori.event")
}
}
@@ -239,7 +239,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
*/
public Object build(PacketPlayOutEntityDestroy packet) throws ReflectiveOperationException {
try {
return nms.newPacketPlayOutEntityDestroy.newInstance(packet.getEntities());
return nms.newPacketPlayOutEntityDestroy.newInstance(new Object[]{packet.getEntities()});
} catch (IllegalArgumentException e) {
//1.17.0
return nms.newPacketPlayOutEntityDestroy.newInstance(packet.getEntities()[0]);
@@ -13,6 +13,7 @@ import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.NickCompatibility;
import me.neznamy.tab.shared.features.PipelineInjector;
import org.jetbrains.annotations.NotNull;
public class BukkitPipelineInjector extends PipelineInjector {
@@ -44,7 +45,7 @@ public class BukkitPipelineInjector extends PipelineInjector {
}
@Override
public void channelRead(ChannelHandlerContext context, Object packet) {
public void channelRead(@NotNull ChannelHandlerContext context, @NotNull Object packet) {
try {
if (TAB.getInstance().getFeatureManager().onPacketReceive(player, packet)) return;
super.channelRead(context, packet);
@@ -120,9 +121,8 @@ public class BukkitPipelineInjector extends PipelineInjector {
return null;
}
private String getName(TabPlayer p) {
return ((NickCompatibility) TAB.getInstance().getFeatureManager().getFeature("nick")).getNickname(p);
return ((NickCompatibility) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.NICK_COMPATIBILITY)).getNickname(p);
}
}
}
@@ -1,6 +1,8 @@
package me.neznamy.tab.platforms.bukkit;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;
@@ -45,6 +47,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
private boolean paperTps;
private boolean paperMspt;
private boolean purpur;
private Method playerIsAfk;
private Listener healthListener = null;
@@ -82,7 +85,7 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
//not paper
}
try {
Player.class.getMethod("isAfk");
playerIsAfk = Player.class.getMethod("isAfk");
purpur = true;
} catch (NoSuchMethodException e) {
//not purpur
@@ -92,11 +95,10 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
@SuppressWarnings("deprecation")
@Override
public void registerPlaceholders(PlaceholderManager manager) {
NumberFormat roundDown = NumberFormat.getInstance();
NumberFormat roundDown = NumberFormat.getNumberInstance(Locale.ENGLISH);
roundDown.setRoundingMode(RoundingMode.DOWN);
roundDown.setMaximumFractionDigits(2);
manager.registerPlayerPlaceholder("%displayname%", 500, p -> ((Player) p.getPlayer()).getDisplayName());
manager.registerPlayerPlaceholder("%vanished%", 1000, TabPlayer::isVanished);
if (paperTps) {
manager.registerServerPlaceholder("%tps%", 1000, () -> formatTPS(Bukkit.getTPS()[0]));
} else if (recentTps != null) {
@@ -115,7 +117,13 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
}
manager.registerPlayerPlaceholder("%afk%", 500, p -> {
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
return purpur && ((Player)p.getPlayer()).isAfk();
if (!purpur || playerIsAfk == null) return false;
try {
return playerIsAfk.invoke(p.getPlayer());
} catch (final IllegalAccessException | InvocationTargetException exception) {
TAB.getInstance().getErrorManager().printError("Failed to invoke isAfk!", exception);
}
return false;
});
manager.registerPlayerPlaceholder("%essentialsnick%", 1000, p -> {
String nickname = null;
@@ -191,11 +199,11 @@ public class BukkitPlaceholderRegistry implements PlaceholderRegistry {
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onRespawn(PlayerRespawnEvent e) {
health.updateValue(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getPlayer().getMaxHealth());
health.updateValue(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()), (int) e.getPlayer().getMaxHealth());
}
};
Bukkit.getPluginManager().registerEvents(healthListener, plugin);
}, () -> HandlerList.unregisterAll(healthListener));
}
}
}
}
@@ -4,6 +4,7 @@ import java.io.File;
import java.util.Collection;
import java.util.List;
import me.neznamy.tab.shared.TabConstants;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@@ -36,6 +37,7 @@ import me.neznamy.tab.shared.permission.UltraPermissions;
import me.neznamy.tab.shared.placeholders.PlayerPlaceholderImpl;
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.plugin.RegisteredServiceProvider;
/**
* Bukkit's implementation of Platform interface
@@ -67,7 +69,9 @@ public class BukkitPlatform implements Platform {
} else if (Bukkit.getPluginManager().isPluginEnabled("UltraPermissions")) {
return new UltraPermissions(getPluginVersion("UltraPermissions"));
} else if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
return new Vault(Bukkit.getServicesManager().getRegistration(Permission.class).getProvider(), getPluginVersion("Vault"));
RegisteredServiceProvider<Permission> provider = Bukkit.getServicesManager().getRegistration(Permission.class);
if (provider == null) return new None();
return new Vault(provider.getProvider(), getPluginVersion("Vault"));
} else {
return new None();
}
@@ -91,20 +95,23 @@ public class BukkitPlatform implements Platform {
libsDisguises = Bukkit.getPluginManager().isPluginEnabled("LibsDisguises");
essentials = Bukkit.getPluginManager().getPlugin("Essentials");
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection()) tab.getFeatureManager().registerFeature("injection", new BukkitPipelineInjector());
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
new UniversalPlaceholderRegistry().registerPlaceholders(tab.getPlaceholderManager());
loadNameTagFeature(tab);
tab.loadUniversalFeatures();
if (tab.getConfiguration().getConfig().getBoolean("bossbar.enabled", false)) {
if (tab.getServerVersion().getMinorVersion() < 9) {
tab.getFeatureManager().registerFeature("bossbar", new WitherBossBar(plugin));
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new WitherBossBar(plugin));
} else {
tab.getFeatureManager().registerFeature("bossbar", new BossBarManagerImpl());
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new BossBarManagerImpl());
}
}
if (tab.getServerVersion().getMinorVersion() >= 9 && tab.getConfiguration().getConfig().getBoolean("fix-pet-names.enabled", false)) tab.getFeatureManager().registerFeature("petfix", new PetFix());
if (tab.getConfiguration().getConfig().getBoolean("per-world-playerlist.enabled", false)) tab.getFeatureManager().registerFeature("pwp", new PerWorldPlayerList(plugin));
if (tab.getServerVersion().getMinorVersion() >= 9 && tab.getConfiguration().getConfig().getBoolean("fix-pet-names.enabled", false))
tab.getFeatureManager().registerFeature(TabConstants.Feature.PET_FIX, new PetFix());
if (tab.getConfiguration().getConfig().getBoolean("per-world-playerlist.enabled", false))
tab.getFeatureManager().registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, new PerWorldPlayerList(plugin));
if (placeholderAPI) {
new TabExpansion(plugin);
}
@@ -114,7 +121,8 @@ public class BukkitPlatform implements Platform {
}
private String getPluginVersion(String plugin) {
return Bukkit.getPluginManager().getPlugin(plugin).getDescription().getVersion();
Plugin pl = Bukkit.getPluginManager().getPlugin(plugin);
return pl == null ? "" : pl.getDescription().getVersion();
}
/**
@@ -124,9 +132,9 @@ public class BukkitPlatform implements Platform {
private void loadNameTagFeature(TAB tab) {
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && tab.getServerVersion().getMinorVersion() >= 8) {
tab.getFeatureManager().registerFeature("nametagx", new NameTagX(plugin));
tab.getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, new NameTagX(plugin));
} else {
tab.getFeatureManager().registerFeature("nametag16", new NameTag());
tab.getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS, new NameTag());
}
}
}
@@ -178,11 +186,11 @@ public class BukkitPlatform implements Platform {
long time = System.nanoTime();
String syncedPlaceholder = identifier.substring(6, identifier.length()-1);
String value = placeholderAPI ? PlaceholderAPI.setPlaceholders((Player) p.getPlayer(), "%" + syncedPlaceholder + "%") : identifier;
getLastValues().put(p.getName(), value);
if (!getForceUpdate().contains(p.getName())) getForceUpdate().add(p.getName());
getLastValues().put(p, value);
getForceUpdate().add(p);
TAB.getInstance().getCPUManager().addPlaceholderTime(getIdentifier(), System.nanoTime()-time);
});
String value = getLastValues().get(p.getName());
String value = getLastValues().get(p);
return value == null ? identifier : value;
}
});
@@ -103,12 +103,10 @@ public class BukkitTabPlayer extends ITabPlayer {
private void handle(PacketPlayOutBoss packet) {
BossBar bar = bossBars.get(packet.getId());
if (bar == null && packet.getOperation() != PacketPlayOutBoss.Action.ADD) return; //no idea how
switch (packet.getOperation()) {
case ADD:
if (packet.getOperation() == PacketPlayOutBoss.Action.ADD) {
if (bossBars.containsKey(packet.getId())) return;
bar = Bukkit.createBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16),
BarColor.valueOf(packet.getColor().name()),
bar = Bukkit.createBossBar(RGBUtils.getInstance().convertToBukkitFormat(packet.getName(), getVersion().getMinorVersion() >= 16 && TAB.getInstance().getServerVersion().getMinorVersion() >= 16),
BarColor.valueOf(packet.getColor().name()),
BarStyle.valueOf(packet.getOverlay().getBukkitName()));
if (packet.isCreateWorldFog()) bar.addFlag(BarFlag.CREATE_FOG);
if (packet.isDarkenScreen()) bar.addFlag(BarFlag.DARKEN_SKY);
@@ -116,7 +114,10 @@ public class BukkitTabPlayer extends ITabPlayer {
bar.setProgress(packet.getPct());
bossBars.put(packet.getId(), bar);
bar.addPlayer(getPlayer());
break;
return;
}
if (bar == null) return; //no idea how
switch (packet.getOperation()) {
case REMOVE:
bar.removePlayer(getPlayer());
bossBars.remove(packet.getId());
@@ -216,7 +217,7 @@ public class BukkitTabPlayer extends ITabPlayer {
try {
if (!((BukkitPlatform)TAB.getInstance().getPlatform()).isLibsDisguisesEnabled()) return false;
return DisguiseAPI.isDisguised(getPlayer());
} catch (NoClassDefFoundError | ExceptionInInitializerError e) {
} catch (LinkageError e) {
//java.lang.NoClassDefFoundError: Could not initialize class me.libraryaddict.disguise.DisguiseAPI
TAB.getInstance().getErrorManager().printError("Failed to check disguise status using LibsDisguises", e);
((BukkitPlatform)TAB.getInstance().getPlatform()).setLibsDisguisesEnabled(false);
@@ -257,4 +258,4 @@ public class BukkitTabPlayer extends ITabPlayer {
public int getGamemode() {
return getPlayer().getGameMode().getValue();
}
}
}
@@ -7,11 +7,9 @@ import java.util.List;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import com.viaversion.viaversion.api.Via;
@@ -46,15 +44,17 @@ public class Main extends JavaPlugin {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Unknown server version: " + Bukkit.getBukkitVersion() + "! Plugin may not work correctly."));
}
Bukkit.getPluginManager().registerEvents(new BukkitEventListener(this), this);
TABCommand command = new TABCommand();
Bukkit.getPluginCommand("tab").setExecutor(command);
Bukkit.getPluginCommand("tab").setTabCompleter(command);
TAB.getInstance().load();
Metrics metrics = new Metrics(this, 5304);
metrics.addCustomChart(new SimplePie("unlimited_nametag_mode_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx") ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("unlimited_nametag_mode_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS) ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("placeholderapi", () -> Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI") ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("permission_system", () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie("server_version", () -> "1." + TAB.getInstance().getServerVersion().getMinorVersion() + ".x"));
PluginCommand cmd = Bukkit.getPluginCommand("tab");
if (cmd == null) return;
TABCommand command = new TABCommand();
cmd.setExecutor(command);
cmd.setTabCompleter(command);
}
@Override
@@ -150,8 +150,9 @@ public class Main extends JavaPlugin {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
} catch (Exception | NoClassDefFoundError e) {
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ViaVersion v%s", player.getName(), Bukkit.getPluginManager().getPlugin("ViaVersion").getDescription().getVersion()), e);
} catch (Exception | LinkageError e) {
Plugin via = Bukkit.getPluginManager().getPlugin("ViaVersion");
TAB.getInstance().getErrorManager().printError(String.format("Failed to get protocol version of %s using ViaVersion v%s", player.getName(), via == null ? "" : via.getDescription().getVersion()), e);
return TAB.getInstance().getServerVersion().getNetworkId();
}
}
@@ -7,7 +7,10 @@ import org.jetbrains.annotations.NotNull;
/**
* Bukkit event that is called each time TAB fully loads. This includes server startup, reload,
* /tab reload and /plugman reload tab
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.plugin.TabLoadEvent}
*/
@Deprecated
public class TabLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@@ -20,4 +23,4 @@ public class TabLoadEvent extends Event {
public static HandlerList getHandlerList() {
return handlers;
}
}
}
@@ -8,7 +8,10 @@ import org.jetbrains.annotations.NotNull;
/**
* Bukkit event that is called when player is successfully loaded after joining. This also includes plugin reloading.
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.player.PlayerLoadEvent}
*/
@Deprecated
public class TabPlayerLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@@ -31,4 +34,4 @@ public class TabPlayerLoadEvent extends Event {
public TabPlayer getPlayer() {
return player;
}
}
}
@@ -1,9 +1,6 @@
package me.neznamy.tab.platforms.bukkit.features;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
@@ -26,7 +23,7 @@ public class PetFix extends TabFeature {
private final int petOwnerPosition = getPetOwnerPosition();
//logger of last interacts to prevent feature not working on 1.16
private final Map<String, Long> lastInteractFix = new HashMap<>();
private final WeakHashMap<TabPlayer, Long> lastInteractFix = new WeakHashMap<>();
/**
* Constructs new instance with given parameter
@@ -67,13 +64,13 @@ public class PetFix extends TabFeature {
@Override
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
if (nms.PacketPlayInUseEntity.isInstance(packet)) {
if (lastInteractFix.containsKey(sender.getName()) && (System.currentTimeMillis() - lastInteractFix.get(sender.getName()) < 5)) {
if (lastInteractFix.containsKey(sender) && (System.currentTimeMillis() - lastInteractFix.get(sender) < 5)) {
//last interact packet was sent right now, cancelling to prevent double-toggle due to this feature enabled
return true;
}
if (isInteract(nms.PacketPlayInUseEntity_ACTION.get(packet))) {
//this is the first packet, saving player so the next packet can be cancelled
lastInteractFix.put(sender.getName(), System.currentTimeMillis());
lastInteractFix.put(sender, System.currentTimeMillis());
}
}
return false;
@@ -124,12 +121,4 @@ public class PetFix extends TabFeature {
}
}
}
/**
* Removing player data to not have a memory leak
*/
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
lastInteractFix.remove(disconnectedPlayer.getName());
}
}
@@ -1,5 +1,6 @@
package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.shared.TabConstants;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
@@ -12,7 +13,6 @@ import me.neznamy.tab.api.bossbar.BossBarManager;
import me.neznamy.tab.api.scoreboard.Scoreboard;
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
import org.jetbrains.annotations.NotNull;
/**
@@ -83,21 +83,21 @@ public class TabExpansion extends PlaceholderExpansion {
}
private boolean hasBossBarVisible(TabPlayer p) {
BossBarManager boss = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature("bossbar");
BossBarManager boss = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.BOSS_BAR);
if (boss == null) return false;
return boss.hasBossBarVisible(p);
}
private boolean hasScoreboardVisible(TabPlayer p) {
ScoreboardManager scoreboard = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
ScoreboardManager scoreboard = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SCOREBOARD);
if (scoreboard == null) return false;
return scoreboard.hasScoreboardVisible(p);
}
private String getActiveScoreboard(TabPlayer p) {
ScoreboardManagerImpl sb = (ScoreboardManagerImpl) TabAPI.getInstance().getScoreboardManager();
ScoreboardManager sb = TabAPI.getInstance().getScoreboardManager();
if (sb == null) return "";
Scoreboard active = sb.getActiveScoreboards().get(p);
Scoreboard active = sb.getActiveScoreboard(p);
if (active == null) return "";
return active.getName();
}
@@ -32,7 +32,7 @@ public class BukkitArmorStand implements ArmorStand {
private static int idCounter = 2000000000;
//NameTag feature
private final NameTagX manager = (NameTagX) TAB.getInstance().getFeatureManager().getFeature("nametagx");
private final NameTagX manager = (NameTagX) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
//owner of the armor stand
private final TabPlayer owner;
@@ -119,7 +119,7 @@ public class BukkitArmorStand implements ArmorStand {
@Override
public void destroy() {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) all.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
for (TabPlayer all : owner.getArmorStandManager().getNearbyPlayers()) all.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
}
@Override
@@ -34,7 +34,7 @@ public class EventListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSneak(PlayerToggleSneakEvent e) {
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
if (p == null || !p.isLoaded() || feature.isPlayerDisabled(p)) return;
if (p == null || feature.isPlayerDisabled(p)) return;
TAB.getInstance().getCPUManager().runMeasuredTask("processing PlayerToggleSneakEvent", feature, TabConstants.CpuUsageCategory.PLAYER_SNEAK, () -> {
if (p.getArmorStandManager() != null) p.getArmorStandManager().sneak(e.isSneaking());
});
@@ -56,12 +56,11 @@ public class EventListener implements Listener {
@EventHandler
public void onWorldChange(PlayerChangedWorldEvent e) {
TabPlayer p = TAB.getInstance().getPlayer(e.getPlayer().getUniqueId());
if (p == null) return;
if (p == null || !p.isLoaded()) return;
long time = System.nanoTime();
String to = e.getPlayer().getWorld().getName();
if (feature.isDisabled(to)) {
if (!feature.getPlayersInDisabledUnlimitedWorlds().contains(p))
feature.getPlayersInDisabledUnlimitedWorlds().add(p);
feature.getPlayersInDisabledUnlimitedWorlds().add(p);
} else {
feature.getPlayersInDisabledUnlimitedWorlds().remove(p);
}
@@ -1,38 +0,0 @@
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import java.util.Collections;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
public class LocationRefresher extends TabFeature {
private final NameTagX feature;
public LocationRefresher(NameTagX feature) {
super(feature.getFeatureName(), "Processing passengers / preview");
this.feature = feature;
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%location0%", 50, p -> {
if (!feature.getVehicleManager().getPlayersInVehicle().containsKey(p) && !p.isPreviewingNametag()) return null;
Location l = ((Player)p.getPlayer()).getLocation();
return l.getX()+l.getY()+l.getZ(); //less cpu usage than literal Location#toString() and then .equals() when comparing last value
});
addUsedPlaceholders(Collections.singletonList("%location0%")); //making sure it's not the same one as in WitherBossBar
}
@Override
public void refresh(TabPlayer p, boolean force) {
if (feature.getVehicleManager().getPlayersInVehicle().containsKey(p)) {
feature.getVehicleManager().processPassengers((Entity) p.getPlayer());
}
if (p.isPreviewingNametag()) {
p.getArmorStandManager().teleport(p);
}
}
}
@@ -1,10 +1,6 @@
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
@@ -42,11 +38,11 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
//bukkit event listener
private final EventListener eventListener = new EventListener(this);
private final List<TabPlayer> playersInDisabledUnlimitedWorlds = new ArrayList<>();
private final Set<TabPlayer> playersInDisabledUnlimitedWorlds = Collections.newSetFromMap(new WeakHashMap<>());
private final String[] disabledUnlimitedWorldsArray = disabledUnlimitedWorlds.toArray(new String[0]);
private final boolean unlimitedWorldWhitelistMode = disabledUnlimitedWorlds.contains("WHITELIST");
private final List<TabPlayer> playersDisabledWithAPI = new ArrayList<>();
private final Set<TabPlayer> playersDisabledWithAPI = Collections.newSetFromMap(new WeakHashMap<>());
private final VehicleRefresher vehicleManager = new VehicleRefresher(this);
@@ -57,9 +53,8 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
public NameTagX(JavaPlugin plugin) {
Collections.reverse(dynamicLines);
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
TAB.getInstance().getFeatureManager().registerFeature("NameTagX-packet", new PacketListener(this));
TAB.getInstance().getFeatureManager().registerFeature("NameTagX-vehicle", vehicleManager);
TAB.getInstance().getFeatureManager().registerFeature("NameTagX-location", new LocationRefresher(this));
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, new PacketListener(this));
TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
TAB.getInstance().debug(String.format("Loaded Unlimited NameTag feature with parameters markerFor18x=%s, disableOnBoats=%s, spaceBetweenLines=%s, disabledUnlimitedWorlds=%s",
markerFor18x, disableOnBoats, spaceBetweenLines, disabledUnlimitedWorlds));
}
@@ -84,10 +79,10 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
}
private void startVisibilityRefreshTask() {
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(500, "refreshing NameTag visibility", this, TabConstants.CpuUsageCategory.REFRESHING_NAMETAG_VISIBILITY, () -> {
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(500, "refreshing NameTag visibility", this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (!p.isLoaded() || isPlayerDisabled(p)) continue;
if (isPlayerDisabled(p)) continue;
p.getArmorStandManager().updateVisibility(false);
}
});
@@ -105,7 +100,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
@Override
public void onJoin(TabPlayer connectedPlayer) {
if (isDisabled(connectedPlayer.getWorld()) && !playersInDisabledUnlimitedWorlds.contains(connectedPlayer))
if (isDisabled(connectedPlayer.getWorld()))
playersInDisabledUnlimitedWorlds.add(connectedPlayer);
super.onJoin(connectedPlayer);
getEntityIdMap().put(((Player) connectedPlayer.getPlayer()).getEntityId(), connectedPlayer);
@@ -135,9 +130,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.getArmorStandManager() != null) all.getArmorStandManager().unregisterPlayer(disconnectedPlayer);
}
getEntityIdMap().remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
playersInDisabledUnlimitedWorlds.remove(disconnectedPlayer);
playersDisabledWithAPI.remove(disconnectedPlayer);
entityIdMap.remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
if (disconnectedPlayer.getArmorStandManager() != null) { //player was not loaded yet
disconnectedPlayer.getArmorStandManager().destroy();
TAB.getInstance().getCPUManager().runTaskLater(500, "processing onQuit", this, TabConstants.CpuUsageCategory.PLAYER_QUIT, () -> disconnectedPlayer.getArmorStandManager().destroy());
@@ -231,16 +224,18 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
* @param p - player to update
*/
@Override
public void updateProperties(TabPlayer p) {
super.updateProperties(p);
p.loadPropertyFromConfig(this, TabConstants.Property.CUSTOMTAGNAME, p.getName());
p.setProperty(this, TabConstants.Property.NAMETAG, p.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue() + p.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue() + p.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
public boolean updateProperties(TabPlayer p) {
boolean changed = super.updateProperties(p);
if (p.loadPropertyFromConfig(this, TabConstants.Property.CUSTOMTAGNAME, p.getName())) changed = true;
if (p.setProperty(this, TabConstants.Property.NAMETAG, p.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue() +
p.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue() + p.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue())) changed = true;
for (String property : dynamicLines) {
if (!property.equals(TabConstants.Property.NAMETAG)) p.loadPropertyFromConfig(this, property);
if (!property.equals(TabConstants.Property.NAMETAG) && p.loadPropertyFromConfig(this, property)) changed = true;
}
for (String property : staticLines.keySet()) {
if (!property.equals(TabConstants.Property.NAMETAG)) p.loadPropertyFromConfig(this, property);
if (!property.equals(TabConstants.Property.NAMETAG) && p.loadPropertyFromConfig(this, property)) changed = true;
}
return changed;
}
@Override
@@ -273,7 +268,7 @@ public class NameTagX extends NameTag implements UnlimitedNametagManager {
return markerFor18x;
}
public List<TabPlayer> getPlayersInDisabledUnlimitedWorlds() {
public Set<TabPlayer> getPlayersInDisabledUnlimitedWorlds() {
return playersInDisabledUnlimitedWorlds;
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import me.neznamy.tab.shared.TabConstants;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
@@ -13,24 +14,50 @@ import me.neznamy.tab.shared.TAB;
public class VehicleRefresher extends TabFeature {
//list of players currently in a vehicle
private final Map<TabPlayer, Entity> playersInVehicle = new ConcurrentHashMap<>();
//map of players currently in a vehicle
private final WeakHashMap<TabPlayer, Entity> playersInVehicle = new WeakHashMap<>();
//map of vehicles carrying players
private final Map<Integer, List<Entity>> vehicles = new ConcurrentHashMap<>();
//list of players currently on boats
private final List<TabPlayer> playersOnBoats = new ArrayList<>();
//set of players currently on boats
private final Set<TabPlayer> playersOnBoats = Collections.newSetFromMap(new WeakHashMap<>());
private final NameTagX feature;
public VehicleRefresher(NameTagX feature) {
super(feature.getFeatureName(), "Refreshing vehicles");
this.feature = feature;
TAB.getInstance().getCPUManager().startRepeatingMeasuredTask(50, "processing player movement",
this, TabConstants.CpuUsageCategory.PROCESSING_PLAYER_MOVEMENT, () -> {
for (TabPlayer inVehicle : playersInVehicle.keySet()) {
inVehicle.getArmorStandManager().teleport();
// feature.getVehicleManager().processPassengers((Entity) inVehicle.getPlayer());
}
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (p.isPreviewingNametag()) {
p.getArmorStandManager().teleport(p);
}
}
});
addUsedPlaceholders(Collections.singletonList("%vehicle%"));
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%vehicle%", 100, p -> ((Player)p.getPlayer()).getVehicle());
TAB.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%vehicle%", 100, p -> ((Player)p.getPlayer()).getVehicle() == null ? "" : ((Player)p.getPlayer()).getVehicle());
}
@Override
public void load() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
Entity vehicle = ((Player)p.getPlayer()).getVehicle();
if (vehicle != null) {
vehicles.put(vehicle.getEntityId(), getPassengers(vehicle));
playersInVehicle.put(p, vehicle);
if (feature.isDisableOnBoats() && vehicle.getType() == EntityType.BOAT) {
playersOnBoats.add(p);
}
}
}
}
@Override
public void refresh(TabPlayer p, boolean force) {
if (feature.isPlayerDisabled(p)) return;
@@ -64,8 +91,6 @@ public class VehicleRefresher extends TabFeature {
@Override
public void onQuit(TabPlayer disconnectedPlayer) {
if (playersInVehicle.containsKey(disconnectedPlayer)) vehicles.remove(playersInVehicle.get(disconnectedPlayer).getEntityId());
playersInVehicle.remove(disconnectedPlayer);
playersOnBoats.remove(disconnectedPlayer);
}
public Map<Integer, List<Entity>> getVehicles() {
@@ -112,8 +137,4 @@ public class VehicleRefresher extends TabFeature {
processPassengers(passenger);
}
}
public Map<TabPlayer, Entity> getPlayersInVehicle() {
return playersInVehicle;
}
}
@@ -38,7 +38,7 @@ public final class NMSStorage {
//another field due to fabric which uses 1.16 package on 1.17 server
private final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]);
//base
private final Class<?> Packet = getNMSClass("net.minecraft.network.protocol.Packet", "Packet");
public final Class<?> EnumChatFormat = getNMSClass("net.minecraft.EnumChatFormat", "EnumChatFormat");
@@ -227,6 +227,7 @@ public final class NMSStorage {
* If any class, field or method fails to load
*/
public NMSStorage() throws ReflectiveOperationException {
ProtocolVersion.UNKNOWN.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
Class<?> NetworkManager = getNMSClass("net.minecraft.network.NetworkManager", "NetworkManager");
if (minorVersion >= 7) {
NETWORK_MANAGER = getFields(PlayerConnection, NetworkManager).get(0);
@@ -460,8 +461,8 @@ public final class NMSStorage {
PacketPlayOutScoreboardTeam_NAME = getFields(PacketPlayOutScoreboardTeam, String.class).get(0);
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
ScoreboardTeam_getPlayerNameSet = getMethods(ScoreboardTeam, Collection.class).get(0);
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a"}, boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[]{"setCanSeeFriendlyInvisibles", "b"}, boolean.class);
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a", "func_96660_a"}, boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[]{"setCanSeeFriendlyInvisibles", "b", "func_98300_b"}, boolean.class);
if (minorVersion >= 8) {
Class<?> EnumNameTagVisibility = getNMSClass("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility", "ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
EnumNameTagVisibility_values = getEnumValues(EnumNameTagVisibility);
@@ -549,7 +550,21 @@ public final class NMSStorage {
//not the first method in array
}
}
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(names) + " with parameters " + Arrays.toString(parameterTypes) + " in class " + clazz.getName());
List<String> list = new ArrayList<>();
for (Method m : clazz.getMethods()) {
if (m.getParameterCount() != parameterTypes.length) continue;
Class<?>[] types = m.getParameterTypes();
boolean valid = true;
for (int i=0; i<types.length; i++) {
if (types[i] != parameterTypes[i]) {
valid = false;
break;
}
}
if (valid) list.add(m.getName());
}
throw new NoSuchMethodException("No method found with possible names " + Arrays.toString(names) + " with parameters " +
Arrays.toString(parameterTypes) + " in class " + clazz.getName() + ". Methods with matching parameters: " + list);
}
private Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException {
+3 -6
View File
@@ -131,7 +131,7 @@ scoreboard:
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- ""
- " "
- "&6Server Info:"
- "* &bTPS&7: %tps%"
- "* &bUptime&7: &f%server_uptime%"
@@ -145,9 +145,9 @@ scoreboard:
- "* &eKills&7: &f%statistic_player_kills%"
- "* &eDeaths&7: &f%statistic_deaths%"
- "* &eHealth&7: &f%health%"
- ""
- " "
- "&6Personal Info:"
- "* &bRank&7: &f%rank%"
- "* &bRank&7: &f%group%"
- "* &bPing&7: &f%ping%&7ms"
- "* &bWorld&7: &f%world%"
- "%animation:MyAnimation1%"
@@ -163,9 +163,6 @@ placeholders:
date-format: "dd.MM.yyyy"
time-format: "[HH:mm:ss / h:mm a]"
time-offset: 0
remove-strings:
- "[]"
- "< >"
placeholder-output-replacements:
"%essentials_vanished%":
+3 -2
View File
@@ -1,10 +1,11 @@
name: @name@
name: TAB
version: @version@
author: NEZNAMY
main: me.neznamy.tab.platforms.bukkit.Main
api-version: 1.13
softdepend: [PlaceholderAPI, Vault, UltraPermissions, LuckPerms, Essentials, LibsDisguises, ProtocolSupport, ViaVersion]
softdepend: [PlaceholderAPI, Vault, UltraPermissions, LuckPerms, Essentials, LibsDisguises, ProtocolSupport, ViaVersion, floodgate]
website: "https://github.com/NEZNAMY/TAB"
description: @description@
commands:
tab:
description: Plugin's main command
@@ -1,34 +1,27 @@
package me.neznamy.tab.platforms.bungeecord;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PluginMessageHandler;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ChatEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.ServerSwitchEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
/**
* The core for BungeeCord forwarding events into all enabled features
*/
public class BungeeEventListener implements Listener {
private final PluginMessageHandler plm;
public BungeeEventListener(PluginMessageHandler plm) {
this.plm = plm;
}
/**
* Disconnect event listener to forward the event to all features
* @param e - disconnect event
*/
@EventHandler(priority = EventPriority.LOWEST)
@EventHandler
public void onQuit(PlayerDisconnectEvent e){
if (TAB.getInstance().isDisabled()) return;
TAB.getInstance().getFeatureManager().onQuit(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()));
TAB.getInstance().getCPUManager().runTask("processing PlayerDisconnectEvent", () ->
TAB.getInstance().getFeatureManager().onQuit(TAB.getInstance().getPlayer(e.getPlayer().getUniqueId())));
}
/**
@@ -36,14 +29,16 @@ public class BungeeEventListener implements Listener {
* @param e
* switch event
*/
@EventHandler(priority = EventPriority.LOW)
@EventHandler
public void onSwitch(ServerSwitchEvent e){
if (TAB.getInstance().isDisabled()) return;
if (TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()) == null) {
TAB.getInstance().getFeatureManager().onJoin(new BungeeTabPlayer(e.getPlayer()));
} else {
TAB.getInstance().getFeatureManager().onServerChange(e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
}
TAB.getInstance().getCPUManager().runTask("processing ServerSwitchEvent", () -> {
if (TAB.getInstance().getPlayer(e.getPlayer().getUniqueId()) == null) {
TAB.getInstance().getFeatureManager().onJoin(new BungeeTabPlayer(e.getPlayer()));
} else {
TAB.getInstance().getFeatureManager().onServerChange(e.getPlayer().getUniqueId(), e.getPlayer().getServer().getInfo().getName());
}
});
}
/**
@@ -62,7 +62,7 @@ public class BungeePipelineInjector extends PipelineInjector {
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
long time = System.nanoTime();
Object modifiedPacket = packet instanceof ByteBuf && byteBufDeserialization ? deserialize((ByteBuf) packet) : packet;
TAB.getInstance().getCPUManager().addTime("Packet deserializing", TabConstants.CpuUsageCategory.BYTEBUF, System.nanoTime()-time);
TAB.getInstance().getCPUManager().addTime("Packet deserializing", TabConstants.CpuUsageCategory.BYTE_BUF, System.nanoTime()-time);
try {
switch(modifiedPacket.getClass().getSimpleName()) {
case "PlayerListItem":
@@ -112,7 +112,7 @@ public class BungeePipelineInjector extends PipelineInjector {
col.remove(getName(p));
}
}
RedisBungeeSupport redis = (RedisBungeeSupport) TAB.getInstance().getFeatureManager().getFeature("redisbungee");
RedisBungeeSupport redis = (RedisBungeeSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
if (redis != null) {
for (RedisPlayer p : redis.getRedisPlayers().values()) {
if (col.contains(p.getName()) && !packet.getName().equals(p.getTeamName())) {
@@ -126,7 +126,7 @@ public class BungeePipelineInjector extends PipelineInjector {
}
private String getName(TabPlayer p) {
NickCompatibility nick = (NickCompatibility) TAB.getInstance().getFeatureManager().getFeature("nick");
NickCompatibility nick = (NickCompatibility) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.NICK_COMPATIBILITY);
if (nick != null) {
return nick.getNickname(p);
}
@@ -3,6 +3,7 @@ package me.neznamy.tab.platforms.bungeecord;
import java.io.File;
import java.util.List;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.protocol.PacketBuilder;
@@ -10,6 +11,7 @@ import me.neznamy.tab.platforms.bungeecord.event.TabLoadEvent;
import me.neznamy.tab.platforms.bungeecord.event.TabPlayerLoadEvent;
import me.neznamy.tab.platforms.bungeecord.redisbungee.RedisBungeeSupport;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.features.PluginMessageHandler;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.PermissionPlugin;
@@ -56,11 +58,16 @@ public class BungeePlatform extends ProxyPlatform {
@Override
public void loadFeatures() {
TAB tab = TAB.getInstance();
if (tab.getConfiguration().isPipelineInjection()) tab.getFeatureManager().registerFeature("injection", new BungeePipelineInjector());
if (tab.getConfiguration().isPipelineInjection())
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BungeePipelineInjector());
new BungeePlaceholderRegistry().registerPlaceholders(tab.getPlaceholderManager());
super.loadFeatures();
if (ProxyServer.getInstance().getPluginManager().getPlugin("RedisBungee") != null) {
tab.getFeatureManager().registerFeature("redisbungee", new RedisBungeeSupport(plugin));
if (RedisBungeeAPI.getRedisBungeeApi() != null) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, new RedisBungeeSupport(plugin));
} else {
TAB.getInstance().getErrorManager().criticalError("RedisBungee plugin was detected, but it returned null API instance. Disabling hook.", null);
}
}
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
tab.addPlayer(new BungeeTabPlayer(p));
@@ -33,14 +33,15 @@ public class BungeePluginMessageHandler extends PluginMessageHandler implements
*/
@EventHandler
public void on(PluginMessageEvent event){
if (!event.getTag().equalsIgnoreCase(channelName)) return;
if (!event.getTag().equals(channelName)) return;
if (event.getReceiver() instanceof ProxiedPlayer) {
long time = System.nanoTime();
BungeeTabPlayer receiver = (BungeeTabPlayer) TAB.getInstance().getPlayer(((ProxiedPlayer) event.getReceiver()).getUniqueId());
if (receiver == null) return;
onPluginMessage(receiver, ByteStreams.newDataInput(event.getData()));
event.setCancelled(true);
TAB.getInstance().getCPUManager().addTime("Plugin message handling", TabConstants.CpuUsageCategory.PLUGIN_MESSAGE, System.nanoTime()-time);
TAB.getInstance().getCPUManager().runMeasuredTask("processing PluginMessageEvent", "Plugin message handling",
TabConstants.CpuUsageCategory.PLUGIN_MESSAGE, () -> {
BungeeTabPlayer receiver = (BungeeTabPlayer) TAB.getInstance().getPlayer(((ProxiedPlayer) event.getReceiver()).getUniqueId());
if (receiver == null) return;
onPluginMessage(receiver, ByteStreams.newDataInput(event.getData()));
});
}
}
@@ -48,6 +49,6 @@ public class BungeePluginMessageHandler extends PluginMessageHandler implements
public void sendPluginMessage(TabPlayer player, byte[] message) {
if (((ProxiedPlayer) player.getPlayer()).getServer() == null) return; //not connected to any server yet
((ProxiedPlayer) player.getPlayer()).getServer().sendData(channelName, message);
TAB.getInstance().getCPUManager().packetSent("Plugin Message");
TAB.getInstance().getCPUManager().packetSent("Plugin Message (" + new String(message) + ")");
}
}
@@ -6,7 +6,6 @@ import java.lang.reflect.Method;
import de.myzelyam.api.vanish.BungeeVanishAPI;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PluginMessageHandler;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.api.ProxyServer;
@@ -32,12 +32,12 @@ public class Main extends Plugin {
}
PluginMessageHandler plm = new BungeePluginMessageHandler(this);
TAB.setInstance(new TAB(new BungeePlatform(this, plm), ProtocolVersion.PROXY));
getProxy().getPluginManager().registerListener(this, new BungeeEventListener(plm));
getProxy().getPluginManager().registerListener(this, new BungeeEventListener());
getProxy().getPluginManager().registerCommand(this, new BTABCommand());
TAB.getInstance().load();
Metrics metrics = new Metrics(this, 10535);
metrics.addCustomChart(new SimplePie("permission_system", () -> TAB.getInstance().getGroupManager().getPlugin().getName()));
metrics.addCustomChart(new SimplePie("global_playerlist_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled("globalplayerlist") ? "Yes" : "No"));
metrics.addCustomChart(new SimplePie("global_playerlist_enabled", () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.GLOBAL_PLAYER_LIST) ? "Yes" : "No"));
}
/**
@@ -5,7 +5,10 @@ import net.md_5.bungee.api.plugin.Event;
/**
* Bungee event that is called each time TAB fully loads. This includes server startup, reload and
* /btab reload
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.plugin.TabLoadEvent}
*/
@Deprecated
public class TabLoadEvent extends Event {
}
}
@@ -5,7 +5,10 @@ import net.md_5.bungee.api.plugin.Event;
/**
* BungeeCord event that is called when player is successfully loaded after joining. This also includes plugin reloading.
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.player.PlayerLoadEvent}
*/
@Deprecated
public class TabPlayerLoadEvent extends Event {
private final TabPlayer player;
@@ -17,4 +20,4 @@ public class TabPlayerLoadEvent extends Event {
public TabPlayer getPlayer() {
return player;
}
}
}
@@ -39,20 +39,15 @@ public class RedisBungeeSupport extends TabFeature implements RedisSupport, List
private static final String CHANNEL_NAME = "TAB";
private final Map<String, RedisPlayer> redisPlayers = new ConcurrentHashMap<>();
private final GlobalPlayerList global = (GlobalPlayerList) TAB.getInstance().getFeatureManager().getFeature("globalplayerlist");
private final PlayerList playerlist = (PlayerList) TAB.getInstance().getFeatureManager().getFeature("playerlist");
private final NameTag nametags = (NameTag) TAB.getInstance().getFeatureManager().getFeature("nametag16");
private final GlobalPlayerList global = (GlobalPlayerList) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST);
private final PlayerList playerlist = (PlayerList) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.PLAYER_LIST);
private final NameTag nametags = (NameTag) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.NAME_TAGS);
private final UUID proxy = UUID.randomUUID();
public RedisBungeeSupport(Plugin plugin) {
super("RedisBungee", null);
RedisBungeeAPI api = RedisBungeeAPI.getRedisBungeeApi();
if (api == null) {
TAB.getInstance().getErrorManager().criticalError("RedisBungee plugin was detected, but it returned null API instance. Disabling hook.", null);
return;
}
ProxyServer.getInstance().getPluginManager().registerListener(plugin, this);
api.registerPubSubChannels(CHANNEL_NAME);
RedisBungeeAPI.getRedisBungeeApi().registerPubSubChannels(CHANNEL_NAME);
overridePlaceholders();
}
@@ -107,120 +102,124 @@ public class RedisBungeeSupport extends TabFeature implements RedisSupport, List
@EventHandler
public void onMessage(PubSubMessageEvent e) {
if (!e.getChannel().equals(CHANNEL_NAME)) return;
JSONObject message;
try {
message = (JSONObject) new JSONParser().parse(e.getMessage());
} catch (ParseException ex) {
TAB.getInstance().getErrorManager().printError("Failed to parse json message \"" + e.getMessage() + "\"", ex);
return;
}
if (message.get("proxy").equals(proxy.toString())) return; //message coming from current proxy
String action = (String) message.get("action");
UUID id = UUID.fromString((String) message.getOrDefault("UUID", proxy.toString()));
RedisPlayer target;
switch(action) {
case "loadrequest":
JSONObject json = new JSONObject();
json.put("proxy", proxy.toString());
json.put("action", "load");
List<JSONObject> players = new ArrayList<>();
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
players.add(RedisPlayer.toJson(this, all));
TAB.getInstance().getCPUManager().runMeasuredTask("processing PubSubMessageEvent", this, TabConstants.CpuUsageCategory.REDIS_BUNGEE_MESSAGE, () -> {
JSONObject message;
try {
message = (JSONObject) new JSONParser().parse(e.getMessage());
} catch (ParseException ex) {
TAB.getInstance().getErrorManager().printError("Failed to parse json message \"" + e.getMessage() + "\"", ex);
return;
}
json.put("players", players);
RedisBungeeAPI.getRedisBungeeApi().sendChannelMessage(CHANNEL_NAME, json.toString());
break;
case "load":
List<JSONObject> players1 = (List<JSONObject>) message.get("players");
for (JSONObject obj : players1) {
RedisPlayer p = RedisPlayer.fromJson(this, obj);
if (!redisPlayers.containsKey(p.getUniqueId().toString())) {
redisPlayers.put(p.getUniqueId().toString(), p);
join(p);
}
if (message.get("proxy").equals(proxy.toString())) return; //message coming from current proxy
String action = (String) message.get("action");
UUID id = UUID.fromString((String) message.getOrDefault("UUID", proxy.toString()));
RedisPlayer target;
switch(action) {
case "loadrequest":
JSONObject json = new JSONObject();
json.put("proxy", proxy.toString());
json.put("action", "load");
List<JSONObject> players = new ArrayList<>();
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
players.add(RedisPlayer.toJson(this, all));
}
json.put("players", players);
RedisBungeeAPI.getRedisBungeeApi().sendChannelMessage(CHANNEL_NAME, json.toString());
break;
case "load":
List<JSONObject> players1 = (List<JSONObject>) message.get("players");
for (JSONObject obj : players1) {
RedisPlayer p = RedisPlayer.fromJson(this, obj);
if (!redisPlayers.containsKey(p.getUniqueId().toString())) {
redisPlayers.put(p.getUniqueId().toString(), p);
join(p);
}
}
break;
case "join":
target = RedisPlayer.fromJson(this, message);
redisPlayers.put(id.toString(), target);
join(target);
break;
case "server":
target = redisPlayers.get(id.toString());
if (target == null) break;
String server = (String) message.get("server");
if (global == null) {
target.setServer(server);
return;
}
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
boolean before = shouldSee(viewer, target.getServer(), target.isVanished());
boolean after = shouldSee(viewer, server, target.isVanished());
if (!before && after) {
viewer.sendCustomPacket(target.getAddPacket(), this);
}
if (before && !after) {
viewer.sendCustomPacket(target.getRemovePacket(), this);
}
}
target.setServer(server);
break;
case "tabformat":
target = redisPlayers.get(id.toString());
if (target == null) break;
target.setTabFormat((String) message.get("tabformat"));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() >= 8) viewer.sendCustomPacket(target.getUpdatePacket(), this);
}
break;
case "nametag":
target = redisPlayers.get(id.toString());
if (target == null) break;
target.setTagPrefix((String) message.get(TabConstants.Property.TAGPREFIX));
target.setTagSuffix((String) message.get(TabConstants.Property.TAGSUFFIX));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(target.getUpdateTeamPacket(), this);
}
break;
case "belowname":
target = redisPlayers.get(id.toString());
if (target == null) break;
target.setBelowName((String) message.get("belowname"));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(target.getBelowNameUpdatePacket(), this);
}
break;
case "yellow-number":
target = redisPlayers.get(id.toString());
if (target == null) break;
target.setYellowNumber((String) message.get("yellow-number"));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(target.getYellowNumberUpdatePacket(), this);
}
break;
case "team":
target = redisPlayers.get(id.toString());
if (target == null) break;
PacketPlayOutScoreboardTeam unregister = target.getUnregisterTeamPacket();
target.setTeamName((String) message.get("to"));
PacketPlayOutScoreboardTeam register = target.getRegisterTeamPacket();
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(unregister, this);
viewer.sendCustomPacket(register, this);
}
break;
case "quit":
target = redisPlayers.get(id.toString());
if (target == null) break; //player left current proxy and was unloaded from memory, therefore null check didn't pass
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
all.sendCustomPacket(target.getUnregisterTeamPacket(), this);
if (all.getVersion().getMinorVersion() < 8) continue;
all.sendCustomPacket(target.getRemovePacket(), this);
}
redisPlayers.remove(id.toString());
break;
default:
break;
}
break;
case "join":
target = RedisPlayer.fromJson(this, message);
redisPlayers.put(id.toString(), target);
join(target);
break;
case "server":
target = redisPlayers.get(id.toString());
if (target == null) break;
String server = (String) message.get("server");
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() < 8) continue;
boolean before = shouldSee(viewer, target.getServer(), target.isVanished());
boolean after = shouldSee(viewer, server, target.isVanished());
if (!before && after) {
viewer.sendCustomPacket(target.getAddPacket(), this);
}
if (before && !after) {
viewer.sendCustomPacket(target.getRemovePacket(), this);
}
}
target.setServer(server);
break;
case "tabformat":
target = redisPlayers.get(id.toString());
if (target == null) break;
target.setTabFormat((String) message.get("tabformat"));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
if (viewer.getVersion().getMinorVersion() >= 8) viewer.sendCustomPacket(target.getUpdatePacket(), this);
}
break;
case "nametag":
target = redisPlayers.get(id.toString());
if (target == null) break;
target.setTagPrefix((String) message.get(TabConstants.Property.TAGPREFIX));
target.setTagSuffix((String) message.get(TabConstants.Property.TAGSUFFIX));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(target.getUpdateTeamPacket(), this);
}
break;
case "belowname":
target = redisPlayers.get(id.toString());
if (target == null) break;
target.setBelowName((String) message.get("belowname"));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(target.getBelowNameUpdatePacket(), this);
}
break;
case "yellow-number":
target = redisPlayers.get(id.toString());
if (target == null) break;
target.setYellowNumber((String) message.get("yellow-number"));
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(target.getYellowNumberUpdatePacket(), this);
}
break;
case "team":
target = redisPlayers.get(id.toString());
if (target == null) break;
PacketPlayOutScoreboardTeam unregister = target.getUnregisterTeamPacket();
target.setTeamName((String) message.get("to"));
PacketPlayOutScoreboardTeam register = target.getRegisterTeamPacket();
for (TabPlayer viewer : TAB.getInstance().getOnlinePlayers()) {
viewer.sendCustomPacket(unregister, this);
viewer.sendCustomPacket(register, this);
}
break;
case "quit":
target = redisPlayers.get(id.toString());
if (target == null) break; //player left current proxy and was unloaded from memory, therefore null check didn't pass
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.getVersion().getMinorVersion() < 8) continue;
all.sendCustomPacket(target.getUnregisterTeamPacket(), this);
if (shouldSee(all, target.getServer(), target.isVanished()) && !all.getServer().equals(target.getServer())) {
all.sendCustomPacket(target.getRemovePacket(), this);
}
}
redisPlayers.remove(id.toString());
break;
default:
break;
}
});
}
private void join(RedisPlayer target) {
@@ -229,6 +228,10 @@ public class RedisBungeeSupport extends TabFeature implements RedisSupport, List
all.sendCustomPacket(target.getBelowNameUpdatePacket(), this);
all.sendCustomPacket(target.getYellowNumberUpdatePacket(), this);
if (all.getVersion().getMinorVersion() < 8) continue;
if (global == null) {
if (all.getServer().equals(target.getServer())) all.sendCustomPacket(target.getUpdatePacket(), this);
continue;
}
if (shouldSee(all, target.getServer(), target.isVanished())) {
if (!all.getServer().equals(target.getServer())) {
all.sendCustomPacket(target.getAddPacket(), this);
@@ -245,12 +248,8 @@ public class RedisBungeeSupport extends TabFeature implements RedisSupport, List
private boolean shouldSee(TabPlayer viewer, String viewerServer, String server, boolean targetVanished) {
if (targetVanished && !viewer.hasPermission(TabConstants.Permission.GLOBAL_PLAYERLIST_SEE_VANISHED)) return false;
if (global != null) {
if (global.getSpyServers().contains(viewerServer)) return true;
return global.getServerGroup(viewerServer).equals(global.getServerGroup(server));
} else {
return viewerServer.equals(server);
}
if (global.getSpyServers().contains(viewerServer)) return true;
return global.getServerGroup(viewerServer).equals(global.getServerGroup(server));
}
@Override
@@ -262,6 +261,7 @@ public class RedisBungeeSupport extends TabFeature implements RedisSupport, List
p.sendCustomPacket(redis.getRegisterTeamPacket(), this);
p.sendCustomPacket(redis.getBelowNameUpdatePacket(), this);
p.sendCustomPacket(redis.getYellowNumberUpdatePacket(), this);
if (global == null) continue;
if (shouldSee(p, redis.getServer(), redis.isVanished())) {
if (!p.getServer().equals(redis.getServer())) {
p.sendCustomPacket(redis.getAddPacket(), this);
@@ -280,7 +280,7 @@ public class RedisBungeeSupport extends TabFeature implements RedisSupport, List
json.put("UUID", p.getTablistUUID().toString());
json.put("server", to);
RedisBungeeAPI.getRedisBungeeApi().sendChannelMessage(CHANNEL_NAME, json.toString());
if (p.getVersion().getMinorVersion() < 8) return;
if (p.getVersion().getMinorVersion() < 8 || global == null) return;
for (RedisPlayer redis : redisPlayers.values()) {
boolean before = shouldSee(p, from, redis.getServer(), redis.isVanished());
boolean after = shouldSee(p, to, redis.getServer(), redis.isVanished());
@@ -355,7 +355,7 @@ public class RedisBungeeSupport extends TabFeature implements RedisSupport, List
@Override
public void onPlayerInfo(TabPlayer receiver, PacketPlayOutPlayerInfo info) {
if (info.getAction() == EnumPlayerInfoAction.REMOVE_PLAYER) {
if (info.getAction() == EnumPlayerInfoAction.REMOVE_PLAYER && global != null) {
for (PlayerInfoData playerInfoData : info.getEntries()) {
RedisPlayer packetPlayer = redisPlayers.get(playerInfoData.getUniqueId().toString());
if (packetPlayer != null && (playerInfoData.getName() == null || playerInfoData.getName().length() == 0) && !packetPlayer.isVanished()) {
@@ -85,7 +85,7 @@ public class RedisPlayer {
if (p.getProperty(TabConstants.Property.TAGPREFIX) != null) {
json.put(TabConstants.Property.TAGPREFIX, p.getProperty(TabConstants.Property.TAGPREFIX).get());
json.put(TabConstants.Property.TAGSUFFIX, p.getProperty(TabConstants.Property.TAGSUFFIX).get());
json.put("namevisibility", ((NameTag)TAB.getInstance().getFeatureManager().getFeature("nametag16")).getTeamVisibility(p, p));
json.put("namevisibility", ((NameTag)TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.NAME_TAGS)).getTeamVisibility(p, p));
} else {
json.put(TabConstants.Property.TAGPREFIX, "");
json.put(TabConstants.Property.TAGSUFFIX, "");
@@ -140,12 +140,12 @@ public class RedisPlayer {
public PacketPlayOutScoreboardScore getBelowNameUpdatePacket() {
if (belowName == null) return null;
return new PacketPlayOutScoreboardScore(Action.CHANGE, BelowName.OBJECTIVE_NAME, name, TAB.getInstance().getErrorManager().parseInteger(belowName, 0, "belowname number"));
return new PacketPlayOutScoreboardScore(Action.CHANGE, BelowName.OBJECTIVE_NAME, name, TAB.getInstance().getErrorManager().parseInteger(belowName, 0));
}
public PacketPlayOutScoreboardScore getYellowNumberUpdatePacket() {
if (yellowNumber == null) return null;
return new PacketPlayOutScoreboardScore(Action.CHANGE, YellowNumber.OBJECTIVE_NAME, name, TAB.getInstance().getErrorManager().parseInteger(yellowNumber, 0, "yellow number"));
return new PacketPlayOutScoreboardScore(Action.CHANGE, YellowNumber.OBJECTIVE_NAME, name, TAB.getInstance().getErrorManager().parseInteger(yellowNumber, 0));
}
public String getServer() {
+1 -1
View File
@@ -1,4 +1,4 @@
name: @name@
name: TAB
main: me.neznamy.tab.platforms.bungeecord.Main
version: @version@
author: NEZNAMY
@@ -16,7 +16,7 @@ header-footer:
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- "%animation:ping-tps%"
- "&2Ping: %ping%"
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- ""
- "&7Visit our webpage %animation:web%"
@@ -132,7 +132,7 @@ scoreboard:
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- ""
- " "
- "&6Server Info:"
- "* &bTPS&7: %tps%"
- "* &bUptime&7: &f%server_uptime%"
@@ -146,9 +146,9 @@ scoreboard:
- "* &eKills&7: &f%statistic_player_kills%"
- "* &eDeaths&7: &f%statistic_deaths%"
- "* &eHealth&7: &f%health%"
- ""
- " "
- "&6Personal Info:"
- "* &bRank&7: &f%rank%"
- "* &bRank&7: &f%group%"
- "* &bPing&7: &f%ping%&7ms"
- "* &bWorld&7: &f%world%"
- "%animation:MyAnimation1%"
@@ -161,9 +161,6 @@ placeholders:
date-format: "dd.MM.yyyy"
time-format: "[HH:mm:ss / h:mm a]"
time-offset: 0
remove-strings:
- "[]"
- "< >"
placeholder-output-replacements:
"%essentials_vanished%":
+1 -1
View File
@@ -1,5 +1,5 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=3.0.1
projectVersion=3.0.2
# Gradle properties
org.gradle.daemon=true
+9 -7
View File
@@ -3,22 +3,23 @@ metadata.format.version = "1.1"
[versions]
# API
netty = "4.1.63.Final"
netty = "4.1.72.Final"
jsonSimple = "1.1.1"
snakeyaml = "1.28"
snakeyaml = "1.30"
yamlAssist = "1.0.5"
gson = "2.8.7"
gson = "2.8.9"
# General
luckperms = "5.2"
guava = "30.1.1-jre"
floodgate = "2.0-SNAPSHOT"
kotlin = "1.6.0"
event = "3.0.0"
# Platforms
bukkit = "1.16.5-R0.1-SNAPSHOT"
bungeecord = "1.18-R0.1-SNAPSHOT"
krypton = "0.58"
krypton = "0.59.2"
velocity = "1.1.0"
# Platform extras
@@ -27,8 +28,8 @@ vault = "1.7"
via = "4.0.0"
authlib = "1.11"
bstats = "2.2.1"
libsDisguises = "10.0.26-SNAPSHOT"
essentials = "2.19.0"
libsDisguises = "10.0.26"
essentials = "2.19.2"
premiumVanish = "2.0.3"
redisBungee = "0.6.5-SNAPSHOT"
@@ -46,9 +47,10 @@ luckperms = { group = "net.luckperms", name = "api", version.ref = "luckperms" }
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
floodgate = { group = "org.geysermc.floodgate", name = "api", version.ref = "floodgate" }
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
event = { group = "net.kyori", name = "event-method", version.ref = "event" }
# Platforms
bukkit = { group = "net.pl3x.purpur", name = "purpur-api", version.ref = "bukkit" }
bukkit = { group = "com.destroystokyo.paper", name = "paper-api", version.ref = "bukkit" }
bungeecord = { group = "net.md-5", name = "bungeecord-proxy", version.ref = "bungeecord" }
krypton-api = { group = "org.kryptonmc", name = "krypton-api", version.ref = "krypton" }
krypton-server = { group = "org.kryptonmc", name = "krypton-server", version.ref = "krypton" }
@@ -17,7 +17,6 @@ class KryptonPlaceholderRegistry(private val plugin: Main) : PlaceholderRegistry
override fun registerPlaceholders(manager: PlaceholderManager) {
// Built-in stuff
manager.registerPlayerPlaceholder("%displayname%", 500) { (it.player as Player).displayName }
manager.registerPlayerPlaceholder("%vanished%", 1000, TabPlayer::isVanished)
manager.registerPlayerPlaceholder("%health%", 100) { ceil((it.player as Player).health) }
// TPS
@@ -2,12 +2,11 @@ package me.neznamy.tab.platforms.krypton
import me.neznamy.tab.api.TabPlayer
import me.neznamy.tab.api.protocol.PacketBuilder
import me.neznamy.tab.platforms.krypton.event.TabLoadEvent
import me.neznamy.tab.platforms.krypton.event.TabPlayerLoadEvent
import me.neznamy.tab.platforms.krypton.features.PerWorldPlayerList
import me.neznamy.tab.platforms.krypton.features.unlimitedtags.NameTagX
import me.neznamy.tab.shared.Platform
import me.neznamy.tab.shared.TAB
import me.neznamy.tab.shared.TabConstants
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl
import me.neznamy.tab.shared.features.nametags.NameTag
import me.neznamy.tab.shared.permission.LuckPerms
@@ -33,7 +32,7 @@ class KryptonPlatform(
override fun loadFeatures() {
if (TAB.getInstance().configuration.isPipelineInjection) {
TAB.getInstance().featureManager.registerFeature("injection", KryptonPipelineInjector())
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.PIPELINE_INJECTION, KryptonPipelineInjector())
}
// Placeholders
@@ -44,10 +43,10 @@ class KryptonPlatform(
loadNametagFeature()
TAB.getInstance().loadUniversalFeatures()
if (TAB.getInstance().config.getBoolean("bossbar.enabled", false)) {
TAB.getInstance().featureManager.registerFeature("bossbar", BossBarManagerImpl())
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.BOSS_BAR, BossBarManagerImpl())
}
if (TAB.getInstance().config.getBoolean("per-world-playerlist.enabled", false)) {
TAB.getInstance().featureManager.registerFeature("pwp", PerWorldPlayerList(plugin))
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, PerWorldPlayerList(plugin))
}
server.players.forEach { TAB.getInstance().addPlayer(KryptonTabPlayer(it, plugin.protocolVersion(it))) }
}
@@ -82,11 +81,11 @@ class KryptonPlatform(
override fun getDataFolder(): File = folder
override fun callLoadEvent() {
server.eventManager.fireAndForget(TabLoadEvent)
// do nothing here, this will be removed
}
override fun callLoadEvent(player: TabPlayer) {
server.eventManager.fireAndForget(TabPlayerLoadEvent(player))
// do nothing here, this will be removed
}
override fun getMaxPlayers(): Int = server.maxPlayers
@@ -104,9 +103,9 @@ class KryptonPlatform(
private fun loadNametagFeature() {
if (!TAB.getInstance().config.getBoolean("scoreboard-teams.enabled", true)) return
if (TAB.getInstance().config.getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false)) {
TAB.getInstance().featureManager.registerFeature("nametagx", NameTagX(plugin))
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, NameTagX(plugin))
} else {
TAB.getInstance().featureManager.registerFeature("nametag16", NameTag())
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.NAME_TAGS, NameTag())
}
}
}
@@ -1,10 +0,0 @@
package me.neznamy.tab.platforms.krypton.event
/**
* Krypton event that is called each time TAB fully loads. This includes server
* startup and /tab reload
*/
object TabLoadEvent {
override fun toString(): String = "TabLoadEvent"
}
@@ -1,11 +0,0 @@
package me.neznamy.tab.platforms.krypton.event
import me.neznamy.tab.api.TabPlayer
/**
* Krypton event that is called when player is successfully loaded after
* joining. This also includes plugin reloading.
*/
// Thanks kapt...
//@JvmRecord
data class TabPlayerLoadEvent(val player: TabPlayer)
@@ -21,7 +21,6 @@ import org.kryptonmc.krypton.packet.out.play.PacketOutMetadata
import org.kryptonmc.krypton.packet.out.play.PacketOutSpawnLivingEntity
import org.spongepowered.math.vector.Vector2f
import org.spongepowered.math.vector.Vector3d
import java.util.Optional
import java.util.UUID
import java.util.concurrent.atomic.AtomicInteger
@@ -32,7 +31,7 @@ class KryptonArmorStand(
private val staticOffset: Boolean
) : ArmorStand {
private val manager = TAB.getInstance().featureManager.getFeature("nametagx") as NameTagX
private val manager = TAB.getInstance().featureManager.getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS) as NameTagX
private val player = owner.player as Player
private val entityId = ID_COUNTER.incrementAndGet()
private val uuid = UUID.randomUUID()
@@ -171,7 +170,7 @@ class KryptonArmorStand(
var flags = 32 // invisible
if (sneaking) flags += 2
holder[MetadataKeys.FLAGS] = flags.toByte()
holder[MetadataKeys.CUSTOM_NAME] = Optional.of(KryptonPacketBuilder.toComponent(displayName, viewer.version))
holder[MetadataKeys.CUSTOM_NAME] = KryptonPacketBuilder.toComponent(displayName, viewer.version)
if (isNameVisibilityEmpty(displayName) || !viewerPlayer.canSee(player) || manager.hasHiddenNametag(owner, viewer)) {
holder[MetadataKeys.CUSTOM_NAME_VISIBILITY] = false
@@ -54,9 +54,9 @@ class NameTagX(plugin: Main) : NameTag(), UnlimitedNametagManager {
init {
dynamicLines.reverse()
plugin.server.eventManager.register(plugin, eventListener)
TAB.getInstance().featureManager.registerFeature("nametagx-packet", PacketListener(this))
TAB.getInstance().featureManager.registerFeature("nametagx-vehicle", vehicleManager)
TAB.getInstance().featureManager.registerFeature("nametagx-location", LocationRefresher(this))
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, PacketListener(this))
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager)
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_LOCATION_REFRESHER, LocationRefresher(this))
TAB.getInstance().debug("Loaded Unlimited nametag featured with parameters markerFor18x=$markerFor18x, disableOnBoats=$disableOnBoats, " +
"spaceBetweenLines=$spaceBetweenLines, disabledUnlimitedWorlds=$disabledUnlimitedWorlds")
}
@@ -136,12 +136,13 @@ class NameTagX(plugin: Main) : NameTag(), UnlimitedNametagManager {
}
}
override fun updateProperties(player: TabPlayer) {
override fun updateProperties(player: TabPlayer): Boolean {
super.updateProperties(player)
player.loadPropertyFromConfig(this, TabConstants.Property.CUSTOMTAGNAME, player.name)
rebuildNametagLine(player)
dynamicLines.forEach { if (it != TabConstants.Property.NAMETAG) player.loadPropertyFromConfig(this, it) }
staticLines.keys.forEach { if (it != TabConstants.Property.NAMETAG) player.loadPropertyFromConfig(this, it) }
return true
}
override fun getFeatureName(): String = "Unlimited Nametags"
@@ -248,7 +249,7 @@ class NameTagX(plugin: Main) : NameTag(), UnlimitedNametagManager {
500,
"refreshing nametag visibility",
this,
TabConstants.CpuUsageCategory.REFRESHING_NAMETAG_VISIBILITY
TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY
) {
TAB.getInstance().onlinePlayers.forEach {
if (!it.isLoaded || isPlayerDisabled(it)) return@forEach
@@ -182,9 +182,6 @@ placeholders:
date-format: "dd.MM.yyyy"
time-format: "[HH:mm:ss / h:mm a]"
time-offset: 0
remove-strings:
- "[]"
- "< >"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements-(premium-only)
placeholder-output-replacements:
+2 -1
View File
@@ -5,10 +5,10 @@ dependencyResolutionManagement {
repositories {
mavenCentral()
maven("https://jitpack.io")
maven("https://repo.pl3x.net/")
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/")
maven("https://repo.viaversion.com/")
maven("https://repo.aikar.co/content/groups/aikar/")
maven("https://repo.md-5.net/content/groups/public/")
maven("https://ci.lucko.me/plugin/repository/everything/")
maven("https://repo.essentialsx.net/releases/")
maven("https://repo.codemc.org/repository/maven-public")
@@ -17,6 +17,7 @@ dependencyResolutionManagement {
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://nexus.velocitypowered.com/repository/maven-public/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://papermc.io/repo/repository/maven-public/")
}
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
}
+4
View File
@@ -4,6 +4,10 @@ plugins {
dependencies {
api(projects.api)
api(libs.event) {
exclude("com.google.guava", "guava")
exclude("org.checkerframework", "checker-qual")
}
compileOnlyApi(libs.luckperms)
compileOnlyApi(libs.guava)
compileOnlyApi(libs.floodgate)
@@ -6,7 +6,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -42,8 +41,12 @@ public class CpuManager implements ThreadManager {
//packets sent in the previous 10 seconds
private Map<String, AtomicInteger> packetsPrevious = new ConcurrentHashMap<>();
//thread pool
private ThreadPoolExecutor exe = (ThreadPoolExecutor) Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("TAB - Thread %d").build());
//thread pools
private ExecutorService thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private final Map<Runnable, String> taskQueue = new HashMap<>();
private boolean enabled = false;
//error manager
private final ErrorManager errorManager;
@@ -76,26 +79,30 @@ public class CpuManager implements ThreadManager {
TAB.getInstance().getPlaceholderManager().addUsedPlaceholders(Collections.singletonList("%cpu%"));
}
/**
* Returns amount of active and total threads
* @return active and total threads from this thread pool
*/
public String getThreadCount() {
return exe.getActiveCount() + "/" + exe.getPoolSize();
}
/**
* Cancels all tasks, new instance is set to avoid errors when starting tasks on shutdown (such as packet readers)
*/
public void cancelAllTasks() {
//preventing errors when tasks are inserted while shutting down
ExecutorService old = exe;
exe = (ThreadPoolExecutor) Executors.newCachedThreadPool();
ExecutorService old = thread;
thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
old.shutdownNow();
threadPool.shutdownNow();
}
public void enable() {
enabled = true;
taskQueue.forEach((r, e) -> submit(e, r));
taskQueue.clear();
}
@Override
public Future<Void> runMeasuredTask(String errorDescription, TabFeature feature, String type, Runnable task) {
return runMeasuredTask(errorDescription, feature.getFeatureName(), type, task);
}
@Override
public Future<Void> runMeasuredTask(String errorDescription, String feature, String type, Runnable task) {
return submit(errorDescription, () -> {
long time = System.nanoTime();
task.run();
@@ -110,34 +117,27 @@ public class CpuManager implements ThreadManager {
@Override
public RepeatingTask startRepeatingMeasuredTask(int intervalMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task) {
return new TabRepeatingTask(exe, task, errorDescription, feature, type, intervalMilliseconds);
return new TabRepeatingTask(threadPool, task, errorDescription, feature, type, intervalMilliseconds);
}
@Override
public Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task) {
return runTaskLater(delayMilliseconds, errorDescription, feature.getFeatureName(), type, task);
}
@Override
public Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, String feature, String type, Runnable task) {
return submit(errorDescription, () -> {
public Future<?> runTaskLater(int delayMilliseconds, String errorDescription, TabFeature feature, String type, Runnable task) {
return threadPool.submit(() -> {
try {
Thread.sleep(delayMilliseconds);
long time = System.nanoTime();
task.run();
addTime(feature, type, System.nanoTime()-time);
runMeasuredTask(errorDescription, feature, type, task);
} catch (InterruptedException pluginDisabled) {
Thread.currentThread().interrupt();
}
});
}
@Override
public Future<Void> runTaskLater(int delayMilliseconds, String errorDescription, Runnable task) {
return submit(errorDescription, () -> {
public Future<?> runTaskLater(int delayMilliseconds, String errorDescription, Runnable task) {
return threadPool.submit(() -> {
try {
Thread.sleep(delayMilliseconds);
task.run();
submit(errorDescription, task);
} catch (InterruptedException pluginDisabled) {
Thread.currentThread().interrupt();
}
@@ -146,10 +146,14 @@ public class CpuManager implements ThreadManager {
@SuppressWarnings("unchecked")
private Future<Void> submit(String errorDescription, Runnable task) {
return (Future<Void>) exe.submit(() -> {
if (!enabled) {
taskQueue.put(task, errorDescription);
return null;
}
return (Future<Void>) thread.submit(() -> {
try {
task.run();
} catch (Exception | NoClassDefFoundError e) {
} catch (Exception | LinkageError e) {
errorManager.printError("An error occurred when " + errorDescription, e);
}
});
@@ -254,7 +258,10 @@ public class CpuManager implements ThreadManager {
* @return sorted map
*/
private <K, V extends Comparable<V>> Map<K, V> sortByValue(Map<K, V> map) {
Comparator<K> valueComparator = (k1, k2) -> map.get(k2).compareTo(map.get(k1));
Comparator<K> valueComparator = (k1, k2) -> {
int diff = map.get(k2).compareTo(map.get(k1));
return diff == 0 ? 1 : diff; //otherwise, entries with duplicate values are lost
};
Map<K, V> sortedByValues = new TreeMap<>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
@@ -267,7 +274,10 @@ public class CpuManager implements ThreadManager {
* @return sorted map
*/
private <K> Map<K, AtomicInteger> sortByValue1(Map<K, AtomicInteger> map) {
Comparator<K> valueComparator = (k1, k2) -> ((Comparable<Integer>)map.get(k2).get()).compareTo(map.get(k1).get());
Comparator<K> valueComparator = (k1, k2) -> {
int diff = map.get(k2).get() - map.get(k1).get();
return diff == 0 ? 1 : diff; //otherwise, entries with duplicate values are lost
};
Map<K, AtomicInteger> sortedByValues = new TreeMap<>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
@@ -21,29 +21,18 @@ public class ErrorManager {
//date format used in error messages
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy - HH:mm:ss - ");
//one time messages already sent into console, so they are not sent again
private final List<String> oneTimeMessages = new ArrayList<>();
//amount of logged startup warns
private int startupWarns = 0;
//error logs
private final File errorLog;
private final File antiOverrideLog;
private final File placeholderErrorLog;
//plugin instance
private final TAB tab;
/**
* Constructs new instance
* @param tab - tab instance
*/
public ErrorManager(TAB tab) {
this.tab = tab;
errorLog = new File(tab.getPlatform().getDataFolder(), "errors.log");
antiOverrideLog = new File(tab.getPlatform().getDataFolder(), "anti-override.log");
placeholderErrorLog = new File(tab.getPlatform().getDataFolder(), "placeholder-errors.log");
public ErrorManager() {
errorLog = new File(TAB.getInstance().getPlatform().getDataFolder(), "errors.log");
antiOverrideLog = new File(TAB.getInstance().getPlatform().getDataFolder(), "anti-override.log");
placeholderErrorLog = new File(TAB.getInstance().getPlatform().getDataFolder(), "placeholder-errors.log");
if (getErrorLog().exists() && getErrorLog().length() > 10) {
startupWarn("File &e" + getErrorLog().getPath() + "&c exists and is not empty. Take a look at the error messages and try to resolve them. After you do, delete the file.");
}
@@ -99,16 +88,16 @@ public class ErrorManager {
}
}
} catch (IOException ex) {
tab.getPlatform().sendConsoleMessage("&c[TAB] An error occurred when printing error message into file", true);
tab.getPlatform().sendConsoleMessage(ex.getClass().getName() + ": " + ex.getMessage(), true);
TAB.getInstance().getPlatform().sendConsoleMessage("&c[TAB] An error occurred when printing error message into file", true);
TAB.getInstance().getPlatform().sendConsoleMessage(ex.getClass().getName() + ": " + ex.getMessage(), true);
for (StackTraceElement e : ex.getStackTrace()) {
tab.getPlatform().sendConsoleMessage("\t" + e.toString(), true);
TAB.getInstance().getPlatform().sendConsoleMessage("\t" + e.toString(), true);
}
tab.getPlatform().sendConsoleMessage("&c[TAB] Original error: " + message, true);
TAB.getInstance().getPlatform().sendConsoleMessage("&c[TAB] Original error: " + message, true);
if (error != null) {
tab.getPlatform().sendConsoleMessage(error.getClass().getName() + ": " + error.getMessage(), true);
TAB.getInstance().getPlatform().sendConsoleMessage(error.getClass().getName() + ": " + error.getMessage(), true);
for (StackTraceElement e : error.getStackTrace()) {
tab.getPlatform().sendConsoleMessage("\t" + e.toString(), true);
TAB.getInstance().getPlatform().sendConsoleMessage("\t" + e.toString(), true);
}
}
}
@@ -129,7 +118,7 @@ public class ErrorManager {
try {
return file.createNewFile();
} catch (IOException e) {
tab.getPlatform().sendConsoleMessage("&c[TAB] Failed to create file " + file.getPath() + ": " + e.getMessage(), true);
TAB.getInstance().getPlatform().sendConsoleMessage("&c[TAB] Failed to create file " + file.getPath() + ": " + e.getMessage(), true);
return false;
}
}
@@ -142,45 +131,24 @@ public class ErrorManager {
* @throws IOException - if IO writer operation fails
*/
private void write(BufferedWriter buf, String prefix, String message, boolean forceConsole) throws IOException {
buf.write(getCurrentTime() + IChatBaseComponent.fromColoredText(prefix).toRawText() + message + System.getProperty("line.separator"));
if (tab.isDebugMode() || forceConsole) tab.getPlatform().sendConsoleMessage(EnumChatFormat.color(prefix) + message, false);
buf.write(dateFormat.format(new Date()) + IChatBaseComponent.fromColoredText(prefix).toRawText() + message + System.getProperty("line.separator"));
if (TAB.getInstance().isDebugMode() || forceConsole) TAB.getInstance().getPlatform().sendConsoleMessage(EnumChatFormat.color(prefix) + message, false);
}
public void criticalError(String message, Throwable t) {
printError(message, t, true);
}
/**
* Sends message into console once
* @param message - message to send
*/
public void oneTimeConsoleError(String message) {
if (oneTimeMessages.contains(message)) return;
oneTimeMessages.add(message);
printError(message, null, true);
}
/**
* Returns current formatted time
* @return current formatted time
*/
private String getCurrentTime() {
return dateFormat.format(new Date());
}
/**
* Parses integer in given string, returns second argument if string is not valid
* @param string - string to parse
* @param defaultValue - value to return if string is not valid
* @param place - used in error message
* @return parsed integer
*/
public int parseInteger(String string, int defaultValue, String place) {
if (string == null || string.length() == 0) return 0; //preventing error message on bungee with papi placeholders due to them not being initialized yet
public int parseInteger(String string, int defaultValue) {
try {
return (int) Math.round(Double.parseDouble(string));
} catch (NumberFormatException e) {
oneTimeConsoleError(formatNumberError(place, string));
return defaultValue;
}
}
@@ -190,14 +158,12 @@ public class ErrorManager {
* Parses float in given string, returns second argument if string is not valid
* @param string - string to parse
* @param defaultValue - value to return if string is not valid
* @param place - used in error message
* @return parsed float
*/
public float parseFloat(String string, float defaultValue, String place) {
public float parseFloat(String string, float defaultValue) {
try {
return Float.parseFloat(string);
} catch (NumberFormatException e) {
oneTimeConsoleError(formatNumberError(place, string));
return defaultValue;
}
}
@@ -206,35 +172,26 @@ public class ErrorManager {
* Parses double in given string, returns second argument if string is not valid
* @param string - string to parse
* @param defaultValue - value to return if string is not valid
* @param place - used in error message
* @return parsed double
*/
public double parseDouble(String string, double defaultValue, String place) {
if (string == null || string.length() == 0) return 0; //preventing error message on bungee with papi placeholders due to them not being initialized yet
public double parseDouble(String string, double defaultValue) {
try {
return Double.parseDouble(string);
} catch (NumberFormatException e) {
oneTimeConsoleError(formatNumberError(place, string));
return defaultValue;
}
}
private String formatNumberError(String place, String value) {
return String.format("%s only accepts numeric values! (Attempted to use \"%s\")", place, value);
}
/**
* Parses bar color in given string, returns second argument if string is not valid
* @param string - string to parse
* @param defaultValue - value to return if string is not valid
* @param place - used in error message
* @return parsed bar color
*/
public BarColor parseColor(String string, BarColor defaultValue, String place) {
public BarColor parseColor(String string, BarColor defaultValue) {
try {
return BarColor.valueOf(string);
} catch (IllegalArgumentException e) {
oneTimeConsoleError(String.format("%s only accepts one of the defined colors! (Attempted to use \"%s\")", place, string));
return defaultValue;
}
}
@@ -243,14 +200,12 @@ public class ErrorManager {
* Parses bar style in given string, returns second argument if string is not valid
* @param string - string to parse
* @param defaultValue - value to return if string is not valid
* @param place - used in error message
* @return parsed bar style
*/
public BarStyle parseStyle(String string, BarStyle defaultValue, String place) {
public BarStyle parseStyle(String string, BarStyle defaultValue) {
try {
return BarStyle.valueOf(string);
} catch (IllegalArgumentException e) {
oneTimeConsoleError(String.format("%s only accepts one of the defined styles! (Attempted to use \"%s\")", place, string));
return defaultValue;
}
}
@@ -294,10 +249,7 @@ public class ErrorManager {
}
public void startupWarn(String message) {
if (oneTimeMessages.contains(message)) return;
oneTimeMessages.add(message);
tab.getPlatform().sendConsoleMessage("&c[TAB] " + message, true);
startupWarns++;
TAB.getInstance().getPlatform().sendConsoleMessage("&c[TAB] " + message, true);
}
/**
@@ -310,19 +262,6 @@ public class ErrorManager {
startupWarn(objectType + " \"&e" + objectName + "&c\" is missing \"&e" + attribute + "&c\" attribute!");
}
/**
* Prints amount of startup warns into console if more than 0
*/
public void printConsoleWarnCount() {
if (startupWarns > 0) {
if (startupWarns == 1) {
tab.getPlatform().sendConsoleMessage("&e[TAB] There was 1 startup warning.", true);
} else {
tab.getPlatform().sendConsoleMessage("&e[TAB] There were " + startupWarns + " startup warnings.", true);
}
}
}
public File getAntiOverrideLog() {
return antiOverrideLog;
}
@@ -24,7 +24,7 @@ public class FeatureManagerImpl implements FeatureManager {
//list of registered features
private final Map<String, TabFeature> features = new LinkedHashMap<>();
private TabFeature[] values;
private TabFeature[] values = new TabFeature[0];
@Override
public void registerFeature(String featureName, TabFeature featureHandler) {
@@ -111,6 +111,7 @@ public class FeatureManagerImpl implements FeatureManager {
*/
public void onQuit(TabPlayer disconnectedPlayer) {
if (disconnectedPlayer == null) return;
long millis = System.currentTimeMillis();
for (TabFeature f : values) {
if (!f.overridesMethod("onQuit")) continue;
long time = System.nanoTime();
@@ -118,14 +119,7 @@ public class FeatureManagerImpl implements FeatureManager {
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_QUIT, System.nanoTime()-time);
}
TAB.getInstance().removePlayer(disconnectedPlayer);
PlayerPlaceholder online = (PlayerPlaceholder) TAB.getInstance().getPlaceholderManager().getPlaceholder("%online%");
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.isLoaded()) online.updateValue(all, online.request(all));
}
if (TAB.getInstance().getConfiguration().getUsers() instanceof MySQLUserConfiguration) {
MySQLUserConfiguration users = (MySQLUserConfiguration) TAB.getInstance().getConfiguration().getUsers();
users.unload(disconnectedPlayer);
}
TAB.getInstance().debug("Player quit of " + disconnectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
}
/**
@@ -145,13 +139,10 @@ public class FeatureManagerImpl implements FeatureManager {
long time = System.nanoTime();
f.onJoin(connectedPlayer);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
}
((ITabPlayer)connectedPlayer).markAsLoaded();
((ITabPlayer)connectedPlayer).markAsLoaded(true);
TAB.getInstance().debug("Player join of " + connectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
PlayerPlaceholder online = (PlayerPlaceholder) TAB.getInstance().getPlaceholderManager().getPlaceholder("%online%");
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
if (all.isLoaded()) online.updateValue(all, online.request(all));
}
if (TAB.getInstance().getConfiguration().getUsers() instanceof MySQLUserConfiguration) {
MySQLUserConfiguration users = (MySQLUserConfiguration) TAB.getInstance().getConfiguration().getUsers();
users.load(connectedPlayer);
@@ -166,10 +157,7 @@ public class FeatureManagerImpl implements FeatureManager {
*/
public void onWorldChange(UUID playerUUID, String to) {
TabPlayer changed = TAB.getInstance().getPlayer(playerUUID);
if (changed == null || !changed.isLoaded()) {
TAB.getInstance().getCPUManager().runTaskLater(100, "processing delayed world/server switch", "Other", "Player world switch", () -> onWorldChange(playerUUID, to));
return;
}
if (changed == null) return;
String from = changed.getWorld();
((ITabPlayer)changed).setWorld(to);
for (TabFeature f : values) {
@@ -179,8 +167,6 @@ public class FeatureManagerImpl implements FeatureManager {
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
}
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%world%")).updateValue(changed, to);
PlayerPlaceholder worldOnline = (PlayerPlaceholder) TAB.getInstance().getPlaceholderManager().getPlaceholder("%worldonline%");
worldOnline.updateValue(changed, worldOnline.request(changed));
}
/**
@@ -194,13 +180,12 @@ public class FeatureManagerImpl implements FeatureManager {
String from = changed.getServer();
((ITabPlayer)changed).setServer(to);
for (TabFeature f : values) {
if (!f.overridesMethod("onServerChange")) continue;
long time = System.nanoTime();
f.onServerChange(changed, from, to);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.SERVER_SWITCH, System.nanoTime()-time);
}
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder("%server%")).updateValue(changed, to);
PlayerPlaceholder serverOnline = (PlayerPlaceholder) TAB.getInstance().getPlaceholderManager().getPlaceholder("%serveronline%");
serverOnline.updateValue(changed, serverOnline.request(changed));
}
/**
@@ -268,7 +253,7 @@ public class FeatureManagerImpl implements FeatureManager {
* @param packetReceiver - player who received the packet
*/
public void onLoginPacket(TabPlayer packetReceiver) {
((ITabPlayer)packetReceiver).clearRegisteredTeams();
((ITabPlayer)packetReceiver).clearRegisteredObjectives();
for (TabFeature f : values) {
if (!f.overridesMethod("onLoginPacket")) continue;
long time = System.nanoTime();
@@ -54,18 +54,19 @@ public class GroupManager extends TabFeature {
luckPermsSub2 = LuckPermsProvider.get().getEventBus().subscribe(GroupDataRecalculateEvent.class, this::updateGroup);
}
//synchronized because LP spams the event
private synchronized void updatePlayer(UserDataRecalculateEvent event) {
long time = System.nanoTime();
TabPlayer p = TAB.getInstance().getPlayer(event.getUser().getUniqueId());
if (p == null) return; //server still starting up and users connecting already (LP loading them)
refresh(p, false);
TAB.getInstance().getCPUManager().addTime("Permission group refreshing", TabConstants.CpuUsageCategory.LUCKPERMS_RECALCULATE_EVENT, System.nanoTime()-time);
groupPlaceholder.updateValue(p, p.getGroup());
private void updatePlayer(UserDataRecalculateEvent event) {
TAB.getInstance().getCPUManager().runMeasuredTask("Processing UserDataRecalculateEvent", this, "Processing UserDataRecalculateEvent", () -> {
long time = System.nanoTime();
TabPlayer p = TAB.getInstance().getPlayer(event.getUser().getUniqueId());
if (p == null) return; //server still starting up and users connecting already (LP loading them)
refresh(p, false);
TAB.getInstance().getCPUManager().addTime("Permission group refreshing", TabConstants.CpuUsageCategory.LUCKPERMS_RECALCULATE_EVENT, System.nanoTime()-time);
groupPlaceholder.updateValue(p, p.getGroup());
});
}
private synchronized void updateGroup(GroupDataRecalculateEvent event) {
TAB.getInstance().getCPUManager().runTaskLater(50, "Processing GroupDataRecalculateEvent", () -> {
private void updateGroup(GroupDataRecalculateEvent event) {
TAB.getInstance().getCPUManager().runTaskLater(50, "Processing GroupDataRecalculateEvent", this, "Processing GroupDataRecalculateEvent", () -> {
long time = System.nanoTime();
for (TabPlayer player : TAB.getInstance().getOnlinePlayers()) {
refresh(player, false);
@@ -2,9 +2,6 @@ package me.neznamy.tab.shared;
import java.util.*;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardTeam;
import org.geysermc.floodgate.api.FloodgateApi;
import io.netty.channel.Channel;
import me.neznamy.tab.api.ArmorStandManager;
import me.neznamy.tab.api.Property;
@@ -12,10 +9,11 @@ import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.protocol.PacketPlayOutChat;
import me.neznamy.tab.api.protocol.*;
import me.neznamy.tab.api.protocol.PacketPlayOutChat.ChatMessageType;
import me.neznamy.tab.api.protocol.TabPacket;
import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.shared.event.impl.PlayerLoadEventImpl;
import org.geysermc.floodgate.api.FloodgateApi;
/**
* The core class for player
@@ -42,6 +40,7 @@ public abstract class ITabPlayer implements TabPlayer {
private boolean onJoinFinished;
private final List<String> registeredTeams = new ArrayList<>();
private final List<String> registeredObjectives = new ArrayList<>();
protected ITabPlayer(Object player, UUID uniqueId, String name, String server, String world) {
this.player = player;
@@ -49,7 +48,7 @@ public abstract class ITabPlayer implements TabPlayer {
this.name = name;
this.server = server;
this.world = world;
bedrockPlayer = TAB.getInstance().isFloodgateInstalled() && FloodgateApi.getInstance().isFloodgatePlayer(uniqueId);
bedrockPlayer = TAB.getInstance().isFloodgateInstalled() && FloodgateApi.getInstance() != null && FloodgateApi.getInstance().isFloodgatePlayer(uniqueId);
setGroup(TAB.getInstance().getGroupManager().detectPermissionGroup(this), false);
}
@@ -106,18 +105,44 @@ public abstract class ITabPlayer implements TabPlayer {
}
@Override
public void sendCustomPacket(TabPacket packet) {
public synchronized void sendCustomPacket(TabPacket packet) {
if (packet == null) return;
//avoiding BungeeCord bug kicking all players
if (packet instanceof PacketPlayOutScoreboardTeam) {
String team = ((PacketPlayOutScoreboardTeam) packet).getName();
if (((PacketPlayOutScoreboardTeam) packet).getMethod() == 0) {
int method = ((PacketPlayOutScoreboardTeam) packet).getMethod();
if (method == 0) {
if (registeredTeams.contains(team)) {
TAB.getInstance().getErrorManager().printError("Tried to register duplicated team " + team + " to player " + getName());
return;
}
registeredTeams.add(team);
} else if (((PacketPlayOutScoreboardTeam) packet).getMethod() == 1) {
registeredTeams.remove(((PacketPlayOutScoreboardTeam) packet).getName());
} else if (method == 1) {
registeredTeams.remove(team);
}
}
//avoiding BungeeCord bug kicking all players
if (packet instanceof PacketPlayOutScoreboardObjective) {
String objective = ((PacketPlayOutScoreboardObjective) packet).getObjectiveName();
int method = ((PacketPlayOutScoreboardObjective) packet).getMethod();
if (method == 0) {
if (registeredObjectives.contains(objective)) {
TAB.getInstance().getErrorManager().printError("Tried to register duplicated objective " + objective + " to player " + getName());
return;
}
registeredObjectives.add(objective);
} else if (method == 1) {
registeredObjectives.remove(objective);
}
}
//avoiding console spam from geyser
if (packet instanceof PacketPlayOutScoreboardScore) {
String objective = ((PacketPlayOutScoreboardScore) packet).getObjectiveName();
String player = ((PacketPlayOutScoreboardScore) packet).getPlayer();
if (!registeredObjectives.contains(objective)) {
TAB.getInstance().getErrorManager().printError("Tried to update score (" + player + ") without the existence of its requested objective '" +
objective + "' to player " + getName());
return;
}
}
try {
@@ -190,12 +215,12 @@ public abstract class ITabPlayer implements TabPlayer {
}
@Override
public void loadPropertyFromConfig(TabFeature feature, String property) {
loadPropertyFromConfig(feature, property, "");
public boolean loadPropertyFromConfig(TabFeature feature, String property) {
return loadPropertyFromConfig(feature, property, "");
}
@Override
public void loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet) {
public boolean loadPropertyFromConfig(TabFeature feature, String property, String ifNotSet) {
String[] value = TAB.getInstance().getConfiguration().getUsers().getProperty(getName(), property, server, world);
if (value.length == 0) {
value = TAB.getInstance().getConfiguration().getUsers().getProperty(getUniqueId().toString(), property, server, world);
@@ -204,10 +229,9 @@ public abstract class ITabPlayer implements TabPlayer {
value = TAB.getInstance().getConfiguration().getGroups().getProperty(getGroup(), property, server, world);
}
if (value.length > 0) {
setProperty(feature, property, value[0], value[1]);
return;
return setProperty(feature, property, value[0], value[1]);
}
setProperty(feature, property, ifNotSet, "None");
return setProperty(feature, property, ifNotSet, "None");
}
@Override
@@ -265,8 +289,9 @@ public abstract class ITabPlayer implements TabPlayer {
teamName = name;
}
public void markAsLoaded() {
public void markAsLoaded(boolean join) {
onJoinFinished = true;
if (TAB.getInstance().getEventBus() != null) TAB.getInstance().getEventBus().fire(new PlayerLoadEventImpl(this, join));
TAB.getInstance().getPlatform().callLoadEvent(this);
}
@@ -290,8 +315,9 @@ public abstract class ITabPlayer implements TabPlayer {
server = name;
}
public void clearRegisteredTeams() {
public void clearRegisteredObjectives() {
registeredTeams.clear();
registeredObjectives.clear();
}
public void setTemporaryGroup(String group) {
@@ -1,36 +0,0 @@
package me.neznamy.tab.shared;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective.EnumScoreboardHealthDisplay;
/**
* Soon to be removed assistant for easier packet creating
*/
public class PacketAPI {
private PacketAPI() {
}
/**
* Registers scoreboard objective with given properties but sends unregister packet first unless disabled to avoid BungeeCord kick
* @param to - player to send the packet to
* @param objectiveName - name of the objective
* @param title - title
* @param position - objective position (0 = PlayerList, 1 = Sidebar, 2 = BelowName)
* @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) {
registerScoreboardObjective(to, objectiveName, title, position, displayType, feature.getFeatureName());
}
public static synchronized void registerScoreboardObjective(TabPlayer to, String objectiveName, String title, int position, EnumScoreboardHealthDisplay displayType, String feature) {
if (to.getVersion().getMinorVersion() >= 8 && TAB.getInstance().getConfiguration().isUnregisterBeforeRegister()) {
to.sendCustomPacket(new PacketPlayOutScoreboardObjective(objectiveName), feature);
}
to.sendCustomPacket(new PacketPlayOutScoreboardObjective(0, objectiveName, title, displayType), feature);
to.sendCustomPacket(new PacketPlayOutScoreboardDisplayObjective(position, objectiveName), feature);
}
}
@@ -1,7 +1,6 @@
package me.neznamy.tab.shared;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import me.neznamy.tab.api.Property;
@@ -42,9 +41,6 @@ public class PropertyImpl implements Property {
//used relational placeholders in current raw value
private String[] relPlaceholders;
private final StringBuilder formatBuffer = new StringBuilder();
private final Formatter formatter = new Formatter(formatBuffer);
public PropertyImpl(TabFeature listener, TabPlayer owner, String rawValue) {
this(listener, owner, rawValue, null);
@@ -56,7 +52,6 @@ public class PropertyImpl implements Property {
this.source = source;
this.rawValue = (rawValue == null ? "" : rawValue);
analyze(this.rawValue);
update();
}
/**
@@ -72,11 +67,11 @@ public class PropertyImpl implements Property {
relPlaceholders0.add(identifier);
}
}
String rawFormattedValue0 = RGBUtils.getInstance().applyFormats(value, true);
String rawFormattedValue0 = value;
for (String placeholder : placeholders0) {
rawFormattedValue0 = rawFormattedValue0.replace(placeholder, "%s");
}
if (rawFormattedValue0.contains("%")) {
if (placeholders0.size() > 0 && rawFormattedValue0.contains("%")) {
int index = rawFormattedValue0.lastIndexOf('%');
if (rawFormattedValue0.length() == index+1 || rawFormattedValue0.charAt(index+1) != 's') {
StringBuilder sb = new StringBuilder(rawFormattedValue0);
@@ -84,13 +79,15 @@ public class PropertyImpl implements Property {
rawFormattedValue0 = sb.toString();
}
}
rawFormattedValue0 = RGBUtils.getInstance().applyFormats(rawFormattedValue0, true);
rawFormattedValue = EnumChatFormat.color(rawFormattedValue0);
placeholders = placeholders0.toArray(new String[0]);
relPlaceholders = relPlaceholders0.toArray(new String[0]);
rawFormattedValue0 = EnumChatFormat.color(rawFormattedValue0);
rawFormattedValue = applyRemoveStrings(rawFormattedValue0); //this should never be needed
if (listener != null) {
listener.addUsedPlaceholders(placeholders0);
}
lastReplacedValue = rawFormattedValue;
update();
}
@Override
@@ -102,7 +99,6 @@ public class PropertyImpl implements Property {
this.temporaryValue = null;
analyze(rawValue);
}
update();
}
@Override
@@ -111,7 +107,6 @@ public class PropertyImpl implements Property {
rawValue = newValue;
if (temporaryValue == null) {
analyze(rawValue);
update();
}
}
@@ -154,24 +149,20 @@ public class PropertyImpl implements Property {
@Override
public boolean update() {
if (placeholders.length == 0) return false;
long time = System.nanoTime();
String string;
if (placeholders.length > 0) {
if ("%s".equals(rawFormattedValue)) {
string = TAB.getInstance().getPlaceholderManager().getPlaceholder(placeholders[0]).set(placeholders[0], owner);
} else {
String[] values = new String[placeholders.length];
for (int i=0; i<placeholders.length; i++) {
values[i] = TAB.getInstance().getPlaceholderManager().getPlaceholder(placeholders[i]).set(placeholders[i], owner);
}
string = formatter.format(rawFormattedValue, (Object[]) values).toString();
formatBuffer.setLength(0);
}
string = applyRemoveStrings(EnumChatFormat.color(string));
if ("%s".equals(rawFormattedValue)) {
string = TAB.getInstance().getPlaceholderManager().getPlaceholder(placeholders[0]).set(placeholders[0], owner);
} else {
string = rawFormattedValue;
Object[] values = new String[placeholders.length];
for (int i=0; i<placeholders.length; i++) {
values[i] = TAB.getInstance().getPlaceholderManager().getPlaceholder(placeholders[i]).set(placeholders[i], owner);
}
string = String.format(rawFormattedValue, values);
}
if (lastReplacedValue == null || !lastReplacedValue.equals(string)) {
string = EnumChatFormat.color(string);
if (!lastReplacedValue.equals(string)) {
lastReplacedValue = string;
TAB.getInstance().getCPUManager().addMethodTime("Property#update", System.nanoTime()-time);
return true;
@@ -179,19 +170,7 @@ public class PropertyImpl implements Property {
TAB.getInstance().getCPUManager().addMethodTime("Property#update", System.nanoTime()-time);
return false;
}
private String applyRemoveStrings(String text) {
if (TAB.getInstance().getConfiguration().getRemoveStrings().length == 0) return text;
String reformatted = text;
for (String removed : TAB.getInstance().getConfiguration().getRemoveStrings()) {
if (removed.startsWith("CONTAINS:") && reformatted.contains(removed.substring(9))) return "";
if (removed.startsWith("STARTS:") && reformatted.startsWith(removed.substring(7))) return "";
if (removed.startsWith("ENDS:") && reformatted.endsWith(removed.substring(5))) return "";
if (reformatted.contains(removed)) reformatted = reformatted.replace(removed, "");
}
return reformatted;
}
@Override
public String get() {
return lastReplacedValue;
@@ -4,6 +4,8 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import me.neznamy.tab.shared.event.EventBusImpl;
import me.neznamy.tab.shared.event.impl.TabLoadEventImpl;
import org.yaml.snakeyaml.error.YAMLException;
import me.neznamy.tab.api.HeaderFooterManager;
@@ -33,7 +35,6 @@ import me.neznamy.tab.shared.features.layout.LayoutManager;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
/**
* Universal variable and method storage
@@ -48,6 +49,7 @@ public class TAB extends TabAPI {
//player data
private final Map<UUID, TabPlayer> data = new ConcurrentHashMap<>();
private final Map<UUID, TabPlayer> playersByTablistId = new ConcurrentHashMap<>();
//player array to reduce memory allocation when iterating
private TabPlayer[] players = new TabPlayer[0];
@@ -64,6 +66,8 @@ public class TAB extends TabAPI {
//cpu manager
private CpuManager cpu;
private EventBusImpl eventBus;
//error manager
private ErrorManager errorManager;
@@ -92,9 +96,14 @@ public class TAB extends TabAPI {
try {
Class.forName("org.geysermc.floodgate.api.FloodgateApi");
floodgate = true;
} catch (ClassNotFoundException e) {
} catch (ClassNotFoundException | IllegalStateException e) {
//plugin not installed
}
try {
eventBus = new EventBusImpl();
} catch (NoSuchMethodError e) {
//1.7.10 or lower
}
}
@Override
@@ -103,15 +112,12 @@ public class TAB extends TabAPI {
}
/**
* Returns player by TabList UUID. This is required due to Velocity as player uuid and TabList uuid do ont match there
* Returns player by TabList UUID. This is required due to Velocity as player uuid and TabList uuid do not match there
* @param tabListId - TabList id of player
* @return the player or null if not found
*/
public TabPlayer getPlayerByTablistUUID(UUID tabListId) {
for (TabPlayer p : data.values()) {
if (p.getTablistUUID().equals(tabListId)) return p;
}
return null;
return playersByTablistId.get(tabListId);
}
/**
@@ -138,32 +144,33 @@ public class TAB extends TabAPI {
public String load() {
try {
long time = System.currentTimeMillis();
this.errorManager = new ErrorManager(this);
this.errorManager = new ErrorManager();
cpu = new CpuManager(errorManager);
featureManager = new FeatureManagerImpl();
configuration = new Configs(this);
configuration.loadFiles();
placeholderManager = new PlaceholderManagerImpl();
cpu.registerPlaceholder();
featureManager.registerFeature("placeholders", placeholderManager);
featureManager.registerFeature(TabConstants.Feature.PLACEHOLDER_MANAGER, placeholderManager);
groupManager = new GroupManager(platform.detectPermissionPlugin());
featureManager.registerFeature("groups", groupManager);
featureManager.registerFeature(TabConstants.Feature.GROUP_MANAGER, groupManager);
platform.loadFeatures();
command = new TabCommand(this);
featureManager.load();
for (TabPlayer p : players) ((ITabPlayer)p).markAsLoaded();
errorManager.printConsoleWarnCount();
print('a', "Enabled in " + (System.currentTimeMillis()-time) + "ms");
for (TabPlayer p : players) ((ITabPlayer)p).markAsLoaded(false);
cpu.enable();
if (eventBus != null) eventBus.fire(TabLoadEventImpl.getInstance());
platform.callLoadEvent();
disabled = false;
print('a', "Enabled in " + (System.currentTimeMillis()-time) + "ms");
return configuration.getMessages().getReloadSuccess();
} catch (YAMLException e) {
print('c', "Did not enable due to a broken configuration file.");
disabled = true;
kill();
return configuration.getReloadFailedMessage().replace("%file%", "-"); //recode soon
} catch (Exception e) {
errorManager.criticalError("Failed to enable. Did you just invent a new way to break the plugin by misconfiguring it?", e);
disabled = true;
kill();
return "&cFailed to enable due to an internal plugin error. Check console for more info.";
}
}
@@ -173,60 +180,68 @@ public class TAB extends TabAPI {
*/
public void unload() {
if (disabled) return;
disabled = true;
try {
long time = System.currentTimeMillis();
cpu.cancelAllTasks();
if (configuration.getMysql() != null) configuration.getMysql().closeConnection();
featureManager.unload();
data.clear();
platform.sendConsoleMessage("&a[TAB] Disabled in " + (System.currentTimeMillis()-time) + "ms", true);
} catch (Exception e) {
data.clear();
errorManager.criticalError("Failed to disable", e);
}
kill();
}
private void kill() {
disabled = true;
data.clear();
players = new TabPlayer[0];
cpu.cancelAllTasks();
}
/**
* Loads universal features present on all platforms with the same configuration
*/
public void loadUniversalFeatures() {
if (configuration.getConfig().getBoolean("header-footer.enabled", true)) featureManager.registerFeature("headerfooter", new HeaderFooter());
if (configuration.isRemoveGhostPlayers()) featureManager.registerFeature("ghostplayerfix", new GhostPlayerFix());
if (configuration.getConfig().getBoolean("header-footer.enabled", true))
featureManager.registerFeature(TabConstants.Feature.HEADER_FOOTER, new HeaderFooter());
if (configuration.isRemoveGhostPlayers())
featureManager.registerFeature(TabConstants.Feature.GHOST_PLAYER_FIX, new GhostPlayerFix());
if (serverVersion.getMinorVersion() >= 8 && configuration.getConfig().getBoolean("tablist-name-formatting.enabled", true)) {
if (configuration.getConfig().getBoolean("tablist-name-formatting.align-tabsuffix-on-the-right", false)) {
featureManager.registerFeature("playerlist", new AlignedPlayerList());
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new AlignedPlayerList());
} else {
featureManager.registerFeature("playerlist", new PlayerList());
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new PlayerList());
}
}
if (configuration.getConfig().getBoolean("ping-spoof.enabled", false)) featureManager.registerFeature("pingspoof", new PingSpoof());
if (configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true)) featureManager.registerFeature("tabobjective", new YellowNumber());
if (configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false)) featureManager.registerFeature("spectatorfix", new SpectatorFix());
if (configuration.getConfig().getBoolean("belowname-objective.enabled", true)) featureManager.registerFeature("belowname", new BelowName());
if (configuration.getConfig().getBoolean("scoreboard.enabled", false)) featureManager.registerFeature("scoreboard", new ScoreboardManagerImpl());
if (configuration.getConfig().getBoolean("ping-spoof.enabled", false))
featureManager.registerFeature(TabConstants.Feature.PING_SPOOF, new PingSpoof());
if (configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true))
featureManager.registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber());
if (configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false))
featureManager.registerFeature(TabConstants.Feature.SPECTATOR_FIX, new SpectatorFix());
if (configuration.getConfig().getBoolean("belowname-objective.enabled", true))
featureManager.registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName());
if (configuration.getConfig().getBoolean("scoreboard.enabled", false))
featureManager.registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl());
if (serverVersion.getMinorVersion() >= 8 && configuration.getLayout().getBoolean("enabled", false)) {
if (getTeamManager() == null) {
//sorting is disabled, but layout needs team names
featureManager.registerFeature("sorting", new Sorting(null));
}
featureManager.registerFeature("layout", new LayoutManager());
}
featureManager.registerFeature("nick", new NickCompatibility());
if (platform.isProxy()) {
for (TabPlayer all : TAB.getInstance().getOnlinePlayers()) {
((ProxyTabPlayer)all).getPluginMessageHandler().requestAttribute(all, "world");
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting(null));
}
featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManager());
}
featureManager.registerFeature(TabConstants.Feature.NICK_COMPATIBILITY, new NickCompatibility());
}
public void addPlayer(TabPlayer player) {
data.put(player.getUniqueId(), player);
playersByTablistId.put(player.getTablistUUID(), player);
players = data.values().toArray(new TabPlayer[0]);
}
public void removePlayer(TabPlayer player) {
data.remove(player.getUniqueId());
playersByTablistId.remove(player.getTablistUUID());
players = data.values().toArray(new TabPlayer[0]);
}
@@ -251,6 +266,11 @@ public class TAB extends TabAPI {
return cpu;
}
@Override
public EventBusImpl getEventBus() {
return eventBus;
}
public ErrorManager getErrorManager() {
return errorManager;
}
@@ -286,18 +306,18 @@ public class TAB extends TabAPI {
@Override
public BossBarManager getBossBarManager() {
return (BossBarManager) featureManager.getFeature("bossbar");
return (BossBarManager) featureManager.getFeature(TabConstants.Feature.BOSS_BAR);
}
@Override
public ScoreboardManager getScoreboardManager() {
return (ScoreboardManager) featureManager.getFeature("scoreboard");
return (ScoreboardManager) featureManager.getFeature(TabConstants.Feature.SCOREBOARD);
}
@Override
public TeamManager getTeamManager() {
if (featureManager.isFeatureEnabled("nametag16")) return (NameTag) featureManager.getFeature("nametag16");
return (NameTag) featureManager.getFeature("nametagx");
if (featureManager.isFeatureEnabled(TabConstants.Feature.NAME_TAGS)) return (NameTag) featureManager.getFeature(TabConstants.Feature.NAME_TAGS);
return (NameTag) featureManager.getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
}
@Override
@@ -325,7 +345,7 @@ public class TAB extends TabAPI {
@Override
public HeaderFooterManager getHeaderFooterManager() {
return (HeaderFooterManager) featureManager.getFeature("headerfooter");
return (HeaderFooterManager) featureManager.getFeature(TabConstants.Feature.HEADER_FOOTER);
}
@Override
@@ -368,6 +388,6 @@ public class TAB extends TabAPI {
@Override
public TablistFormatManager getTablistFormatManager() {
return (TablistFormatManager) featureManager.getFeature("playerlist");
return (TablistFormatManager) featureManager.getFeature(TabConstants.Feature.PLAYER_LIST);
}
}
@@ -14,11 +14,12 @@ public class TabConstants {
public static final String PLAYER_SNEAK = "Player Sneak";
public static final String PLAYER_RESPAWN = "Player Respawn";
public static final String PLUGIN_MESSAGE = "PluginMessageEvent";
public static final String REDIS_BUNGEE_MESSAGE = "PubSubMessageEvent";
public static final String LUCKPERMS_RECALCULATE_EVENT = "UserDataRecalculateEvent";
public static final String ANTI_OVERRIDE = "Anti override";
public static final String BYTEBUF = "ByteBuf";
public static final String BYTE_BUF = "ByteBuf";
public static final String PACKET_PLAYER_INFO = "PacketPlayOutPlayerInfo";
public static final String PACKET_DISPLAY_OBJECTIVE = "PacketPlayOutScoreboardDisplayObjective";
public static final String PACKET_OBJECTIVE = "PacketPlayOutScoreboardObjective";
@@ -33,12 +34,9 @@ public class TabConstants {
public static final String PLACEHOLDER_REFRESHING = "Refreshing placeholders";
public static final String V1_8_0_BUG_COMPENSATION = "Compensating for 1.8.0 bugs";
public static final String REFRESHING_NAMETAG_VISIBILITY = "Refreshing nametag visibility";
public static final String TICKING_VEHICLES = "Ticking vehicles";
public static final String TELEPORTING_WITHER = "Teleporting Wither entity";
public static final String REFRESHING_NAME_TAG_VISIBILITY = "Refreshing NameTag visibility";
public static final String SCOREBOARD_PACKET_CHECK = "Checking for other plugins";
public static final String REFRESHING_TEAM_NAMES = "Refreshing team names";
public static final String REFRESHING_GROUPS = "Refreshing player groups";
public static final String PROCESSING_PLAYER_MOVEMENT = "Processing player movement";
private CpuUsageCategory() {}
}
@@ -50,26 +48,25 @@ public class TabConstants {
public static final String BOSSBAR_TEXT = "BossBar (Text)";
public static final String BOSSBAR_WITHER_TELEPORT = "BossBar (Teleporting Wither)";
public static final String GLOBAL_PLAYERLIST_LATENCY = "Global Playerlist (Updating latency)";
public static final String GLOBAL_PLAYERLIST_VANISH = "Global Playerlist (Vanishing)";
public static final String GLOBAL_PLAYERLIST_LATENCY = "Global PlayerList (Updating latency)";
public static final String GLOBAL_PLAYERLIST_VANISH = "Global PlayerList (Vanishing)";
public static final String LAYOUT_FIXED_SLOTS = "Layout (Fixed slots)";
public static final String LAYOUT_LATENCY = "Layout (Updating latency)";
public static final String LAYOUT_PLAYER_SLOTS = "Layout (Player slots)";
public static final String NAMETAGS_TEAM_REGISTER = "Nametags (Team register)";
public static final String NAMETAGS_TEAM_UNREGISTER = "Nametags (Team unregister)";
public static final String NAMETAGS_TEAM_UPDATE = "Nametags (Team update)";
public static final String NAMETAGS_TEAM_REGISTER = "NameTags (Team register)";
public static final String NAMETAGS_TEAM_UNREGISTER = "NameTags (Team unregister)";
public static final String NAMETAGS_TEAM_UPDATE = "NameTags (Team update)";
public static final String SCOREBOARD_TITLE = "Scoreboard (Title)";
public static final String SCOREBOARD_LINES = "Scoreboard (Lines)";
public static final String UNLIMITED_NAMETAGS_OFFSET_CHANGE = "Unlimited Nametags (Changing offset)";
public static final String UNLIMITED_NAMETAGS_SPAWN = "Unlimited Nametags (Spawning)";
public static final String UNLIMITED_NAMETAGS_DESPAWN = "Unlimited Nametags (Despawning)";
public static final String UNLIMITED_NAMETAGS_TELEPORT = "Unlimited Nametags (Teleporting)";
public static final String UNLIMITED_NAMETAGS_SNEAK = "Unlimited Nametags (Sneaking)";
public static final String UNLIMITED_NAMETAGS_METADATA = "Unlimited Nametags (Metadata)";
public static final String UNLIMITED_NAMETAGS_OFFSET_CHANGE = "Unlimited NameTags (Changing offset)";
public static final String UNLIMITED_NAMETAGS_SPAWN = "Unlimited NameTags (Spawning)";
public static final String UNLIMITED_NAMETAGS_DESPAWN = "Unlimited NameTags (Despawning)";
public static final String UNLIMITED_NAMETAGS_TELEPORT = "Unlimited NameTags (Teleporting)";
public static final String UNLIMITED_NAMETAGS_SNEAK = "Unlimited NameTags (Sneaking)";
public static final String UNLIMITED_NAMETAGS_METADATA = "Unlimited NameTags (Metadata)";
private PacketCategory() {}
}
@@ -162,4 +159,74 @@ public class TabConstants {
return scoreboard + "-" + lineNumber + "-suffix";
}
}
public static class Feature {
//universal features
public static final String HEADER_FOOTER = "HeaderFooter";
public static final String GHOST_PLAYER_FIX = "GhostPlayerFix";
public static final String PLAYER_LIST = "PlayerList";
public static final String SPECTATOR_FIX = "SpectatorFix";
public static final String YELLOW_NUMBER = "YellowNumber";
public static final String BELOW_NAME = "BelowName";
public static final String BELOW_NAME_TEXT = "BelowNameText";
public static final String SCOREBOARD = "ScoreBoard";
public static final String PING_SPOOF = "PingSpoof";
public static final String SORTING = "sorting";
public static final String LAYOUT = "layout";
public static final String LAYOUT_VANISH = "layout-vanish";
public static final String NICK_COMPATIBILITY = "nick";
public static final String PIPELINE_INJECTION = "injection";
public static final String BOSS_BAR = "BossBar";
public static final String NAME_TAGS = "NameTag16";
public static final String NAME_TAGS_COLLISION = "NameTagCollision";
public static final String NAME_TAGS_VISIBILITY = "NameTagVisibility";
public static final String GROUP_MANAGER = "GroupManager";
public static final String PLACEHOLDER_MANAGER = "PlaceholderManager";
//Bukkit only
public static final String PER_WORLD_PLAYER_LIST = "PerWorldPlayerList";
public static final String PET_FIX = "PetFix";
public static final String UNLIMITED_NAME_TAGS = "NameTagX";
public static final String UNLIMITED_NAME_TAGS_PACKET_LISTENER = "nametagx-packet";
public static final String UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER = "nametagx-vehicle";
public static final String UNLIMITED_NAME_TAGS_LOCATION_REFRESHER = "nametagx-location";
//BungeeCord only
public static final String REDIS_BUNGEE = "RedisBungee";
public static final String GLOBAL_PLAYER_LIST = "GlobalPlayerList";
public static final String GLOBAL_PLAYER_LIST_LATENCY = "GlobalPlayerList-Latency";
public static final String GLOBAL_PLAYER_LIST_VANISH = "GlobalPlayerList-Vanish";
private Feature(){
}
public static String scoreboardLine(String line) {
return "scoreboard-" + line;
}
public static String layout(String name) {
return "layout-" + name;
}
public static String layoutSlot(String layout, int slot) {
return "layout-" + layout + "-slot-" + slot;
}
public static String bossBarTitle(String name) {
return "BossBar-title-" + name;
}
public static String bossBarProgress(String name) {
return "BossBar-progress-" + name;
}
public static String bossBarColorStyle(String name) {
return "BossBar-color-style-" + name;
}
public static String scoreboardLine(String scoreboard, int index) {
return "scoreboard-score-" + scoreboard + "-" + index;
}
}
}
@@ -1,49 +1,46 @@
package me.neznamy.tab.shared;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.task.RepeatingTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
public class TabRepeatingTask implements RepeatingTask {
private Future<Void> task;
private final ThreadPoolExecutor executor;
private final ExecutorService exe;
private Runnable runnable;
private final String errorDescription;
private final TabFeature feature;
private final String type;
private int interval;
private Future<?> task;
public TabRepeatingTask(ThreadPoolExecutor executor, Runnable runnable, String errorDescription, TabFeature feature, String type, int interval) {
public TabRepeatingTask(ExecutorService exe, Runnable runnable, String errorDescription, TabFeature feature, String type, int interval) {
if (interval < 0) throw new IllegalArgumentException("Interval cannot be negative");
this.executor = executor;
this.exe = exe;
this.runnable = runnable;
this.errorDescription = errorDescription;
this.feature = feature;
this.type = type;
this.interval = interval;
this.task = createTask();
createTask();
}
@SuppressWarnings("unchecked")
private Future<Void> createTask() {
return (Future<Void>)executor.submit(() -> {
long nextLoop = System.currentTimeMillis() - interval;
private void createTask() {
task = exe.submit(() -> {
long nextLoop = System.currentTimeMillis();
while (true) {
try {
nextLoop += interval;
long sleep = nextLoop - System.currentTimeMillis();
if (sleep > interval) sleep = interval; //time travelers who travel back in time
if (sleep > interval) sleep = interval; //servers who travel back in time
if (sleep > 0) Thread.sleep(sleep);
long time = System.nanoTime();
runnable.run();
TAB.getInstance().getCPUManager().addTime(feature, type, System.nanoTime() - time);
TAB.getInstance().getCPUManager().runMeasuredTask(errorDescription, feature, type, runnable);
} catch (InterruptedException pluginDisabled) {
Thread.currentThread().interrupt();
break;
} catch (Exception | NoClassDefFoundError e) {
} catch (Exception | LinkageError e) {
TAB.getInstance().getErrorManager().printError("An error occurred when " + errorDescription, e);
}
}
@@ -60,7 +57,7 @@ public class TabRepeatingTask implements RepeatingTask {
if (interval < 0) throw new IllegalArgumentException("Interval cannot be negative");
cancel();
this.interval = interval;
task = createTask();
createTask();
}
@Override
@@ -19,7 +19,7 @@ public class BossBarCommand extends SubCommand {
@Override
public void execute(TabPlayer sender, String[] args) {
BossBarManager bossBar = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature("bossbar");
BossBarManager bossBar = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.BOSS_BAR);
if (bossBar == null) {
sendMessage(sender, getMessages().getBossBarNotEnabled());
return;
@@ -67,7 +67,6 @@ public class CpuCommand extends SubCommand {
sendToConsole(features);
}
sendMessage(sender, SEPARATOR);
sendMessage(sender, String.format("&8&l%s &7Threads created by the plugin (active/total): &7%s", LINE_CHAR, tab.getCPUManager().getThreadCount()));
if (sender != null) {
sendPacketCountToPlayer(sender);
} else {
@@ -66,8 +66,8 @@ public class DebugCommand extends SubCommand {
sendMessage(sender, getGroup(analyzed));
sendMessage(sender, getTeamName(analyzed));
sendMessage(sender, getTeamNameNote(analyzed));
if (tab.getFeatureManager().isFeatureEnabled("playerlist")) {
PlayerList playerlist = (PlayerList) tab.getFeatureManager().getFeature("playerlist");
if (tab.getFeatureManager().isFeatureEnabled(TabConstants.Feature.PLAYER_LIST)) {
PlayerList playerlist = (PlayerList) tab.getFeatureManager().getFeature(TabConstants.Feature.PLAYER_LIST);
boolean disabledPlayerlist = playerlist.isDisabled(analyzed.getServer(), analyzed.getWorld());
showProperty(sender, analyzed, TabConstants.Property.TABPREFIX, disabledPlayerlist);
showProperty(sender, analyzed, TabConstants.Property.TABSUFFIX, disabledPlayerlist);
@@ -164,7 +164,7 @@ public class DebugCommand extends SubCommand {
*/
@SuppressWarnings("unchecked")
public List<Object> getExtraLines(){
if (!TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx")) return new ArrayList<>();
if (!TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS)) return new ArrayList<>();
List<Object> lines = Lists.newArrayList((List<Object>) TAB.getInstance().getConfiguration().getConfig().getObject("scoreboard-teams.unlimited-nametag-mode.dynamic-lines"));
lines.addAll(TAB.getInstance().getConfiguration().getConfig().getConfigurationSection("scoreboard-teams.unlimited-nametag-mode.static-lines").keySet());
lines.remove(TabConstants.Property.NAMETAG);
@@ -27,7 +27,20 @@ public class GroupCommand extends PropertyCommand {
}
String group = args[0];
String type = args[1].toLowerCase();
String value = buildArgument(Arrays.copyOfRange(args, 2, args.length));
String value = String.join(" ", Arrays.copyOfRange(args, 2, args.length));
String world = null;
String server = null;
if (args[args.length-2].equals("-w")) {
world = args[args.length-1];
value = value.substring(0, value.length()-world.length()-4);
}
if (args[args.length-2].equals("-s")) {
server = args[args.length-1];
value = value.substring(0, value.length()-server.length()-4);
}
if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
value = value.substring(1, value.length()-1);
}
if ("remove".equals(type)) {
if (hasPermission(sender, TabConstants.Permission.COMMAND_DATA_REMOVE)) {
TAB.getInstance().getConfiguration().getGroups().remove(group);
@@ -45,8 +58,8 @@ public class GroupCommand extends PropertyCommand {
for (String property : getAllProperties()) {
if (type.equals(property)) {
if (hasPermission(sender, TabConstants.Permission.COMMAND_PROPERTY_CHANGE_PREFIX + property)) {
saveGroup(sender, group, type, value);
if (extraProperties.contains(property) && !TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx")) {
saveGroup(sender, group, type, value, server, world);
if (extraProperties.contains(property) && !TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS)) {
sendMessage(sender, getMessages().getUnlimitedNametagModeNotEnabled());
}
} else {
@@ -65,15 +78,15 @@ public class GroupCommand extends PropertyCommand {
* @param type - property type
* @param value - new value
*/
private void saveGroup(TabPlayer sender, String group, String type, String value){
private void saveGroup(TabPlayer sender, String group, String type, String value, String server, String world){
if (value.length() > 0){
sendMessage(sender, getMessages().getGroupValueAssigned(type, value, group));
} else {
sendMessage(sender, getMessages().getGroupValueRemoved(type, group));
}
String[] property = TAB.getInstance().getConfiguration().getGroups().getProperty(group, type, null, null);
String[] property = TAB.getInstance().getConfiguration().getGroups().getProperty(group, type, server, world);
if (property.length > 0 && String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(property[0]))) return;
TAB.getInstance().getConfiguration().getGroups().setProperty(group, type, null, null, value.length() == 0 ? null : value);
TAB.getInstance().getConfiguration().getGroups().setProperty(group, type, server, world, value.length() == 0 ? null : value);
for (TabPlayer pl : TAB.getInstance().getOnlinePlayers()) {
if (pl.getGroup().equals(group) || "_DEFAULT_".equals(group)){
pl.forceRefresh();
@@ -18,7 +18,7 @@ public class NTPreviewCommand extends SubCommand{
@Override
public void execute(TabPlayer sender, String[] args) {
if ((TAB.getInstance().getFeatureManager().getFeature("nametagx")) != null) {
if ((TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS)) != null) {
if (sender != null) {
sender.toggleNametagPreview();
} else {
@@ -3,10 +3,12 @@ package me.neznamy.tab.shared.command;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.placeholder.Placeholder;
import me.neznamy.tab.shared.PropertyImpl;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
@@ -60,9 +62,15 @@ public class ParseCommand extends SubCommand {
@Override
public List<String> complete(TabPlayer sender, String[] arguments) {
if (arguments.length != 1) return new ArrayList<>();
List<String> suggestions = getOnlinePlayers(arguments[0]);
if ("me".startsWith(arguments[0].toLowerCase())) suggestions.add("me");
return suggestions;
if (arguments.length == 1) {
List<String> suggestions = getOnlinePlayers(arguments[0]);
if ("me".startsWith(arguments[0].toLowerCase())) suggestions.add("me");
return suggestions;
}
if (arguments.length == 2) {
return TAB.getInstance().getPlaceholderManager().getAllPlaceholders().stream().map(Placeholder::getIdentifier)
.filter(placeholder -> placeholder.toLowerCase().startsWith(arguments[1].toLowerCase())).collect(Collectors.toList());
}
return new ArrayList<>();
}
}
@@ -28,7 +28,20 @@ public class PlayerCommand extends PropertyCommand {
}
String player = args[0];
String type = args[1].toLowerCase();
String value = buildArgument(Arrays.copyOfRange(args, 2, args.length));
String value = String.join(" ", Arrays.copyOfRange(args, 2, args.length));
String world = null;
String server = null;
if (args[args.length-2].equals("-w")) {
world = args[args.length-1];
value = value.substring(0, value.length()-world.length()-4);
}
if (args[args.length-2].equals("-s")) {
server = args[args.length-1];
value = value.substring(0, value.length()-server.length()-4);
}
if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
value = value.substring(1, value.length()-1);
}
if ("remove".equals(type)) {
if (hasPermission(sender, TabConstants.Permission.COMMAND_DATA_REMOVE)) {
TAB.getInstance().getConfiguration().getUsers().remove(player);
@@ -45,8 +58,8 @@ public class PlayerCommand extends PropertyCommand {
for (String property : getAllProperties()) {
if (type.equals(property)) {
if (hasPermission(sender, TabConstants.Permission.COMMAND_PROPERTY_CHANGE_PREFIX + property)) {
savePlayer(sender, player, type, value);
if (extraProperties.contains(property) && !TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx")) {
savePlayer(sender, player, type, value, server, world);
if (extraProperties.contains(property) && !TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS)) {
sendMessage(sender, getMessages().getUnlimitedNametagModeNotEnabled());
}
} else {
@@ -65,15 +78,15 @@ public class PlayerCommand extends PropertyCommand {
* @param type - property type
* @param value - new value
*/
public void savePlayer(TabPlayer sender, String player, String type, String value){
public void savePlayer(TabPlayer sender, String player, String type, String value, String server, String world){
if (value.length() > 0){
sendMessage(sender, getMessages().getPlayerValueAssigned(type, value, player));
} else {
sendMessage(sender, getMessages().getPlayerValueRemoved(type, player));
}
String[] property = TAB.getInstance().getConfiguration().getUsers().getProperty(player, type, null, null);
String[] property = TAB.getInstance().getConfiguration().getUsers().getProperty(player, type, server, world);
if (property.length > 0 && String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(property[0]))) return;
TAB.getInstance().getConfiguration().getUsers().setProperty(player, type, null, null, value.length() == 0 ? null : value);
TAB.getInstance().getConfiguration().getUsers().setProperty(player, type, server, world, value.length() == 0 ? null : value);
TabPlayer pl = TAB.getInstance().getPlayer(player);
if (pl != null) {
pl.forceRefresh();
@@ -33,7 +33,20 @@ public class PlayerUUIDCommand extends PropertyCommand {
return;
}
String type = args[1].toLowerCase();
String value = buildArgument(Arrays.copyOfRange(args, 2, args.length));
String value = String.join(" ", Arrays.copyOfRange(args, 2, args.length));
String world = null;
String server = null;
if (args[args.length-2].equals("-w")) {
world = args[args.length-1];
value = value.substring(0, value.length()-world.length()-4);
}
if (args[args.length-2].equals("-s")) {
server = args[args.length-1];
value = value.substring(0, value.length()-server.length()-4);
}
if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
value = value.substring(1, value.length()-1);
}
if ("remove".equals(type)) {
if (hasPermission(sender, TabConstants.Permission.COMMAND_DATA_REMOVE)) {
TAB.getInstance().getConfiguration().getUsers().remove(changed.getUniqueId().toString());
@@ -47,8 +60,8 @@ public class PlayerUUIDCommand extends PropertyCommand {
for (String property : getAllProperties()) {
if (type.equals(property)) {
if (hasPermission(sender, TabConstants.Permission.COMMAND_PROPERTY_CHANGE_PREFIX + property)) {
savePlayer(sender, changed, type, value);
if (extraProperties.contains(property) && !TAB.getInstance().getFeatureManager().isFeatureEnabled("nametagx")) {
savePlayer(sender, changed, type, value, server, world);
if (extraProperties.contains(property) && !TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.UNLIMITED_NAME_TAGS)) {
sendMessage(sender, getMessages().getUnlimitedNametagModeNotEnabled());
}
} else {
@@ -67,15 +80,15 @@ public class PlayerUUIDCommand extends PropertyCommand {
* @param type - property type
* @param value - new value
*/
public void savePlayer(TabPlayer sender, TabPlayer player, String type, String value){
public void savePlayer(TabPlayer sender, TabPlayer player, String type, String value, String server, String world){
if (value.length() > 0){
sendMessage(sender, getMessages().getPlayerValueAssigned(type, value, player.getName() + "(" + player.getUniqueId().toString() + ")"));
} else {
sendMessage(sender, getMessages().getPlayerValueRemoved(type, player.getName() + "(" + player.getUniqueId().toString() + ")"));
}
String[] property = TAB.getInstance().getConfiguration().getUsers().getProperty(player.getUniqueId().toString(), type, null, null);
String[] property = TAB.getInstance().getConfiguration().getUsers().getProperty(player.getUniqueId().toString(), type, server, world);
if (property.length > 0 && String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(property[0]))) return;
TAB.getInstance().getConfiguration().getUsers().setProperty(player.getUniqueId().toString(), type, null, null, value.length() == 0 ? null : value);
TAB.getInstance().getConfiguration().getUsers().setProperty(player.getUniqueId().toString(), type, server, world, value.length() == 0 ? null : value);
player.forceRefresh();
}
@@ -42,20 +42,18 @@ public class ScoreboardCommand extends SubCommand {
TabPlayer p = getTarget(sender, args);
if (scoreboard.getOtherPluginScoreboards().containsKey(p)) return; //not overriding other plugins
boolean silent = args.length >= 3 && args[2].equals("-s");
if (args.length >= 1) {
switch(args[0]) {
case "on":
scoreboard.setScoreboardVisible(p, true, !silent);
break;
case "off":
scoreboard.setScoreboardVisible(p, false, !silent);
break;
case "toggle":
scoreboard.toggleScoreboard(p, !silent);
break;
default:
break;
}
switch(args[0]) {
case "on":
scoreboard.setScoreboardVisible(p, true, !silent);
break;
case "off":
scoreboard.setScoreboardVisible(p, false, !silent);
break;
case "toggle":
scoreboard.toggleScoreboard(p, !silent);
break;
default:
break;
}
}
@@ -74,6 +72,10 @@ public class ScoreboardCommand extends SubCommand {
}
private void show(TabPlayer sender, String[] args) {
if (args.length < 2) {
sendMessage(sender, getMessages().getScoreboardShowUsage());
return;
}
ScoreboardManagerImpl scoreboard = getScoreboardManager();
Scoreboard sb = scoreboard.getRegisteredScoreboards().get(args[1]);
if (sb == null) {
@@ -88,7 +90,7 @@ public class ScoreboardCommand extends SubCommand {
}
target = sender;
} else {
if (!hasPermission(sender,TabConstants.Permission.COMMAND_SCOREBOARD_SHOW)) {
if (!hasPermission(sender,TabConstants.Permission.COMMAND_SCOREBOARD_SHOW_OTHER)) {
sendMessage(sender, getMessages().getNoPermission());
return;
}
@@ -114,7 +116,7 @@ public class ScoreboardCommand extends SubCommand {
}
private ScoreboardManagerImpl getScoreboardManager() {
return (ScoreboardManagerImpl) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
return (ScoreboardManagerImpl) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SCOREBOARD);
}
@Override
@@ -151,19 +151,6 @@ public abstract class SubCommand {
}
return new ArrayList<>();
}
/**
* Converts string array into a single string with "" and '' support for spaces
* @param args - array of arguments
* @return argument from array
*/
public String buildArgument(String[] args) {
String value = String.join(" ", args);
if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
value = value.substring(1, value.length()-1);
}
return value;
}
public MessageFile getMessages() {
return TAB.getInstance().getConfiguration().getMessages();
@@ -82,26 +82,10 @@ public class TabCommand extends SubCommand {
} else {
tab.getPlatform().sendConsoleMessage("&3TAB v" + TAB.PLUGIN_VERSION, true);
}
String command = !tab.getPlatform().isProxy() ? "/tab" : "/btab";
String prefix = " &8>> &3&l";
sendMessage(sender, "&m ");
sendMessage(sender, prefix + command + " reload");
sendMessage(sender, " - &7Reloads plugin and config");
sendMessage(sender, prefix + command + " &9group&3/&9player &3<name> &9<property> &3<value...>");
sendMessage(sender, " - &7Do &8/tab group/player &7to show properties");
sendMessage(sender, prefix + command + " ntpreview");
sendMessage(sender, " - &7Shows your nametag for yourself, for testing purposes");
sendMessage(sender, prefix + command + " announce bar &3<name> &9<seconds>");
sendMessage(sender, " - &7Temporarily displays bossbar to all players");
sendMessage(sender, prefix + command + " parse <player> <placeholder> ");
sendMessage(sender, " - &7Test if a placeholder works");
sendMessage(sender, prefix + command + " debug [player]");
sendMessage(sender, " - &7displays debug information about player");
sendMessage(sender, prefix + command + " cpu");
sendMessage(sender, " - &7shows CPU usage of the plugin");
sendMessage(sender, prefix + command + " group/player <name> remove");
sendMessage(sender, " - &7Clears all data about player/group");
sendMessage(sender, "&m ");
for (String message : getMessages().getHelpMenu()) {
if (tab.getPlatform().isProxy()) message = message.replace("/tab", "/btab");
sendMessage(sender, message);
}
}
}
@@ -10,6 +10,7 @@ import me.neznamy.tab.api.bossbar.BossBarManager;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.command.SubCommand;
import me.neznamy.tab.shared.features.bossbar.BossBarLine;
/**
* Handler for "/tab announce bar" subcommand
@@ -25,7 +26,7 @@ public class AnnounceBarCommand extends SubCommand {
@Override
public void execute(TabPlayer sender, String[] args) {
BossBarManager feature = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature("bossbar");
BossBarManager feature = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.BOSS_BAR);
if (feature == null) {
sendMessage(sender, getMessages().getBossBarNotEnabled());
return;
@@ -47,16 +48,21 @@ public class AnnounceBarCommand extends SubCommand {
sendMessage(sender, getMessages().getBossBarNotFound(barName));
return;
}
if (!((BossBarLine)bar).isAnnouncementOnly()) {
sendMessage(sender, getMessages().getBossBarNotMarkedAsAnnouncement());
return;
}
if (feature.getAnnouncedBossBars().contains(bar)) {
sendMessage(sender, getMessages().getBossBarAlreadyAnnounced());
return;
}
feature.announceBossBar(bar.getName(), duration);
sendMessage(sender, getMessages().getBossBarAnnouncementSuccess(bar.getName(), duration));
}
@Override
public List<String> complete(TabPlayer sender, String[] arguments) {
BossBarManager b = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature("bossbar");
BossBarManager b = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.BOSS_BAR);
if (b == null) return new ArrayList<>();
List<String> suggestions = new ArrayList<>();
if (arguments.length == 1) {
@@ -25,7 +25,7 @@ public class AnnounceScoreboardCommand extends SubCommand {
@Override
public void execute(TabPlayer sender, String[] args) {
ScoreboardManager feature = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
ScoreboardManager feature = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SCOREBOARD);
if (feature == null) {
sendMessage(sender, getMessages().getScoreboardFeatureNotEnabled());
return;
@@ -52,7 +52,7 @@ public class AnnounceScoreboardCommand extends SubCommand {
@Override
public List<String> complete(TabPlayer sender, String[] arguments) {
ScoreboardManager s = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature("scoreboard");
ScoreboardManager s = (ScoreboardManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SCOREBOARD);
if (s == null) return new ArrayList<>();
List<String> suggestions = new ArrayList<>();
if (arguments.length == 1) {
@@ -10,6 +10,7 @@ import me.neznamy.tab.api.bossbar.BossBarManager;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.TabConstants;
import me.neznamy.tab.shared.command.SubCommand;
import me.neznamy.tab.shared.features.bossbar.BossBarLine;
/**
* Handler for "/tab announce bar" subcommand
@@ -25,7 +26,7 @@ public class SendBarCommand extends SubCommand {
@Override
public void execute(TabPlayer sender, String[] args) {
BossBarManager feature = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature("bossbar");
BossBarManager feature = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.BOSS_BAR);
if (feature == null) {
sendMessage(sender, getMessages().getBossBarNotEnabled());
return;
@@ -52,12 +53,17 @@ public class SendBarCommand extends SubCommand {
sendMessage(sender, getMessages().getBossBarNotFound(barName));
return;
}
if (!((BossBarLine)bar).isAnnouncementOnly()) {
sendMessage(sender, getMessages().getBossBarNotMarkedAsAnnouncement());
return;
}
feature.sendBossBarTemporarily(target, bar.getName(), duration);
sendMessage(sender, getMessages().getBossBarSendSuccess(target.getName(), bar.getName(), duration));
}
@Override
public List<String> complete(TabPlayer sender, String[] arguments) {
BossBarManager b = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature("bossbar");
BossBarManager b = (BossBarManager) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.BOSS_BAR);
if (b == null) return new ArrayList<>();
if (arguments.length == 1) return getOnlinePlayers(arguments[0]);
if (arguments.length == 2) return getStartingArgument(b.getRegisteredBossBars().keySet(), arguments[1]);
@@ -1,25 +1,18 @@
package me.neznamy.tab.shared.config;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.sql.SQLException;
import java.util.*;
import java.util.Map.Entry;
import org.yaml.snakeyaml.error.YAMLException;
import me.neznamy.tab.api.PropertyConfiguration;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.config.YamlConfigurationFile;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.config.file.YamlPropertyConfigurationFile;
import me.neznamy.tab.shared.config.mysql.MySQLGroupConfiguration;
import me.neznamy.tab.shared.config.mysql.MySQLUserConfiguration;
import org.yaml.snakeyaml.error.YAMLException;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
/**
* Core of loading configuration files
@@ -28,10 +21,11 @@ public class Configs {
private final TAB tab;
private final Converter converter = new Converter();
//config.yml file
private ConfigurationFile config;
private String[] removeStrings;
private boolean bukkitPermissions;
//hidden config options
@@ -59,9 +53,6 @@ public class Configs {
private PropertyConfiguration userFile;
private MySQL mysql;
private final List<String> disabledWorld = Collections.singletonList("disabledworld");
private final List<String> disabledServer = Collections.singletonList("disabledServer");
/**
* Constructs new instance with given parameter
@@ -82,12 +73,7 @@ public class Configs {
ClassLoader loader = Configs.class.getClassLoader();
loadConfig();
animation = new YamlConfigurationFile(loader.getResourceAsStream("animations.yml"), new File(tab.getPlatform().getDataFolder(), "animations.yml"));
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.");
}
converter.convertAnimationFile(animation);
messages = new MessageFile();
layout = new YamlConfigurationFile(loader.getResourceAsStream("layout.yml"), new File(tab.getPlatform().getDataFolder(), "layout.yml"));
reloadFailed = messages.getReloadFailBrokenFile();
@@ -102,19 +88,15 @@ public class Configs {
*/
public void loadConfig() throws YAMLException, IOException {
config = new YamlConfigurationFile(Configs.class.getClassLoader().getResourceAsStream(tab.getPlatform().getConfigName()), new File(tab.getPlatform().getDataFolder(), "config.yml"));
if (!config.hasConfigOption("mysql"))
convertToV3();
List<String> list = config.getStringList("placeholders.remove-strings", Arrays.asList("[] ", "< > "));
removeStrings = new String[list.size()];
for (int i=0; i<list.size(); i++) {
removeStrings[i] = EnumChatFormat.color(list.get(i));
}
converter.convertToV3(config);
converter.removeOldOptions(config);
tab.setDebugMode(getConfig().getBoolean("debug", false));
if (tab.getPlatform().isProxy()) {
bukkitPermissions = getConfig().getBoolean("use-bukkit-permissions-manager", false);
} else {
unregisterBeforeRegister = (boolean) getSecretOption("unregister-before-register", true);
armorStandsAlwaysVisible = (boolean) getSecretOption("scoreboard-teams.unlimited-nametag-mode.always-visible", false);
}
unregisterBeforeRegister = (boolean) getSecretOption("unregister-before-register", true);
armorStandsAlwaysVisible = (boolean) getSecretOption("unlimited-nametag-prefix-suffix-mode.always-visible", false);
removeGhostPlayers = (boolean) getSecretOption("remove-ghost-players", false);
pipelineInjection = (boolean) getSecretOption("pipeline-injection", true) && tab.getServerVersion().getMinorVersion() >= 8;
if (config.getBoolean("mysql.enabled", false)) {
@@ -144,10 +126,6 @@ public class Configs {
return value == null ? defaultValue : value;
}
public String[] getRemoveStrings() {
return removeStrings;
}
public boolean isUnregisterBeforeRegister() {
return unregisterBeforeRegister;
}
@@ -227,260 +205,4 @@ public class Configs {
}
return element;
}
public void convertToV3() throws YAMLException, IOException {
config.setValues(new HashMap<>());
Map<String, ConfigurationFile> yamls = new HashMap<>();
File folder = tab.getPlatform().getDataFolder();
String path = folder.getPath();
File premiumcfg = new File(tab.getPlatform().getDataFolder(), "premiumconfig.yml");
if (!premiumcfg.exists()) {
Files.copy(Configs.class.getClassLoader().getResourceAsStream("premiumconfig.yml"), premiumcfg.toPath());
}
for (File file : folder.listFiles()) if (!checkFiles(file,yamls)) return;
Files.createFile(Paths.get(path+File.separator+"config.yml"));
if (!Files.exists(Paths.get(path+File.separator+"groups.yml")))
Files.createFile(Paths.get(path+File.separator+"groups.yml"));
if (!Files.exists(Paths.get(path+File.separator+"users.yml")))
Files.createFile(Paths.get(path+File.separator+"users.yml"));
yamls.put("finalConfig", new YamlConfigurationFile(null, new File(path+File.separator+"config.yml")));
yamls.put("groups.yml", new YamlConfigurationFile(null, new File(path+File.separator+"groups.yml")));
yamls.put("users.yml", new YamlConfigurationFile(null, new File(path+File.separator+"users.yml")));
config = yamls.get("finalConfig");
createConfigYml(yamls);
}
public boolean checkFiles(File file, Map<String,ConfigurationFile> yamls) {
if (!file.getName().equals("config.yml") && !file.getName().equals("premiumconfig.yml") && !file.getName().equals("bossbar.yml") && !file.getName().equals("translation.yml"))
return true;
try {
File oldFolder = new File(file.getParent()+File.separator+"old_configs");
if (!oldFolder.exists()) oldFolder.mkdir();
Path oldConfig = Paths.get(file.getParent()+File.separator+"old_configs"+File.separator+file.getName());
Files.copy(file.toPath(), oldConfig, StandardCopyOption.REPLACE_EXISTING);
yamls.put(file.getName(), new YamlConfigurationFile(null,file));
Files.delete(file.toPath());
return true;
} catch (Exception ex) {
return false;
}
}
private void createConfigYml(Map<String, ConfigurationFile> yamls) throws YAMLException, IOException {
tab.sendConsoleMessage("&e[TAB] --------------------------------------------------------------",true);
tab.sendConsoleMessage("&e[TAB] Performing configuration conversion from 2.9.2 to 3.0.0",true);
tab.sendConsoleMessage("&e[TAB] Please note that this may not be 100% accurate",true);
tab.sendConsoleMessage("&e[TAB] Review your configuration and verify everything is as you want it to be",true);
tab.sendConsoleMessage("&e[TAB] --------------------------------------------------------------",true);
String worldOrServer = tab.getPlatform().isProxy() ? "server" : "world";
boolean isProxy = "server".equals(worldOrServer);
ConfigurationFile premium = yamls.get("premiumconfig.yml");
ConfigurationFile bossbar = yamls.get("bossbar.yml");
ConfigurationFile config = yamls.get("config.yml");
ConfigurationFile finalCfg = yamls.get("finalConfig");
finalCfg.set("scoreboard-teams.enabled",config.getBoolean("change-nametag-prefix-suffix",true));
finalCfg.set("scoreboard-teams.invisible-nametags",config.getBoolean("invisible-nametags",false));
finalCfg.set("scoreboard-teams.anti-override",config.getBoolean("anti-override.scoreboard-teams",true));
finalCfg.set("scoreboard-teams.enable-collision",config.getBoolean("enable-collision",true));
List<String> sortingtypes = new ArrayList<>();
String sortingtype = premium.getString("sorting-type");
if (sortingtype == null)
sortingtypes = Arrays.asList("GROUPS:owner,admin,mod,helper,builder,vip,default","PLACEHOLDER_A_TO_Z:%player%");
else {
for (String type : sortingtype.split("_THEN_")) {
if (type.equalsIgnoreCase("GROUPS") || type.equalsIgnoreCase("GROUP_PERMISSIONS")) {
List<String> sortinglist = config.getStringList("group-sorting-priority-list", Arrays.asList("owner", "admin", "mod", "helper", "builder", "premium", "player", "default"));
StringBuilder groups = new StringBuilder(("GROUP_PERMISSIONS".equals(type) ? "PERMISSIONS" : "GROUPS") + ":");
for (String group : sortinglist) {
groups.append(("GROUP_PERMISSIONS".equals(type) ? "tab.sort." : ""));
groups.append(group.replace(" ", "|"));
if (sortinglist.indexOf(group) != sortinglist.size() - 1)
groups.append(",");
}
sortingtypes.add(groups.toString());
}
else if (type.equalsIgnoreCase("PLACEHOLDER")) {
List<String> sortinglist = config.getStringList("placeholder-order", Arrays.asList("value1", "value2"));
String placeholder = premium.getString("sorting-placeholder", "%some_level_maybe?%");
StringBuilder groups = new StringBuilder("PLACEHOLDER:"+placeholder+":");
for (String group : sortinglist) {
groups.append(group);
if (sortinglist.indexOf(group) != sortinglist.size() - 1)
groups.append(",");
}
sortingtypes.add(groups.toString());
}
else {
String placeholder = premium.getString("sorting-placeholder", "%some_level_maybe?%");
if (!placeholder.equalsIgnoreCase("%some_level_maybe?%"))
sortingtypes.add(type+":"+placeholder);
}
}
}
sortingtypes.add("PLACEHOLDER_A_TO_Z:%player%");
finalCfg.set("scoreboard-teams.sorting-types",sortingtypes);
finalCfg.set("scoreboard-teams.case-sensitive-sorting",premium.getBoolean("case-sensitive-sorting",true));
finalCfg.set("scoreboard-teams.disable-in-worlds",config.getStringList("disable-features-in-worlds.nametag",disabledWorld));
if (isProxy)
finalCfg.set("scoreboard-teams.disable-in-servers",config.getStringList("disable-features-in-servers.nametag",disabledServer));
finalCfg.set("scoreboard-teams.unlimited-nametag-mode.enabled",config.getBoolean("unlimited-nametag-prefix-suffix-mode.enabled",false));
finalCfg.set("scoreboard-teams.unlimited-nametag-mode.use-marker-tag-for-1-8-x-clients",config.getBoolean("unlimited-nametag-prefix-suffix-mode.use-marker-tag-for-1-8-x-clients",false));
finalCfg.set("scoreboard-teams.unlimited-nametag-mode.disable-on-boats",config.getBoolean("unlimited-nametag-prefix-suffix-mode.disable-on-boats",true));
finalCfg.set("scoreboard-teams.unlimited-nametag-mode.space-between-lines",config.getBoolean("unlimited-nametag-prefix-suffix-mode.space-between-lines",true));
finalCfg.set("scoreboard-teams.unlimited-nametag-mode.disable-in-worlds",config.getStringList("disable-features-in-worlds.unlimited-nametags",disabledWorld));
finalCfg.set("scoreboard-teams.unlimited-nametag-mode.dynamic-lines",premium.getStringList("unlimited-nametag-mode-dynamic-lines",Arrays.asList("abovename","nametag","belowname","another")));
finalCfg.set("scoreboard-teams.unlimited-nametag-mode.static-lines",premium.getConfigurationSection("unlimited-nametag-mode-static-lines"));
finalCfg.set("tablist-name-formatting.enabled", config.getBoolean("change-tablist-prefix-suffix",true));
finalCfg.set("tablist-name-formatting.align-tabsuffix-on-the-right", premium.getBoolean("align-tabsuffix-on-the-right",false));
finalCfg.set("tablist-name-formatting.character-width-overrides", premium.getConfigurationSection("character-width-overrides"));
finalCfg.set("tablist-name-formatting.anti-override", config.getBoolean("anti-override.tablist-names",true));
finalCfg.set("tablist-name-formatting.disable-in-worlds", config.getStringList("disable-features-in-worlds.tablist-names",disabledWorld));
if (isProxy)
finalCfg.set("tablist-name-formatting.disable-in-servers",config.getStringList("disable-features-in-servers.tablist-names",disabledServer));
finalCfg.set("header-footer.enabled", config.getBoolean("enable-header-footer",true));
finalCfg.set("header-footer.disable-in-worlds", config.getStringList("disable-features-in-worlds.header-footer",disabledWorld));
if (isProxy)
finalCfg.set("header-footer.disable-in-servers",premium.getStringList("disable-features-in-servers.header-footer",disabledServer));
finalCfg.set("header-footer.header", config.getStringList("header"));
finalCfg.set("header-footer.footer", config.getStringList("footer"));
finalCfg.set("yellow-number-in-tablist.enabled", !config.getString("yellow-number-in-tablist","%ping%").equals(""));
finalCfg.set("yellow-number-in-tablist.value", config.getString("yellow-number-in-tablist","%ping%"));
finalCfg.set("yellow-number-in-tablist.disable-in-worlds", config.getStringList("disable-features-in-worlds.yellow-number",disabledWorld));
if (isProxy)
finalCfg.set("yellow-number.disable-in-servers",config.getStringList("disable-features-in-servers.yellow-number",disabledServer));
finalCfg.set("belowname-objective", config.getConfigurationSection("classic-vanilla-belowname"));
finalCfg.set("belowname-objective.disable-in-worlds", config.getStringList("disable-features-in-worlds.belowname",disabledWorld));
if (isProxy)
finalCfg.set("belowname-objective.disable-in-servers",config.getStringList("disable-features-in-servers.belowname",disabledServer));
finalCfg.set("prevent-spectator-effect.enabled", config.getBoolean("do-not-move-spectators",false));
finalCfg.set("bossbar.enabled", bossbar.getBoolean("bossbar-enabled",false));
finalCfg.set("bossbar.toggle-command", bossbar.getString("bossbar-toggle-command","/bossbar"));
finalCfg.set("bossbar.remember-toggle-choice", bossbar.getBoolean("remember-toggle-choice",false));
finalCfg.set("bossbar.hidden-by-default", bossbar.getBoolean("hidden-by-default",false));
finalCfg.set("bossbar.default-bars", bossbar.getStringList("default-bars",Arrays.asList("ServerInfo","tpsbar")));
finalCfg.set("bossbar.disable-in-worlds", bossbar.getObject("disable-features-in-worlds.bossbar"));
if (isProxy)
finalCfg.set("bossbar.disable-in-servers",config.getStringList("disable-features-in-servers.bossbar",disabledServer));
Map<String,Map<String,String>> bossbars = bossbar.getConfigurationSection("bars");
Map<String,List<String>> perworldBossbars = bossbar.getConfigurationSection("per-world");
if (perworldBossbars != null) {
for (Entry<String, List<String>> entry : perworldBossbars.entrySet()) {
String world = entry.getKey();
for (String bar : entry.getValue()) {
if (!bossbars.containsKey(bar)) continue;
if (bossbars.get(bar).containsKey("display-condition"))
bossbars.get(bar).put("display-condition", bossbars.get(bar).get("display-condition")+";%"+worldOrServer+"%="+world);
else bossbars.get(bar).put("display-condition", "%"+worldOrServer+"%="+world);
}
}
}
finalCfg.set("bossbar.bars", bossbars);
if (!isProxy)
finalCfg.set("per-world-playerlist", config.getConfigurationSection("per-world-playerlist"));
finalCfg.set("scoreboard", premium.getObject("scoreboard"));
finalCfg.set("scoreboard.permission-required-to-toggle", null);
Map<String,Map<String,Object>> scoreboards = premium.getConfigurationSection("scoreboards");
Map<String,String> perworldScoreboards = premium.getConfigurationSection("scoreboard.per-world");
finalCfg.set("scoreboard.default-scoreboard", null);
finalCfg.set("scoreboard.per-world", null);
if (perworldScoreboards != null)
perworldScoreboards.forEach((world,sb)->{
if (!scoreboards.containsKey(sb)) return;
if (scoreboards.get(sb).containsKey("display-condition"))
scoreboards.get(sb).put("display-condition", scoreboards.get(sb).get("display-condition")+";%"+worldOrServer+"%="+world);
else scoreboards.get(sb).put("display-condition", "%"+worldOrServer+"%="+world);
});
finalCfg.set("scoreboard.scoreboards", scoreboards);
finalCfg.set("ping-spoof", config.getConfigurationSection("ping-spoof"));
finalCfg.set("fix-pet-names.enabled", config.getBoolean("fix-pet-names",false));
Map<String,Object> placeholders = config.getConfigurationSection("placeholders");
String afkyes = placeholders.remove("afk-yes")+"";
String afkno = placeholders.remove("afk-no")+"";
finalCfg.set("placeholders", placeholders);
finalCfg.set("placeholder-output-replacements", premium.getConfigurationSection("placeholder-output-replacements"));
finalCfg.set("placeholder-output-replacements.%afk%.true", afkyes);
finalCfg.set("placeholder-output-replacements.%afk%.false", afkno);
finalCfg.set("conditions",premium.getConfigurationSection("conditions"));
finalCfg.set("placeholderapi-refresh-intervals", config.getConfigurationSection("placeholderapi-refresh-intervals"));
finalCfg.set("assign-groups-by-permissions", config.getBoolean("assign-groups-by-permissions",false));
finalCfg.set("primary-group-finding-list", config.getStringList("primary-group-finding-list",Arrays.asList("Owner","Admin","Mod","Helper","default")));
finalCfg.set("debug", config.getBoolean("debug", false));
finalCfg.set("mysql.enabled", false);
finalCfg.set("mysql.host", "127.0.0.1");
finalCfg.set("mysql.port", 3306);
finalCfg.set("mysql.database", "tab");
finalCfg.set("mysql.username", "user");
finalCfg.set("mysql.password", "password");
ConfigurationFile groups = yamls.get("groups.yml");
ConfigurationFile users = yamls.get("users.yml");
groups.setValues(config.getConfigurationSection("Groups"));
users.setValues(config.getConfigurationSection("Users"));
Map<String,Map<String,Object>> perworldsettings = config.getConfigurationSection("per-"+worldOrServer+"-settings");
Map<String,Object> groupMap = new HashMap<>();
Map<String,Object> userMap = new HashMap<>();
Map<String,Object> headerFooterMap = new HashMap<>();
if (perworldsettings != null) {
Map<String,Map<String,Object>> worldMap = new HashMap<>(perworldsettings);
for (Entry<String, Map<String, Object>> entry : worldMap.entrySet()) {
String world = entry.getKey();
Map<String, Object> map = new HashMap<>(entry.getValue());
Map<String,Object> headerFooter = new HashMap<>();
for (Entry<String, Object> entry2 : map.entrySet()) {
String value = entry2.getKey();
if (value.equalsIgnoreCase("Groups"))
groupMap.put(world,entry2.getValue());
else if (value.equalsIgnoreCase("Users"))
userMap.put(world,entry2.getValue());
else if (value.equalsIgnoreCase("header") || value.equalsIgnoreCase("footer"))
headerFooter.put(value,entry2.getValue());
}
headerFooterMap.put(world,headerFooter);
if (entry.getValue().isEmpty())
perworldsettings.remove(world);
}
}
groups.set("per-"+worldOrServer, groupMap);
groups.set("_DEFAULT_",groups.getConfigurationSection("_OTHER_"));
groups.set("_OTHER_",null);
users.set("per-"+worldOrServer, userMap);
for (Object world : groups.getConfigurationSection("per-"+worldOrServer).keySet()) {
String gPath = "per-"+worldOrServer+"."+world;
if (!groups.hasConfigOption(gPath+"._OTHER_")) continue;
groups.set(gPath+"._DEFAULT_",groups.getObject(gPath+"._OTHER_"));
groups.set(gPath+"._OTHER_",null);
}
finalCfg.set("header-footer.per-"+worldOrServer, headerFooterMap);
Files.delete(premium.getFile().toPath());
}
}
@@ -0,0 +1,384 @@
package me.neznamy.tab.shared.config;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.config.YamlConfigurationFile;
import me.neznamy.tab.shared.TAB;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.*;
/**
* Configuration converter that converts configuration files from practically
* any previous version of TAB to the current format.
*/
public class Converter {
/**
* Creates new instance of this class
*/
public Converter() {
}
/**
* Converts animation file from old format with everything under "animations" key
* to new format which does not use the redundant key anymore.
* <p>
* The method contains a check if the conversion is necessary, so it is always safe
* to call this method, even if conversion is not needed.
* <p>
* This change was made from 2.8.10 -> 2.9.0
* @param animations
* animation file to convert
*/
public void convertAnimationFile(ConfigurationFile animations) {
if (animations.getValues().size() == 1 && animations.getValues().containsKey("animations")) {
animations.setValues(animations.getConfigurationSection("animations"));
animations.save();
TAB.getInstance().getPlatform().sendConsoleMessage("&2[TAB] Converted animations.yml to new format.", true);
}
}
/**
* Converts all configuration files from 2.9.2 into new 3.0.0 format and saves a copy
* of old files into a new "old_configs" folder.
* @param currentConfig
* currently detected config.yml file
* @throws IOException
* if an I/O operation with the files fails
*/
public void convertToV3(ConfigurationFile currentConfig) throws IOException {
if (currentConfig.hasConfigOption("mysql")) return;
TAB.getInstance().sendConsoleMessage("&e[TAB] --------------------------------------------------------------",true);
TAB.getInstance().sendConsoleMessage("&e[TAB] Performing configuration conversion from 2.9.2 to 3.0.0",true);
TAB.getInstance().sendConsoleMessage("&e[TAB] Please note that this may not be 100% accurate",true);
TAB.getInstance().sendConsoleMessage("&e[TAB] Review your configuration and verify everything is as you want it to be",true);
TAB.getInstance().sendConsoleMessage("&e[TAB] --------------------------------------------------------------",true);
File folder = TAB.getInstance().getPlatform().getDataFolder();
moveOldFiles();
Files.createFile(new File(folder, "groups.yml").toPath());
Files.createFile(new File(folder, "users.yml").toPath());
Files.createFile(new File(folder, "config.yml").toPath());
ConfigurationFile groups = new YamlConfigurationFile(null, new File(folder, "groups.yml"));
ConfigurationFile users = new YamlConfigurationFile(null, new File(folder, "users.yml"));
File oldConfigsFolder = new File(folder, "old_configs");
Files.copy(new File(oldConfigsFolder, "animations.yml").toPath(), new File(folder, "animations.yml").toPath());
File premiumFile = new File(oldConfigsFolder, "premiumconfig.yml");
ConfigurationFile premiumConfig = premiumFile.exists() ? new YamlConfigurationFile(null, premiumFile) : null;
File bossBarFile = new File(oldConfigsFolder, "bossbar.yml");
if (!bossBarFile.exists()) throw new IllegalStateException("Failed to convert configuration to v3: File bossbar.yml does not exist");
ConfigurationFile bossBar = new YamlConfigurationFile(null, bossBarFile);
ConfigurationFile oldConfig = new YamlConfigurationFile(null, new File(oldConfigsFolder, "config.yml"));
ConfigurationFile newConfig = new YamlConfigurationFile(null, new File(folder, "config.yml"));
convertHeaderFooter(oldConfig, newConfig);
convertTabListFormatting(oldConfig, newConfig, premiumConfig);
convertTeamOptions(oldConfig, newConfig, premiumConfig);
convertYellowNumber(oldConfig, newConfig);
convertBelowName(oldConfig, newConfig);
convertBossBar(bossBar, oldConfig, newConfig);
if (premiumConfig != null) {
convertScoreboard(newConfig, premiumConfig);
} else {
createDefaultScoreboard(newConfig);
}
convertOtherOptions(oldConfig, newConfig, premiumConfig);
convertGroupsAndUsers(oldConfig, groups, users);
currentConfig.setValues(newConfig.getValues());
}
/**
* Creates a new "old_configs" folder and moves all files in TAB's folder into it.
* @throws IOException
* if thrown by file move operation
*/
private void moveOldFiles() throws IOException {
File folder = TAB.getInstance().getPlatform().getDataFolder();
File oldFolder = new File(folder, "old_configs");
if (!oldFolder.exists() && !oldFolder.mkdirs()) throw new RuntimeException("Failed to convert configuration to v3: Failed to create old_configs folder");
File[] files = folder.listFiles();
if (files == null) throw new IllegalStateException("Failed to convert configuration to v3: old_configs is not a folder");
for (File file : files) {
if (!file.isFile()) continue; //old_configs folder
Files.move(file.toPath(), new File(folder.getPath() +
File.separator + "old_configs" + File.separator + file.getName()).toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
private void convertTeamOptions(ConfigurationFile oldConfig, ConfigurationFile newConfig, ConfigurationFile premiumConfig) {
newConfig.set("scoreboard-teams.enabled", oldConfig.getBoolean("change-nametag-prefix-suffix", true));
newConfig.set("scoreboard-teams.invisible-nametags", oldConfig.getBoolean("invisible-nametags", false));
newConfig.set("scoreboard-teams.anti-override", oldConfig.getBoolean("anti-override.scoreboard-teams", true));
newConfig.set("scoreboard-teams.enable-collision", oldConfig.getBoolean("enable-collision", true));
newConfig.set("scoreboard-teams.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.nametag", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy()) {
newConfig.set("scoreboard-teams.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.nametag", Collections.singletonList("disabledserver")));
} else {
newConfig.set("scoreboard-teams.unlimited-nametag-mode.enabled", oldConfig.getBoolean("unlimited-nametag-prefix-suffix-mode.enabled", false));
newConfig.set("scoreboard-teams.unlimited-nametag-mode.use-marker-tag-for-1-8-x-clients", oldConfig.getBoolean("unlimited-nametag-prefix-suffix-mode.use-marker-tag-for-1-8-x-clients", false));
newConfig.set("scoreboard-teams.unlimited-nametag-mode.disable-on-boats", oldConfig.getBoolean("unlimited-nametag-prefix-suffix-mode.disable-on-boats", true));
newConfig.set("scoreboard-teams.unlimited-nametag-mode.space-between-lines", oldConfig.getBoolean("unlimited-nametag-prefix-suffix-mode.space-between-lines", true));
newConfig.set("scoreboard-teams.unlimited-nametag-mode.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.unlimited-nametags", Collections.singletonList("disabledworld")));
}
String sortingType;
String sortingPlaceholder;
List<String> placeholderOrder = new ArrayList<>();
if (premiumConfig != null) {
newConfig.set("scoreboard-teams.case-sensitive-sorting", premiumConfig.getBoolean("case-sensitive-sorting", true));
newConfig.set("scoreboard-teams.unlimited-nametag-mode.dynamic-lines", premiumConfig.getStringList("unlimited-nametag-mode-dynamic-lines", Arrays.asList("abovename","nametag","belowname","another")));
newConfig.set("scoreboard-teams.unlimited-nametag-mode.static-lines", premiumConfig.getConfigurationSection("unlimited-nametag-mode-static-lines"));
sortingType = premiumConfig.getString("sorting-type", "GROUPS");
sortingPlaceholder = premiumConfig.getString("sorting-placeholder", "%some_level_maybe?%");
placeholderOrder = premiumConfig.getStringList("placeholder-order", Arrays.asList("value1", "value2"));
} else {
newConfig.set("scoreboard-teams.case-sensitive-sorting", true);
newConfig.set("scoreboard-teams.unlimited-nametag-mode.dynamic-lines", Arrays.asList("abovename","nametag","belowname","another"));
newConfig.set("scoreboard-teams.unlimited-nametag-mode.static-lines", new HashMap<String, Object>(){{put("myCustomLine", 0.66);}});
sortingType = oldConfig.getBoolean("sort-players-by-permissions", false) ? "GROUP_PERMISSIONS" : "GROUPS";
sortingPlaceholder = "";
}
List<String> sortingTypes = new ArrayList<>();
for (String type : sortingType.split("_THEN_")) {
if (type.equalsIgnoreCase("GROUPS") || type.equalsIgnoreCase("GROUP_PERMISSIONS")) {
List<String> sortingList = oldConfig.getStringList("group-sorting-priority-list", Arrays.asList("owner", "admin", "mod", "helper", "builder", "premium", "player", "default"));
StringBuilder groups = new StringBuilder(("GROUP_PERMISSIONS".equals(type) ? "PERMISSIONS" : "GROUPS") + ":");
for (String group : sortingList) {
groups.append(("GROUP_PERMISSIONS".equals(type) ? "tab.sort." : ""));
groups.append(group.replace(" ", "|"));
if (sortingList.indexOf(group) != sortingList.size() - 1)
groups.append(",");
}
sortingTypes.add(groups.toString());
} else if (type.equalsIgnoreCase("PLACEHOLDER")) {
sortingTypes.add("PLACEHOLDER:" + sortingPlaceholder + ":" + String.join(",", placeholderOrder));
} else {
sortingTypes.add(type + ":" + sortingPlaceholder);
}
}
sortingTypes.add("PLACEHOLDER_A_TO_Z:%player%");
newConfig.set("scoreboard-teams.sorting-types", sortingTypes);
}
private void convertTabListFormatting(ConfigurationFile oldConfig, ConfigurationFile newConfig, ConfigurationFile premiumConfig) {
newConfig.set("tablist-name-formatting.enabled", oldConfig.getBoolean("change-tablist-prefix-suffix", true));
newConfig.set("tablist-name-formatting.anti-override", oldConfig.getBoolean("anti-override.tablist-names", true));
newConfig.set("tablist-name-formatting.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.tablist-names", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy())
newConfig.set("tablist-name-formatting.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.tablist-names", Collections.singletonList("disabledserver")));
if (premiumConfig != null) {
newConfig.set("tablist-name-formatting.align-tabsuffix-on-the-right", premiumConfig.getBoolean("align-tabsuffix-on-the-right", false));
newConfig.set("tablist-name-formatting.character-width-overrides", premiumConfig.getConfigurationSection("character-width-overrides"));
} else {
newConfig.set("tablist-name-formatting.align-tabsuffix-on-the-right", false);
newConfig.set("tablist-name-formatting.character-width-overrides", new HashMap<Integer, Integer>());
}
}
private void convertYellowNumber(ConfigurationFile oldConfig, ConfigurationFile newConfig) {
newConfig.set("yellow-number-in-tablist.enabled", !oldConfig.getString("yellow-number-in-tablist","%ping%").equals(""));
newConfig.set("yellow-number-in-tablist.value", oldConfig.getString("yellow-number-in-tablist","%ping%"));
newConfig.set("yellow-number-in-tablist.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.yellow-number", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy())
newConfig.set("yellow-number.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.yellow-number", Collections.singletonList("disabledserver")));
}
private void convertBelowName(ConfigurationFile oldConfig, ConfigurationFile newConfig) {
newConfig.set("belowname-objective", oldConfig.getConfigurationSection("classic-vanilla-belowname"));
newConfig.set("belowname-objective.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.belowname", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy())
newConfig.set("belowname-objective.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.belowname", Collections.singletonList("disabledserver")));
}
private void convertBossBar(ConfigurationFile bossBar, ConfigurationFile oldConfig, ConfigurationFile newConfig) {
newConfig.set("bossbar.enabled", bossBar.getBoolean("bossbar-enabled", false));
newConfig.set("bossbar.toggle-command", bossBar.getString("bossbar-toggle-command", "/bossbar"));
newConfig.set("bossbar.remember-toggle-choice", bossBar.getBoolean("remember-toggle-choice", false));
newConfig.set("bossbar.hidden-by-default", bossBar.getBoolean("hidden-by-default", false));
newConfig.set("bossbar.disable-in-worlds", bossBar.getObject("disable-features-in-worlds.bossbar"));
if (TAB.getInstance().getPlatform().isProxy())
newConfig.set("bossbar.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.bossbar", Collections.singletonList("disabledserver")));
Map<String, Map<String, Object>> bars = bossBar.getConfigurationSection("bars");
Map<String, List<String>> perWorldBossBars = bossBar.getConfigurationSection("per-world");
List<String> activeBossBars = new ArrayList<>(bossBar.getStringList("default-bars", new ArrayList<>()));
String separator = TAB.getInstance().getPlatform().isProxy() ? "server" : "world";
if (perWorldBossBars != null) {
for (Map.Entry<String, List<String>> entry : perWorldBossBars.entrySet()) {
for (String bar : entry.getValue()) {
if (!bars.containsKey(bar)) continue;
activeBossBars.add(bar);
if (bars.get(bar).containsKey("display-condition")){
bars.get(bar).put("display-condition", bars.get(bar).get("display-condition") + ";%" + separator + "%=" + entry.getKey());
} else {
bars.get(bar).put("display-condition", "%" + separator + "%=" + entry.getKey());
}
}
}
}
for (String definedBossBar : bars.keySet()) {
bars.get(definedBossBar).put("announcement-bar", !activeBossBars.contains(definedBossBar));
}
newConfig.set("bossbar.bars", bars);
}
private void convertScoreboard(ConfigurationFile newConfig, ConfigurationFile premiumConfig) {
String separator = TAB.getInstance().getPlatform().isProxy() ? "server" : "world";
newConfig.set("scoreboard", premiumConfig.getObject("scoreboard"));
newConfig.set("scoreboard.permission-required-to-toggle", null);
Map<String, Map<String,Object>> scoreboards = premiumConfig.getConfigurationSection("scoreboards");
Map<String, String> perWorldScoreboards = premiumConfig.getConfigurationSection("scoreboard.per-world");
newConfig.set("scoreboard.default-scoreboard", null);
newConfig.set("scoreboard.per-world", null);
for (Map.Entry<String, String> entry : perWorldScoreboards.entrySet()) {
String world = entry.getKey();
String sb = entry.getValue();
if (!scoreboards.containsKey(sb)) continue;
Map<String, Object> scoreboard = scoreboards.get(sb);
if (scoreboard.containsKey("display-condition")) {
scoreboard.put("display-condition", scoreboards.get(sb).get("display-condition") + ";%" + separator + "%=" + world);
} else {
scoreboard.put("display-condition", "%" + separator + "%=" + world);
//move to the top, so it's actually displayed with new priority system
scoreboards.remove(sb);
Map<String, Map<String, Object>> reordered = new HashMap<>();
reordered.put(sb, scoreboard);
reordered.putAll(scoreboards);
scoreboards = reordered;
}
}
newConfig.set("scoreboard.scoreboards", scoreboards);
}
private void createDefaultScoreboard(ConfigurationFile newConfig) {
newConfig.set("scoreboard.enabled", false);
newConfig.set("scoreboard.toggle-command", "/sb");
newConfig.set("scoreboard.remember-toggle-choice", false);
newConfig.set("scoreboard.hidden-by-default", false);
newConfig.set("scoreboard.use-numbers", false);
newConfig.set("scoreboard.static-number", 0);
newConfig.set("scoreboard.delay-on-join-milliseconds", 0);
newConfig.set("scoreboard.respect-other-plugins", true);
newConfig.set("scoreboard.disable-in-worlds", Collections.singletonList("disabledworld"));
if (TAB.getInstance().getPlatform().isProxy()) {
newConfig.set("scoreboard.disable-in-servers", Collections.singletonList("disabledserver"));
}
newConfig.set("scoreboard.scoreboards.admin.display-condition", "permission:tab.scoreboard.admin");
newConfig.set("scoreboard.scoreboards.admin.title", "Admin scoreboard");
newConfig.set("scoreboard.scoreboards.admin.lines", Arrays.asList("%animation:MyAnimation1%", "&6Online:", "* &eOnline&7: &f%online%&7/&4%maxplayers%",
"* &eCurrent World&7: &f%worldonline%", "* &eStaff&7: &f%staffonline%", " ", "&6Server Info:", "* &bTPS&7: %tps%",
"* &bUptime&7: &f%server_uptime%", "* &bMemory&7: &f%memory-used%&7/&4%memory-max%", "%animation:MyAnimation1%"));
newConfig.set("scoreboard.scoreboards.scoreboard1.title", "Default");
newConfig.set("scoreboard.scoreboards.scoreboard1.lines", Arrays.asList("%animation:MyAnimation1%", "&6My Stats:", "* &eKills&7: &f%statistic_player_kills%",
"* &eDeaths&7: &f%statistic_deaths%", "* &eHealth&7: &f%health%", " ", "&6Personal Info:", "* &bRank&7: &f%group%",
"* &bPing&7: &f%ping%&7ms", "* &bWorld&7: &f%world%", "%animation:MyAnimation1%"));
}
private void convertHeaderFooter(ConfigurationFile oldConfig, ConfigurationFile newConfig) {
newConfig.set("header-footer.enabled", oldConfig.getBoolean("enable-header-footer", true));
newConfig.set("header-footer.header", oldConfig.getStringList("header"));
newConfig.set("header-footer.footer", oldConfig.getStringList("footer"));
newConfig.set("header-footer.disable-in-worlds", oldConfig.getStringList("disable-features-in-worlds.header-footer", Collections.singletonList("disabledworld")));
if (TAB.getInstance().getPlatform().isProxy())
newConfig.set("header-footer.disable-in-servers", oldConfig.getStringList("disable-features-in-servers.header-footer", Collections.singletonList("disabledserver")));
String separator = TAB.getInstance().getPlatform().isProxy() ? "server" : "world";
Map<String, Map<String, Object>> perWorldSettings = oldConfig.getConfigurationSection("per-" + separator + "-settings");
Map<String, Object> headerFooterMap = new LinkedHashMap<>();
for (Map.Entry<String, Map<String, Object>> worldEntry : new LinkedHashMap<>(perWorldSettings).entrySet()) {
Map<String, Object> headerFooter = new LinkedHashMap<>();
for (Map.Entry<String, Object> propertyValueEntry : new LinkedHashMap<>(worldEntry.getValue()).entrySet()) {
if (propertyValueEntry.getKey().equalsIgnoreCase("header") || propertyValueEntry.getKey().equalsIgnoreCase("footer"))
headerFooter.put(propertyValueEntry.getKey(), propertyValueEntry.getValue());
}
headerFooterMap.put(translateWorldGroup(oldConfig, worldEntry.getKey()), headerFooter);
}
newConfig.set("header-footer.per-" + separator, headerFooterMap);
}
private void convertOtherOptions(ConfigurationFile oldConfig, ConfigurationFile newConfig, ConfigurationFile premiumConfig) {
newConfig.set("ping-spoof", oldConfig.getConfigurationSection("ping-spoof"));
newConfig.set("prevent-spectator-effect.enabled", oldConfig.getBoolean("do-not-move-spectators",false));
Map<String,Object> placeholders = oldConfig.getConfigurationSection("placeholders");
if (premiumConfig != null) {
newConfig.set("placeholder-output-replacements", premiumConfig.getConfigurationSection("placeholder-output-replacements"));
newConfig.set("conditions", premiumConfig.getConfigurationSection("conditions"));
} else {
newConfig.set("placeholder-output-replacements.%essentials_vanished%.yes", "&7| Vanished");
newConfig.set("placeholder-output-replacements.%essentials_vanished%.no", "");
newConfig.set("conditions.nick.conditions", Collections.singletonList("%player%=%essentials_nickname%"));
newConfig.set("conditions.nick.yes", "%player%");
newConfig.set("conditions.nick.no", "~%essentials_nickname%");
}
if (!TAB.getInstance().getPlatform().isProxy()) {
newConfig.set("placeholder-output-replacements.%afk%.true", placeholders.remove("afk-yes"));
newConfig.set("placeholder-output-replacements.%afk%.false", placeholders.remove("afk-no"));
}
newConfig.set("placeholders", placeholders);
newConfig.set("placeholderapi-refresh-intervals", oldConfig.getConfigurationSection("placeholderapi-refresh-intervals"));
newConfig.set("assign-groups-by-permissions", oldConfig.getBoolean("assign-groups-by-permissions", false));
newConfig.set("primary-group-finding-list", oldConfig.getStringList("primary-group-finding-list", Arrays.asList("Owner","Admin","Mod","Helper","default")));
newConfig.set("debug", oldConfig.getBoolean("debug", false));
newConfig.set("mysql.enabled", false);
newConfig.set("mysql.host", "127.0.0.1");
newConfig.set("mysql.port", 3306);
newConfig.set("mysql.database", "tab");
newConfig.set("mysql.username", "user");
newConfig.set("mysql.password", "password");
if (TAB.getInstance().getPlatform().isProxy()) {
newConfig.set("global-playerlist", oldConfig.getConfigurationSection("global-playerlist"));
newConfig.set("global-playerlist.update-latency", false);
newConfig.set("use-bukkit-permissions-manager", false);
} else {
newConfig.set("per-world-playerlist", oldConfig.getConfigurationSection("per-world-playerlist"));
newConfig.set("fix-pet-names.enabled", oldConfig.getBoolean("fix-pet-names", false));
}
}
private void convertGroupsAndUsers(ConfigurationFile oldConfig, ConfigurationFile groups, ConfigurationFile users) {
groups.setValues(oldConfig.getConfigurationSection("Groups"));
users.setValues(oldConfig.getConfigurationSection("Users"));
String separator = TAB.getInstance().getPlatform().isProxy() ? "server" : "world";
Map<String,Map<String,Object>> perWorldSettings = oldConfig.getConfigurationSection("per-" + separator + "-settings");
Map<String,Object> groupMap = new LinkedHashMap<>();
Map<String,Object> userMap = new LinkedHashMap<>();
Map<String,Map<String,Object>> worldMap = new LinkedHashMap<>(perWorldSettings);
for (Map.Entry<String, Map<String, Object>> worldEntry : worldMap.entrySet()) {
for (Map.Entry<String, Object> entry2 : new LinkedHashMap<>(worldEntry.getValue()).entrySet()) {
if (entry2.getKey().equalsIgnoreCase("Groups"))
groupMap.put(translateWorldGroup(oldConfig, worldEntry.getKey()),entry2.getValue());
else if (entry2.getKey().equalsIgnoreCase("Users"))
userMap.put(translateWorldGroup(oldConfig, worldEntry.getKey()),entry2.getValue());
}
}
groups.set("per-" + separator, groupMap);
groups.set("_DEFAULT_", groups.getConfigurationSection("_OTHER_"));
groups.set("_OTHER_", null);
users.set("per-" + separator, userMap);
for (Object world : groups.getConfigurationSection("per-" + separator).keySet()) {
String gPath = "per-" + separator + "." + world;
if (!groups.hasConfigOption(gPath + "._OTHER_")) continue;
groups.set(gPath + "._DEFAULT_", groups.getObject(gPath + "._OTHER_"));
groups.set(gPath + "._OTHER_", null);
}
}
private String translateWorldGroup(ConfigurationFile oldConfig, String group) {
String oldSeparator = oldConfig.hasConfigOption("multi-world-separator") ? oldConfig.getString("multi-world-separator") : "-";
return group.replace(oldSeparator, ";");
}
public void removeOldOptions(ConfigurationFile config) {
if (config.hasConfigOption("placeholders.remove-strings")) {
config.set("placeholders.remove-strings", null);
}
}
}
@@ -2,151 +2,190 @@ package me.neznamy.tab.shared.config;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.yaml.snakeyaml.error.YAMLException;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.api.config.YamlConfigurationFile;
import me.neznamy.tab.shared.TAB;
public class MessageFile {
public class MessageFile extends YamlConfigurationFile {
private final ConfigurationFile file;
public MessageFile() throws YAMLException, IOException {
file = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("messages.yml"), new File(TAB.getInstance().getPlatform().getDataFolder(), "messages.yml"));
super(MessageFile.class.getClassLoader().getResourceAsStream("messages.yml"), new File(TAB.getInstance().getPlatform().getDataFolder(), "messages.yml"));
}
public String getAnnounceCommandUsage() {
return file.getString("announce-command-usage", "Usage: /tab announce <type> <name> <length>\nCurrently supported types: &lbar, scoreboard");
return getString("announce-command-usage", "Usage: /tab announce <type> <name> <length>\nCurrently supported types: &lbar, scoreboard");
}
public String getBossBarNotEnabled() {
return file.getString("bossbar-feature-not-enabled", "&cThis command requires the bossbar feature to be enabled.");
return getString("bossbar-feature-not-enabled", "&cThis command requires the bossbar feature to be enabled.");
}
public String getBossBarAnnounceCommandUsage() {
return file.getString("bossbar-announce-command-usage", "Usage: /tab announce bar <bar name> <length>");
return getString("bossbar-announce-command-usage", "Usage: /tab announce bar <bar name> <length>");
}
public String getBossBarNotFound(String name) {
return file.getString("bossbar-not-found", "&cNo bossbar found with the name \"%name%\"").replace("%name%", name);
return getString("bossbar-not-found", "&cNo bossbar found with the name \"%name%\"").replace("%name%", name);
}
public String getBossBarAlreadyAnnounced() {
return file.getString("bossbar-already-announced", "&cThis bossbar is already being announced");
return getString("bossbar-already-announced", "&cThis bossbar is already being announced");
}
public String getGroupDataRemoved(String group) {
return file.getString("group-data-removed", "&3[TAB] All data has been successfully removed from group &e%group%").replace("%group%", group);
return getString("group-data-removed", "&3[TAB] All data has been successfully removed from group &e%group%").replace("%group%", group);
}
public String getGroupValueAssigned(String property, String value, String group) {
return file.getString("group-value-assigned", "&3[TAB] %property% '&r%value%&r&3' has been successfully assigned to group &e%group%")
return getString("group-value-assigned", "&3[TAB] %property% '&r%value%&r&3' has been successfully assigned to group &e%group%")
.replace("%property%", property).replace("%value%", value).replace("%group%", group);
}
public String getGroupValueRemoved(String property, String group) {
return file.getString("group-value-removed", "&3[TAB] %property% has been successfully removed from group &e%group%")
return getString("group-value-removed", "&3[TAB] %property% has been successfully removed from group &e%group%")
.replace("%property%", property).replace("%group%", group);
}
public String getPlayerDataRemoved(String player) {
return file.getString("user-data-removed", "&3[TAB] All data has been successfully removed from player &e%player%").replace("%player%", player);
return getString("user-data-removed", "&3[TAB] All data has been successfully removed from player &e%player%").replace("%player%", player);
}
public String getPlayerValueAssigned(String property, String value, String player) {
return file.getString("user-value-assigned", "&3[TAB] %property% '&r%value%&r&3' has been successfully assigned to player &e%player%")
return getString("user-value-assigned", "&3[TAB] %property% '&r%value%&r&3' has been successfully assigned to player &e%player%")
.replace("%property%", property).replace("%value%", value).replace("%player%", player);
}
public String getPlayerValueRemoved(String property, String player) {
return file.getString("user-value-removed", "&3[TAB] %property% has been successfully removed from player &e%player%")
return getString("user-value-removed", "&3[TAB] %property% has been successfully removed from player &e%player%")
.replace("%property%", property).replace("%player%", player);
}
public String getParseCommandUsage() {
return file.getString("parse-command-usage", "Usage: /tab parse <player> <placeholder>");
return getString("parse-command-usage", "Usage: /tab parse <player> <placeholder>");
}
public String getSendCommandUsage() {
return file.getString("send-command-usage", "Usage: /tab send <type> <player> <bar name> <length>\nCurrently supported types: &lbar");
return getString("send-command-usage", "Usage: /tab send <type> <player> <bar name> <length>\nCurrently supported types: &lbar");
}
public String getSendBarCommandUsage() {
return file.getString("send-bar-command-usage", "Usage: /tab send bar <player> <bar name> <length>");
return getString("send-bar-command-usage", "Usage: /tab send bar <player> <bar name> <length>");
}
public String getTeamFeatureRequired() {
return file.getString("team-feature-required", "This command requires scoreboard teams feature enabled");
return getString("team-feature-required", "This command requires scoreboard teams feature enabled");
}
public String getCollisionCommandUsage() {
return file.getString("collision-command-ussage", "Usage: /tab setcollision <player> <true/false>");
return getString("collision-command-ussage", "Usage: /tab setcollision <player> <true/false>");
}
public String getNoPermission() {
return file.getString("no-permission", "&cI'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.");
return getString("no-permission", "&cI'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.");
}
public String getCommandOnlyFromGame() {
return file.getString("command-only-from-game", "&cThis command must be ran from the game");
return getString("command-only-from-game", "&cThis command must be ran from the game");
}
public String getPlayerNotFound(String name) {
return file.getString("player-not-online", "&cNo online player found with the name \"%player%\"").replace("%player%", name);
return getString("player-not-online", "&cNo online player found with the name \"%player%\"").replace("%player%", name);
}
public String getUnlimitedNametagModeNotEnabled() {
return file.getString("unlimited-nametag-mode-not-enabled", "&c[TAB] Warning! To make this feature work, you need to enable unlimited-nametag-mode in the config!");
return getString("unlimited-nametag-mode-not-enabled", "&c[TAB] Warning! To make this feature work, you need to enable unlimited-nametag-mode in the config!");
}
public String getInvalidNumber(String input) {
return file.getString("invalid-number", "\"%input%\" is not a number!").replace("%input%", input);
return getString("invalid-number", "\"%input%\" is not a number!").replace("%input%", input);
}
public String getScoreboardFeatureNotEnabled() {
return file.getString("scoreboard-feature-not-enabled", "&4This command requires the scoreboard feature to be enabled.");
return getString("scoreboard-feature-not-enabled", "&4This command requires the scoreboard feature to be enabled.");
}
public String getScoreboardAnnounceCommandUsage() {
return file.getString("scoreboard-announce-command-usage", "Usage: /tab announce scoreboard <scoreboard name> <length>");
return getString("scoreboard-announce-command-usage", "Usage: /tab announce scoreboard <scoreboard name> <length>");
}
public String getScoreboardNotFound(String name) {
return file.getString("scoreboard-not-found", "&cNo scoreboard found with the name \"%name%\"").replace("%name%", name);
return getString("scoreboard-not-found", "&cNo scoreboard found with the name \"%name%\"").replace("%name%", name);
}
public String getNametagPreviewOn() {
return file.getString("nametag-preview-on", "&7Preview mode &aactivated&7.");
return getString("nametag-preview-on", "&7Preview mode &aactivated&7.");
}
public String getNametagPreviewOff() {
return file.getString("nametag-preview-of", "&7Preview mode &3deactivated&7.");
return getString("nametag-preview-of", "&7Preview mode &3deactivated&7.");
}
public String getReloadSuccess() {
return file.getString("reload-success", "&3[TAB] Successfully reloaded");
return getString("reload-success", "&3[TAB] Successfully reloaded");
}
//keeping %file% to replace later since message file may not be available
public String getReloadFailBrokenFile() {
return file.getString("reload-fail-file", "&3[TAB] &4Failed to reload, file %file% has broken syntax. Check console for more info.");
return getString("reload-fail-file", "&3[TAB] &4Failed to reload, file %file% has broken syntax. Check console for more info.");
}
public String getScoreboardOn() {
return file.getString("scoreboard-toggle-on", "&2Scoreboard enabled");
return getString("scoreboard-toggle-on", "&2Scoreboard enabled");
}
public String getScoreboardOff() {
return file.getString("scoreboard-toggle-off", "&7Scoreboard disabled");
return getString("scoreboard-toggle-off", "&7Scoreboard disabled");
}
public String getBossBarOn() {
return file.getString("bossbar-toggle-on", "&2Bossbar is now visible");
return getString("bossbar-toggle-on", "&2Bossbar is now visible");
}
public String getBossBarOff() {
return getString("bossbar-toggle-off", "&7Bossbar is no longer visible. Magic!");
}
public String getScoreboardShowUsage() {
return getString("scoreboard-show-usage", "Usage: /tab scoreboard show <scoreboard> [player]");
}
public String getBossBarNotMarkedAsAnnouncement() {
return getString("bossbar-not-marked-as-announcement", "&cThis bossbar is not marked as an announcement bar and is therefore " +
"already displayed permanently (if display condition is met)");
}
public String getBossBarOff() {
return file.getString("bossbar-toggle-off", "&7Bossbar is no longer visible. Magic!");
public String getBossBarAnnouncementSuccess(String bar, int length) {
return getString("bossbar-announcement-success", "&aAnnouncing bossbar &6%bossbar% &afor %length% seconds.")
.replace("%bossbar%", bar).replace("%length%", String.valueOf(length));
}
public String getBossBarSendSuccess(String player, String bar, int length) {
return getString("bossbar-send-success", "&aSending bossbar &6%bossbar% &ato player &6%player% &afor %length% seconds.")
.replace("%player%", player).replace("%bossbar%", bar).replace("%length%", String.valueOf(length));
}
public List<String> getHelpMenu() {
return getStringList("help-menu", Arrays.asList("&m "
," &8>> &3&l/tab reload"
," &7Reloads plugin and config"
," &8>> &3&l/tab &9group&3/&9player &3<name> &9<property> &3<value...>"
," &7Do &8/tab group/player &7to show properties"
," &8>> &3&l/tab ntpreview"
," &7Shows your nametag for yourself, for testing purposes"
," &8>> &3&l/tab announce bar &3<name> &9<seconds>"
," &7Temporarily displays bossbar to all players"
," &8>> &3&l/tab parse <player> <placeholder> "
," &7Test if a placeholder works"
," &8>> &3&l/tab debug [player]"
," &7displays debug information about player"
," &8>> &3&l/tab cpu"
," &7shows CPU usage of the plugin"
," &8>> &3&l/tab group/player <name> remove"
," &7Clears all data about player/group"
,"&m "));
}
}
@@ -56,18 +56,13 @@ public class MySQL {
}
public void execute(String query, Object... vars) throws SQLException {
if (isConnected()) {
openConnection();
}
try (PreparedStatement ps = prepareStatement(query, vars)){
ps.execute();
}
}
private PreparedStatement prepareStatement(String query, Object... vars) throws SQLException {
if (isConnected()) {
openConnection();
}
if (!isConnected()) openConnection();
PreparedStatement ps = con.prepareStatement(query);
int i = 0;
if (query.contains("?") && vars.length != 0) {
@@ -80,9 +75,6 @@ public class MySQL {
}
public CachedRowSet getCRS(String query, Object... vars) throws SQLException {
if (isConnected()) {
openConnection();
}
PreparedStatement ps = prepareStatement(query, vars);
ResultSet rs = ps.executeQuery();
CachedRowSet crs;
@@ -3,6 +3,8 @@ package me.neznamy.tab.shared.config.mysql;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.WeakHashMap;
import javax.sql.rowset.CachedRowSet;
@@ -15,9 +17,9 @@ public class MySQLUserConfiguration implements PropertyConfiguration {
private final MySQL mysql;
private final Map<String, Map<String, String>> values = new HashMap<>();
private final Map<String, Map<String, Map<String, String>>> perWorld = new HashMap<>();
private final Map<String, Map<String, Map<String, String>>> perServer = new HashMap<>();
private final WeakHashMap<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
private final Map<String, WeakHashMap<TabPlayer, Map<String, String>>> perWorld = new HashMap<>();
private final Map<String, WeakHashMap<TabPlayer, Map<String, String>>> perServer = new HashMap<>();
public MySQLUserConfiguration(MySQL mysql) throws SQLException {
this.mysql = mysql;
@@ -26,12 +28,13 @@ public class MySQLUserConfiguration implements PropertyConfiguration {
@Override
public void setProperty(String user, String property, String server, String world, String value) {
TabPlayer p = getPlayer(user);
user = user.toLowerCase();
try {
if (getProperty(user, property, server, world) != null) {
mysql.execute("delete from `tab_users` where `user` = ? and `property` = ? and world " + querySymbol(world == null) + " ? and server " + querySymbol(server == null) + " ?", user, property, world, server);
}
setProperty0(user, property, server, world, value);
if (p != null) setProperty0(p, property, server, world, value);
if (value != null) mysql.execute("insert into `tab_users` (`user`, `property`, `value`, `world`, `server`) values (?, ?, ?, ?, ?)", user, property, value, world, server);
} catch (SQLException e) {
TAB.getInstance().getErrorManager().printError("Failed to execute MySQL query", e);
@@ -42,12 +45,11 @@ public class MySQLUserConfiguration implements PropertyConfiguration {
return isNull ? "is" : "=";
}
private void setProperty0(String user, String property, String server, String world, String value) {
user = user.toLowerCase();
private void setProperty0(TabPlayer user, String property, String server, String world, String value) {
if (server != null) {
perServer.computeIfAbsent(server, s -> new HashMap<>()).computeIfAbsent(user, g -> new HashMap<>()).put(property, value);
perServer.computeIfAbsent(server, s -> new WeakHashMap<>()).computeIfAbsent(user, g -> new HashMap<>()).put(property, value);
} else if (world != null) {
perWorld.computeIfAbsent(world, w -> new HashMap<>()).computeIfAbsent(user, g -> new HashMap<>()).put(property, value);
perWorld.computeIfAbsent(world, w -> new WeakHashMap<>()).computeIfAbsent(user, g -> new HashMap<>()).put(property, value);
} else {
values.computeIfAbsent(user, g -> new HashMap<>()).put(property, value);
}
@@ -55,15 +57,15 @@ public class MySQLUserConfiguration implements PropertyConfiguration {
@Override
public String[] getProperty(String user, String property, String server, String world) {
user = user.toLowerCase();
TabPlayer p = getPlayer(user);
String value;
if ((value = perServer.getOrDefault(server, new HashMap<>()).getOrDefault(user, new HashMap<>()).get(property)) != null) {
if ((value = perServer.getOrDefault(server, new WeakHashMap<>()).getOrDefault(p, new HashMap<>()).get(property)) != null) {
return new String[] {value, String.format("user=%s,server=%s", user, server)};
}
if ((value = perWorld.getOrDefault(world, new HashMap<>()).getOrDefault(user, new HashMap<>()).get(property)) != null) {
if ((value = perWorld.getOrDefault(world, new WeakHashMap<>()).getOrDefault(p, new HashMap<>()).get(property)) != null) {
return new String[] {value, String.format("user=%s,world=%s", user, world)};
}
if ((value = values.getOrDefault(user, new HashMap<>()).get(property)) != null) {
if ((value = values.getOrDefault(p, new HashMap<>()).get(property)) != null) {
return new String[] {value, String.format("user=%s", user)};
}
return new String[0];
@@ -71,10 +73,28 @@ public class MySQLUserConfiguration implements PropertyConfiguration {
@Override
public void remove(String player) {
final String user = player.toLowerCase();
values.getOrDefault(user, new HashMap<>()).keySet().forEach(property -> setProperty(user, property, null, null, null));
perWorld.keySet().forEach(world -> perWorld.get(world).getOrDefault(user, new HashMap<>()).keySet().forEach(property -> setProperty(user, property, null, world, null)));
perServer.keySet().forEach(server -> perServer.get(server).getOrDefault(user, new HashMap<>()).keySet().forEach(property -> setProperty(user, property, server, null, null)));
try {
mysql.execute("delete from `tab_users` where `user` = ?", player);
} catch (SQLException e) {
TAB.getInstance().getErrorManager().printError("Failed to execute MySQL query", e);
}
TabPlayer user = getPlayer(player);
if (user == null) return;
values.remove(user);
perWorld.keySet().forEach(world -> perWorld.get(world).remove(user));
perServer.keySet().forEach(server -> perServer.get(server).remove(user));
}
private TabPlayer getPlayer(String string) {
TabPlayer p = TAB.getInstance().getPlayer(string);
if (p == null) {
try {
p = TAB.getInstance().getPlayer(UUID.fromString(string));
} catch (IllegalArgumentException ex) {
//not a valid uuid
}
}
return p;
}
public void load(TabPlayer player) {
@@ -89,10 +109,20 @@ public class MySQLUserConfiguration implements PropertyConfiguration {
String world = crs.getString("world");
String server = crs.getString("server");
TAB.getInstance().debug("Loaded user line: " + String.format("%s, %s, %s, %s, %s", user, property, value, world, server));
setProperty0(user, property, server, world, value);
setProperty0(player, property, server, world, value);
}
CachedRowSet crs2 = mysql.getCRS("select * from `tab_users` where `user` = ?", player.getUniqueId().toString());
while (crs2.next()) {
String user = crs2.getString("user");
String property = crs2.getString("property");
String value = crs2.getString("value");
String world = crs2.getString("world");
String server = crs2.getString("server");
TAB.getInstance().debug("Loaded user line: " + String.format("%s, %s, %s, %s, %s", user, property, value, world, server));
setProperty0(player, property, server, world, value);
}
TAB.getInstance().debug("Loaded MySQL data of " + player.getName());
if (crs.size() > 0) {
if (crs.size() > 0 || crs2.size() > 0) {
player.forceRefresh();
}
} catch (SQLException e) {
@@ -100,11 +130,4 @@ public class MySQLUserConfiguration implements PropertyConfiguration {
}
});
}
public void unload(TabPlayer player) {
values.remove(player.getName().toLowerCase());
perWorld.keySet().forEach(world -> perWorld.get(world).remove(player.getName().toLowerCase()));
perServer.keySet().forEach(server -> perServer.get(server).remove(player.getName().toLowerCase()));
TAB.getInstance().debug("Unloaded MySQL data of " + player.getName());
}
}
@@ -0,0 +1,98 @@
package me.neznamy.tab.shared.event;
import java.lang.reflect.Method;
import me.neznamy.tab.api.event.EventBus;
import me.neznamy.tab.api.event.EventHandler;
import me.neznamy.tab.api.event.Subscribe;
import me.neznamy.tab.api.event.TabEvent;
import me.neznamy.tab.shared.TAB;
import net.kyori.event.EventSubscriber;
import net.kyori.event.PostOrders;
import net.kyori.event.PostResult;
import net.kyori.event.SimpleEventBus;
import net.kyori.event.method.MethodHandleEventExecutorFactory;
import net.kyori.event.method.MethodScanner;
import net.kyori.event.method.MethodSubscriptionAdapter;
import net.kyori.event.method.SimpleMethodSubscriptionAdapter;
import org.checkerframework.checker.nullness.qual.NonNull;
public final class EventBusImpl implements EventBus {
private final SimpleEventBus<TabEvent> bus;
private final MethodSubscriptionAdapter<Object> methodAdapter;
public EventBusImpl() {
this.bus = new SimpleEventBus<TabEvent>(TabEvent.class) {
@Override
protected boolean shouldPost(@NonNull TabEvent event, @NonNull EventSubscriber subscriber) {
return true;
}
};
this.methodAdapter = new SimpleMethodSubscriptionAdapter<>(bus, new MethodHandleEventExecutorFactory<>(), new TabMethodScanner());
}
public <E extends TabEvent> void fire(final E event) {
if (!bus.hasSubscribers(event.getClass())) return;
final PostResult result = bus.post(event);
if (result.exceptions().isEmpty()) return;
TAB.getInstance().getErrorManager().printError("Some errors occurred whilst trying to fire event " + event);
int i = 0;
for (final Throwable exception : result.exceptions().values()) {
TAB.getInstance().getErrorManager().printError("#" + i++ + ": \n", exception);
}
}
@Override
public void register(Object listener) {
methodAdapter.register(listener);
}
@Override
public <E extends TabEvent> void register(Class<E> type, EventHandler<E> handler) {
bus.register(type, new HandlerWrapper<>(handler));
}
@Override
public void unregister(Object listener) {
methodAdapter.unregister(listener);
}
@Override
public <E extends TabEvent> void unregister(EventHandler<E> handler) {
bus.unregister(subscriber -> subscriber instanceof HandlerWrapper && ((HandlerWrapper<?>) subscriber).handler == handler);
}
private static final class TabMethodScanner implements MethodScanner<Object> {
@Override
public boolean shouldRegister(@NonNull Object listener, @NonNull Method method) {
return method.isAnnotationPresent(Subscribe.class);
}
@Override
public int postOrder(@NonNull Object listener, @NonNull Method method) {
return PostOrders.NORMAL;
}
@Override
public boolean consumeCancelledEvents(@NonNull Object listener, @NonNull Method method) {
return true;
}
}
private static final class HandlerWrapper<E> implements EventSubscriber<E> {
private final EventHandler<E> handler;
public HandlerWrapper(final EventHandler<E> handler) {
this.handler = handler;
}
@Override
public void invoke(@NonNull E event) {
handler.handle(event);
}
}
}
@@ -0,0 +1,25 @@
package me.neznamy.tab.shared.event.impl;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.event.player.PlayerLoadEvent;
public final class PlayerLoadEventImpl implements PlayerLoadEvent {
private final TabPlayer player;
private final boolean join;
public PlayerLoadEventImpl(TabPlayer player, boolean join) {
this.player = player;
this.join = join;
}
@Override
public TabPlayer getPlayer() {
return player;
}
@Override
public boolean isJoin() {
return join;
}
}
@@ -0,0 +1,15 @@
package me.neznamy.tab.shared.event.impl;
import me.neznamy.tab.api.event.plugin.TabLoadEvent;
public final class TabLoadEventImpl implements TabLoadEvent {
private static final TabLoadEvent INSTANCE = new TabLoadEventImpl();
public static TabLoadEvent getInstance() {
return INSTANCE;
}
private TabLoadEventImpl() {
}
}
Loaded 100 of 153 files, more files were not shown because too many files have changed in this diff. Show more