Compare commits

...
335 Commits
Author SHA1 Message Date
Luck aea44fc8bc API changes for 2.17 - closes #123 2017-01-16 20:01:41 +00:00
Luck c0da9d49d9 make clickable message invert filter more specific 2017-01-16 16:55:59 +00:00
Luck ce42573f43 Properly invalidate user caches when defaults chance - fixes #118 2017-01-15 17:47:59 +00:00
Luck 148fe4c729 Make prefix/suffix string optional in the meta remove commands - related to #83 2017-01-15 15:11:35 +00:00
Luck 32fd484b58 Change promote / demote behaviour at the end of tracks - towards #112 2017-01-15 13:22:50 +00:00
Luck 7d24e748b7 Fix reading and writing UTF-8 characters from storage files - closes #129 2017-01-15 12:18:12 +00:00
Luck f1a1612f5d Subscribe to default permissions too 2017-01-14 23:49:45 +00:00
Luck 6501e5cf8d Fix bukkit permission subscriptions 2017-01-14 23:19:11 +00:00
Luck cd4a684613 Fix demote command not removing old group 2017-01-14 08:27:09 +00:00
Luck d06fda6d9d Add /lp search command 2017-01-13 20:11:25 +00:00
Luck 139010e75b Fix repo 2017-01-12 21:59:38 +00:00
Luck 7638b07e34 bump fanciful version 2017-01-12 21:48:16 +00:00
Luck f7bf4416c9 Fix illegal cast from LPSubject to SpongeGroup - fixes #126 2017-01-12 21:19:34 +00:00
Luck 2b96b656a9 constants do not need getters 2017-01-12 21:14:27 +00:00
Luck 0fe251b6ec Fix promote/demote commands and primary groups conflicting 2017-01-12 19:45:44 +00:00
Luck a1afc70bcb Cache regex permissions when they get processed 2017-01-12 19:45:15 +00:00
Luck ced3e6959c Add methods to get a list of users/groups with a specific permission 2017-01-11 21:25:57 +00:00
Luck 1c229d54d9 Cleanup 2017-01-11 18:55:46 +00:00
Luck dd50193c18 Fix Sponge subjects from being unloaded and never reinitialized - closes #124 2017-01-10 19:17:45 +00:00
Luck ab0f9bbdbd Fix creating new tracks with SQLBacking 2017-01-09 19:24:12 +00:00
Luck 055d181c67 Fix KCauldron dependency loading - closes #121 2017-01-09 18:20:03 +00:00
Luck 7042c45deb Fix retrieving log from SQLBacking 2017-01-08 20:12:33 +00:00
Luck 960c2291b6 Fully implement new SQL schema & add migration code 2017-01-08 19:33:27 +00:00
Luck 773bfe1407 Automatically sort YAML/JSON storage - closes #113 2017-01-08 11:02:13 +00:00
Luck a23f995717 Hopefully fix #118 2017-01-08 10:44:48 +00:00
Luck bd509b3c75 Fix build 2017-01-05 18:30:09 +00:00
Luck a0f8ba9afb Fix NPE in LuckPermsSubjectData - closes #115 2017-01-05 18:27:58 +00:00
Luck a71e040c6d Fix NPE with Vault transient permissions - closes #117 2017-01-05 18:20:17 +00:00
Luck 454abec468 Cleanup vault hook 2017-01-05 18:19:14 +00:00
Luck 96035d5257 Fix clickable node list 2016-12-30 15:02:22 +00:00
Luck 9f2b59f2a2 Refactor SQLBacking - still towards #77 2016-12-30 14:27:43 +00:00
Luck a54046907d change first login event handling 2016-12-30 12:46:24 +00:00
Luck 2ce5679373 Fix issue where only the first request for a user subject would wait for the loading task to complete. Closes #108 2016-12-30 00:42:33 +00:00
Luck 2dad9f84fc Fixes towards #77 2016-12-27 14:55:21 +00:00
Luck a81a361a93 Fully implement new SQLBacking - towards #77 2016-12-27 14:27:05 +00:00
Luck 4660e68abf Some small fixes for new SQL backing 2016-12-27 13:03:59 +00:00
bakatrouble f8a77cf809 Change comments on configs; default ports for all storage backends 2016-12-27 12:39:54 +00:00
bakatrouble 0abe9598f6 It shouldn't be here 2016-12-27 12:39:54 +00:00
bakatrouble 20de713716 Some readability 2016-12-27 12:39:54 +00:00
bakatrouble 9dbef6acc2 Change providers syntax; fix port for PostgreSQL provider 2016-12-27 12:39:54 +00:00
bakatrouble 4c93bf7cf0 Fix connection leaking on batch inserts; fix IDEA analyser warning 2016-12-27 12:39:54 +00:00
bakatrouble a8fea0fc49 Implement NodeDataHolder 2016-12-27 12:39:54 +00:00
Luck 2d5975d6e3 Vault only save if a change was made 2016-12-27 12:24:19 +00:00
Luck 6874cfd5b1 WIP: implement better sql schema - towards #77 2016-12-24 20:30:35 +00:00
Luck d34013396a woops 2016-12-23 16:59:01 +00:00
Luck a35b0915ab Refactor SQL storage - towards #77 2016-12-23 16:55:35 +00:00
Luck b35f3b4375 Properly cleanup in the case of a reload - towards #100 2016-12-23 11:28:42 +00:00
Luck f4f9612789 Fix MongoDB auth-less connection 2016-12-23 10:14:41 +00:00
Luck bfc79c6051 Reduce page entry count to 15 - resolves #95 2016-12-22 22:06:41 +00:00
Luck d3e60487bb Download dependencies at runtime instead of shading them in the jar 2016-12-22 21:17:02 +00:00
Luck f36f411a8d Fix broken inheritance caching system - closes #97 2016-12-22 17:50:56 +00:00
Luck 35f4aea69f Maybe fix caching issues - closes #99 2016-12-22 15:34:22 +00:00
Luck 8795383e7c Make sync command reload sponge data too - resolves #98 2016-12-21 21:34:43 +00:00
Luck fc4750706b Cleanup sponge files and don't persist empty sections - resolves #96 2016-12-21 21:33:32 +00:00
Luck 0ae59bd3e6 show group weight value in the group info command output 2016-12-21 21:26:22 +00:00
Luck 61fd14c816 Add group setweight command - closes #94 2016-12-21 21:22:48 +00:00
Luck c7d3d87b09 Fix verbose recording filters (again) 2016-12-20 13:59:56 +00:00
Luck 5e68f7b58c Implement paginated permission listings - closes #80 2016-12-20 13:55:42 +00:00
Luck 8a692200d5 Improve memory footprint in low throughput caches 2016-12-20 12:42:20 +00:00
Luck 829eacb0fb Fix verbose recording not taking note of filters 2016-12-20 02:53:37 +00:00
Luck 9aeca91f22 Allow space on Sponge Group subjectcollection 2016-12-19 21:52:26 +00:00
Luck 1bdb512826 Ease handling of group names containing spaces, after a faulty migration process 2016-12-19 21:50:34 +00:00
Luck 060fa6e5f7 change weight node 2016-12-19 18:22:32 +00:00
Luck 171a54c0fc PEX migration - include username too. Closes #92 2016-12-19 01:22:47 +00:00
Luck 17406b448d Add more detail to the group info command 2016-12-18 23:34:33 +00:00
Luck b4b661a3ad Set default port 2016-12-18 10:16:05 +00:00
Luck 4857969ca4 Cache calls querying parent groups in bridge subjects - closes #69 2016-12-18 09:57:39 +00:00
Luck d9d01e09aa Cache collation keys - resolves #90 2016-12-18 09:42:27 +00:00
Luck dadca97d8a actually remove 2016-12-17 00:04:04 +00:00
Luck 8afba8b53d remove /p alias 2016-12-17 00:03:21 +00:00
Luck 85dac1184c silence NPE in autoop listener 2016-12-16 22:47:44 +00:00
Luck 4167460d28 Print true/false alongside permission info output for consoles without color. Closes #84 2016-12-16 19:21:36 +00:00
Eric Coan 1d8a60749d help tame mysql connection settings
this commit further helps tame the mysql connection pool (and really
reconnecting to the pool even when the underlying db goes down) further
from commit: #31a597c

this sets up `setValidationTimeout`, and `setConnectionTestQuery`
in order to better (more speedily(?)/fastly(?)/<other adective>ly(?))
handle connection timeouts in a timely manner. (although admittedly
this is already handeled well imo with the cache). however, it could
be better ™️.

in order to do this I did the following things:
  1. Switch to `TimeUnit.SECONDS.toMillis` instead of manually entering
     milliseconds, and having the time in seconds as a comment. just
     makes it more readable imo. if you want me to change it back I can
     I'm not like attached to this or anything.
  2. Perform more common validation timeouts with `setValidationTimeout`
     this allows us to potentially bump back up the connection timeout
     (although I see no harm leaving it where it is), although allows
     us to still "fail-fast" in a way when doing validations.
  3. Use `setConnectionTestQuery` for people who somehow someway
     may not be using a JDBC4 compliant driver.
2016-12-16 17:14:24 +00:00
Luck 31a597ca81 lower mysql connection timeout - may help with #81 2016-12-12 22:21:54 +00:00
Luck e5e77ffec5 Add PEX track migration - closes #78 2016-12-12 19:51:21 +00:00
Luck 41c3df41b4 Allow group weights as permission nodes - resolves #73 2016-12-12 19:30:42 +00:00
Luck 053066f1a7 add parent clear command - closes #67 2016-12-12 19:06:23 +00:00
Luck 21135dac07 Implement (partially) clickable permission lists - towards #68 2016-12-12 18:56:48 +00:00
Luck 22ccce3208 Default the value to true in set/settemp commands if no arg is given. Closes #82 2016-12-12 16:52:15 +00:00
Luck 70aa3964ee Run all commands on the same thread - fixes #79 2016-12-11 11:34:18 +00:00
Luck 167489823a more lenient command blocking 2016-12-09 19:39:05 +00:00
Luck d1426f1b37 change jar names 2016-12-08 19:33:49 +00:00
Luck 7dcdc59c8e Fix IllegalArgumentException with BiMap#put 2016-12-08 16:30:00 +00:00
Luck 1e13ee87af cleanup imports 2016-12-07 22:21:22 +00:00
Luck 5101c1a759 fix build 2016-12-07 22:16:01 +00:00
Slind14 74bb079625 Fix zPermissions imports (PR #72, fixes #61)
* removed zPerm inherit loop check. (blocked the import due to throwing NPE)

* should fix zperm migration (group inheritance and memberships)

* zperm import, include username

* zperm import: fixed memberships
2016-12-07 22:13:50 +00:00
Luck 2da7979d94 Partially add clickable command listings - towards #68 2016-12-07 22:09:11 +00:00
Luck 4ecb2c7c27 Closes #58 - Disable OP commands on Sponge 2016-12-07 21:27:50 +00:00
Luck 251f51b01c add missing messages 2016-12-07 21:20:54 +00:00
Luck 2fba9b83af apply group rewrites to vault - resolves #59 2016-12-07 21:18:57 +00:00
Luck 1c97cb3cec Sponge: Fix migration for subjects with identifiers containing spaces 2016-12-07 21:14:24 +00:00
Luck 25511f898c implement clickable message base - towards #68 2016-12-07 21:08:40 +00:00
Luck d820f153a9 idk 2016-12-06 22:35:06 +00:00
Luck 77b06acad4 declare child states 2016-12-06 21:53:57 +00:00
Luck 6760a96527 Ensure holder state is always declared 2016-12-06 21:48:37 +00:00
Luck 257f426ab9 implement cached state for all permission holders. towards #69 2016-12-06 21:33:55 +00:00
Luck 3771a50f30 cleanup 2016-12-06 19:21:48 +00:00
dracnis 85ee211172 Update VaultChatHook.java
need update as soon as possible.
2016-12-06 19:03:01 +00:00
Luck 1b7a66b41a Fix #66 2016-12-04 23:09:12 +00:00
Luck 8f9a1aa931 Add verbose pastebin upload functionality - resolves #55 2016-12-03 19:30:25 +00:00
Luck 8b60fd0e55 Sponge: remove spammy user not already loaded message 2016-12-03 14:11:53 +00:00
Luck cd7b3b4f6e Fix compat with older guava versions 2016-12-03 13:55:51 +00:00
Luck 83b0f62d59 Implement full caching for non user/group subjects 2016-12-03 13:26:17 +00:00
Luck dd3a78ada8 Fix #65 2016-12-03 11:08:47 +00:00
Luck 307e2b889c Changes for version 2.16 2016-12-02 20:20:31 +00:00
Luck a063f7664d Fix #54 - getPermissionValue not checking default user 2016-12-02 17:26:38 +00:00
Luck b1ab857592 Merge remote-tracking branch 'origin/master' 2016-12-02 17:21:10 +00:00
Luck 2b8bbc434f WIP: Sponge service refactor 2016-12-02 17:20:36 +00:00
Luck c6878c27b9 Fix Vault #setPrefix method 2016-11-30 21:43:10 +00:00
Luck 02ff4a4acb cleanup 2016-11-29 23:35:25 +00:00
Luck 23f757e916 fix formatting 2016-11-28 21:50:19 +00:00
Luck f0ababa409 Maybe fix lilypad uuid retrieval 2016-11-28 21:39:10 +00:00
Luck 331ff341f3 Fix autoop 2016-11-28 20:11:10 +00:00
Luck 793a84edfb Sponge: replace MemorySubjectData with own implementation 2016-11-26 17:19:04 +00:00
Luck 2893d4aebd Silence InterruptedException when the plugin disables 2016-11-26 13:21:21 +00:00
Luck b7ac7680d9 Fix NPE 2016-11-25 21:19:12 +00:00
Luck 18dd59ed0f Refactor Sponge subject handling 2016-11-25 20:59:28 +00:00
Luck 443906da55 Refactor managers 2016-11-25 18:28:10 +00:00
Luck a9f29da4f8 Fix potential memory leak 2016-11-24 21:59:48 +00:00
Luck cf456cff81 Implement tab completion for permissions 2016-11-24 19:07:01 +00:00
Luck 69b3c96e09 Fix Sponge subject data permission overrides 2016-11-23 22:17:49 +00:00
Luck 61eb86b3a0 Fix Sponge defaults serialization 2016-11-23 21:44:59 +00:00
Luck f0ad32bc37 Change PermissionService handling 2016-11-22 21:57:51 +00:00
Luck aeeb2820f9 Sponge: also migrate defaults 2016-11-22 21:14:43 +00:00
Luck 2d88405d69 Fix onLateEnable always being fired 2016-11-22 20:42:43 +00:00
Luck f32ed5db89 Fix Sponge PEX migration 2016-11-22 20:39:56 +00:00
Luck f1606ac51b API changes for 2.15 2016-11-22 18:27:40 +00:00
Luck d13eb1a55d Fix default assignments never being applied on Bungee 2016-11-21 18:26:37 +00:00
Luck 384fbf5464 Revert: Use faster wildcard parsing method 2016-11-21 17:07:13 +00:00
Luck 4874f9d051 Use faster wildcard parsing method 2016-11-20 20:41:39 +00:00
Luck 51732fa660 Rearrange some packages, general cleanup 2016-11-20 13:44:36 +00:00
Luck bb25632178 Improve info command 2016-11-19 21:56:11 +00:00
Luck 9a2cf82623 Improve user info command 2016-11-19 17:52:39 +00:00
Luck c0734fc29e refactor contexts slightly 2016-11-18 22:39:00 +00:00
Luck c60067e733 Maybe fix zPerms migration & tidy up shorthand parser 2016-11-17 22:02:47 +00:00
Luck 6293634e36 refactor shorthand nodes 2016-11-17 20:50:37 +00:00
Luck a047695a84 Fix issue with nodes never being removed on sync with flatfile storage types 2016-11-17 19:01:58 +00:00
Luck db9d84ed76 Maybe fix issue with corrupted permission migration 2016-11-15 20:18:32 +00:00
Luck e296b1df60 Fix migration commands never being executed 2016-11-15 18:17:35 +00:00
Luck b72e5f5437 Update plugin.yml to make LuckPerms always enable before Vault 2016-11-14 22:50:11 +00:00
Luck de09ae94b8 Fix Vault hook - thanks @Yann151924 2016-11-14 22:32:06 +00:00
Luck b36a33f015 Make meta info command show where meta was inherited from 2016-11-14 20:40:33 +00:00
Luck d95891256b Setup a users calculated data when loaded by the SubjectCollection 2016-11-14 18:55:10 +00:00
Luck 9c8097ecb9 Sponge: Implement support for editing more than just users/groups with commands 2016-11-14 18:45:48 +00:00
Luck 18a3dfd604 cleanup 2016-11-13 19:30:46 +00:00
Luck 59fd337e44 remove debug messages, woops 2016-11-13 11:12:06 +00:00
Luck 696cd235fd Fix meta serialization. Resolves #41 2016-11-13 11:09:57 +00:00
Luck 59c7efccb2 Fix Sponge plugin guice load failure. Thanks @gravityfox 2016-11-13 09:44:34 +00:00
Luck b031ebbffd Update readme 2016-11-12 17:27:15 +00:00
Luck 67415babc2 Fix javadocs & fix compile 2016-11-12 16:25:44 +00:00
Luck 563dd4683d Changes for & release of API 2.14 2016-11-12 14:10:42 +00:00
Luck e9131f729a add methods for older sponge api versions 2016-11-11 22:54:09 +00:00
Luck c4ea626e33 Fix PM migration 2016-11-11 20:43:05 +00:00
Luck fefba7976b Actually register new migration command 2016-11-11 20:35:11 +00:00
Luck 2ce8555692 Add Sponge PermissionManager migration support 2016-11-11 20:31:42 +00:00
Luck 9fe9c386bb Sponge: rely less on dummy subjects 2016-11-11 19:32:25 +00:00
Luck e8cd2c7548 More command refactors 2016-11-10 22:13:53 +00:00
Luck 158d8b086a Command refactor 2016-11-10 19:19:43 +00:00
Luck 1c534d7475 More cleanup 2016-11-08 20:46:29 +00:00
Luck 90f8dbe243 Refactor commands 2016-11-07 22:02:48 +00:00
Luck 0c33b26fa9 Refactor meta accumulation 2016-11-07 19:01:15 +00:00
Luck d320679f69 Implement proper meta inheritance handling 2016-11-07 17:32:17 +00:00
Luck 8d5398b5a3 Make nodes with more contexts take priority over nodes with less 2016-11-06 16:15:45 +00:00
Luck 6cd8593fa5 Fix vault primary group override feature 2016-11-06 15:11:28 +00:00
Luck 5ebbc178a5 Remove messenger system & more cleanup 2016-11-06 14:56:13 +00:00
Luck 4f6e229943 Small cleanup 2016-11-05 23:23:08 +00:00
Luck 3e846d9527 Maybe fix split storage - closes #38 2016-11-05 21:43:48 +00:00
Luck 981d745b74 Fix verbose system 2016-11-05 19:19:29 +00:00
Luck 2dd0858960 Allow multiple verbose filters 2016-11-05 19:01:06 +00:00
Luck f72819a664 Try to reduce contention in PermissionHolder 2016-11-05 17:49:52 +00:00
Luck 5361b1e87b Implement group weights 2016-11-05 09:04:26 +00:00
Luck e15d03ed4e Fix parents being cleared on promote/demote 2016-11-05 07:53:18 +00:00
Luck 6c9041ce40 Improve meta info command 2016-11-04 19:13:39 +00:00
Luck 8da96e6e48 Re-add showtracks/showpos commands 2016-11-04 18:36:32 +00:00
Luck 385aa6d438 Add set "none" command as an alternative to unset 2016-11-04 16:40:55 +00:00
Luck 79f24a5944 Improve track functionality. Resolves #39 2016-11-01 21:34:54 +00:00
Luck 80570717f9 Fix slow datastore performance when using the API 2016-11-01 19:41:40 +00:00
Luck ebab79c4ca Maybe fix blocking issue with #getPermissions 2016-11-01 19:38:08 +00:00
Luck 42882ebe11 Update some ugly messages 2016-10-31 22:25:42 +00:00
Luck d2de26f765 Move hasperm/inheritsperm commands & add parent set command. Resolves #40 2016-10-31 19:27:04 +00:00
Luck e2cebfbd16 Refactor Node class 2016-10-31 18:18:31 +00:00
Luck a58cc6bdf3 Refactor PermissionHolder 2016-10-30 21:29:14 +00:00
Luck 36bc1553c0 Implement Sponge timings 2016-10-30 19:35:11 +00:00
Luck 17b06c39e9 Ignore negated primary group override nodes & fix broken message 2016-10-30 15:29:55 +00:00
Luck ba2f6eb7be Update .gitignore 2016-10-30 13:37:25 +00:00
Luck b93145f26d Change some listener priorities 2016-10-30 13:23:45 +00:00
Luck e88ea9c13d Don't fully unload Sponge user subjects 2016-10-30 13:21:43 +00:00
Luck 1275d0023d Listen to PlayerQuit on monitor priority 2016-10-29 22:58:06 +01:00
Luck 27f6f225c9 whoops 2016-10-29 21:56:55 +01:00
Luck 41362aa34a Use a temporary executor until the bukkit one has started 2016-10-29 21:54:13 +01:00
Luck a99609e016 Don't wrap buffer tasks in scheduler calls 2016-10-29 21:23:30 +01:00
Luck c0ea2d8ec3 Fix data not being loaded sync when the plugin starts 2016-10-29 21:14:59 +01:00
Luck bb6b31d715 change Sponge listener priorities 2016-10-29 21:12:55 +01:00
Luck 21695dd2cb Implement support for Bukkit child permissions 2016-10-28 19:16:32 +01:00
Luck 531835c948 Fix GroupManager primary group migration 2016-10-28 18:00:18 +01:00
Luck 77e3b351fd Fix GroupManager user migration 2016-10-28 17:58:36 +01:00
Luck 66f756575a Remove redundant config option 2016-10-27 18:48:38 +01:00
Luck 7acc8bc7ed Add verbose command 2016-10-27 17:46:44 +01:00
Luck 0a4d337a53 Cache context lookups & refactor Sponge subjects 2016-10-27 16:32:14 +01:00
Luck 0198068a13 Improve permission lookup speed (slightly) 2016-10-26 21:35:44 +01:00
Luck 1c15f8462f Don't run commands through the buffer system 2016-10-26 19:52:03 +01:00
Luck 3929ce0dfc Fix compile 2016-10-26 19:32:41 +01:00
Luck 7deeaf305d Experimental: Run I/O after commands in a new thread 2016-10-26 17:55:38 +01:00
Luck 0f04c06e13 Remove redundant method 2016-10-26 17:19:56 +01:00
Luck d1863fa714 Implement more customizable primary groups 2016-10-26 17:19:33 +01:00
Luck 46b122a167 Fix memory leak preventing users from being unloaded 2016-10-25 18:44:11 +01:00
Luck ee3db1e5c5 Maybe fix GroupManager primary group migration 2016-10-25 15:02:49 +01:00
Luck bd830617c9 Small cleanup 2016-10-25 11:14:56 +01:00
Luck a9b6493091 A few small optimizations 2016-10-24 18:48:39 +01:00
Luck 9894e45516 Fix issue with Vault lookups not being preprocessed 2016-10-24 15:57:05 +01:00
Luck 2cfc82f3aa Implement support for instant data propagation with Redis 2016-10-24 14:38:12 +01:00
Luck bb9eab0989 Lower sync wait buffer & fix export command 2016-10-24 13:02:26 +01:00
Luck dc87ec1c82 Update placeholder expansion 2016-10-24 09:57:29 +01:00
Luck 98b7b65ce7 Remove PEX dependency 2016-10-24 09:22:53 +01:00
Luck 9f8f32b33e Fix suffixes not being applied 2016-10-23 22:02:48 +01:00
Luck a80ec6d893 Implement PermissionsEx 2.0 migration for Sponge 2016-10-23 20:42:58 +01:00
Luck 4d85930f5a Refactor migration classes 2016-10-23 20:42:25 +01:00
Luck f722f4112d Update default-lang.yml 2016-10-23 17:59:34 +01:00
Luck 383276f47a Refactor contexts, expose cached data in the API & release 2.13 2016-10-23 16:46:53 +01:00
Luck 33c78e4a17 Use LoadingCache instead of a regular map + more datastore fixes 2016-10-22 20:39:26 +01:00
Luck 55a0de7899 Lower buffer time to 1 second. Should resolve any issues with changes being overridden. (related: #32) 2016-10-22 18:52:51 +01:00
Luck 6b3aab66bf Fix some #saveX methods being waited for, when they can just be ran async 2016-10-22 09:35:11 +01:00
Luck 77e70ef8d3 Fix deadlocks caused by waiting for async operations to complete before the scheduler has started 2016-10-22 09:21:30 +01:00
Luck 9b92143501 prevent logins until the plugin has started 2016-10-22 00:07:30 +01:00
Luck 87f94f6b8f add started value to bukkit plugin 2016-10-22 00:05:33 +01:00
Luck 5d955e77b6 Make the max pool size configurable 2016-10-21 22:22:06 +01:00
Luck 270a276b98 Refactor SQL storage to not hold onto connections for as long 2016-10-21 22:07:14 +01:00
Luck be7d914e39 Finish the buffering refactor - still WIP. Towards #32 2016-10-21 18:34:53 +01:00
Luck 37fc00cce8 Refactoring storage - WIP. Towards #32 2016-10-20 21:55:34 +01:00
Luck 0d515abadb Cleanup some bad code in SQLDatastore. Might help #32 2016-10-19 23:30:30 +01:00
Luck daef01194e Shade MySQL connector into Sponge jar. Resolves #31 2016-10-19 22:23:43 +01:00
Luck 258bf1cc41 small cleanup 2016-10-19 20:57:53 +01:00
Luck ce3d7829d7 Update internal permissions to match the new command layouts 2016-10-19 20:14:16 +01:00
Luck c66a01c1a6 Fix temporary permission expirations never persisting. Fixes #30 2016-10-19 17:37:26 +01:00
Luck d113a92ce5 Cleanup permission calculation 2016-10-18 20:48:59 +01:00
Luck 7b655d12df Add set-primary-group option to the default assignments 2016-10-17 18:23:41 +01:00
Luck a4c872f1e8 Fix per-world permissions not working until a user switches worlds 2016-10-17 17:10:02 +01:00
Luck 0e7d9bf9c1 Bump hikari version 2016-10-16 21:05:25 +01:00
Luck 6252a5bc75 Fix invalid property 2016-10-16 20:53:12 +01:00
Luck e4e80c9c31 Update to SpongeAPI 5.0.0 release 2016-10-16 18:13:55 +01:00
Luck 165c5610c3 Add extra Hikari flag 2016-10-16 17:24:02 +01:00
Luck 13fbc7dd39 Implement default assignment system (#25) 2016-10-16 13:58:57 +01:00
Luck 02a75fc32a Reimplement autoop on Bukkit & other misc fixes 2016-10-15 21:04:04 +01:00
Luck 699c4d107b Swap the javaassist maven plugin with a better method 2016-10-15 08:59:49 +01:00
Luck e4810302e1 Implement meta / Sponge option commands & fix meta caching system. Fully resolves #23 2016-10-14 22:49:32 +01:00
Luck 73f10cad66 Make the recent command changes backwards compatible 2016-10-14 20:42:00 +01:00
Luck f1d670dc46 refactor permission commands 2016-10-14 19:55:22 +01:00
Luck 045539c752 refactor inheritance commands 2016-10-14 19:19:57 +01:00
Luck 782c09b686 Refine bulk group edit primary group check 2016-10-13 21:29:30 +01:00
Luck 6109718c67 Cleanup some remaining TODOs 2016-10-13 21:16:34 +01:00
Luck cd758d7e48 Bump minor version 2016-10-13 20:40:09 +01:00
Luck fcb0fcadeb Fix StackOverflowException 2016-10-13 20:36:08 +01:00
Luck 26fb506560 Reorganize packages 2016-10-13 20:07:10 +01:00
Luck b6c4239322 Fix NPE 2016-10-13 19:43:48 +01:00
Luck 74f9283f18 Silence exception on #giveDefaultIfNeeded. Resolves #26 2016-10-13 19:25:31 +01:00
Luck 60e6ae6e23 Fix Sponge implementation, remove standalone module 2016-10-13 19:13:34 +01:00
Luck 83d9229295 Refactor caching system (WIP) - still working towards #23 2016-10-12 21:57:57 +01:00
Luck e079b0f3ed Fix meta command showing when a user doesn't have permission to use it 2016-10-11 21:17:04 +01:00
Luck d433a5461d Refactor meta commands, working towards #23 2016-10-11 21:12:10 +01:00
Luck 2c43f6bf21 Fix global permissions and groups sometimes not applying 2016-10-11 19:31:55 +01:00
Luck ac57509a29 Fix prefix/suffix priorities being ignored 2016-10-11 19:31:26 +01:00
Luck c5ce655cf6 Add Vault chat debug messages 2016-10-11 19:06:18 +01:00
Luck 9fe05efb4e Don't force the default group 2016-10-11 18:54:42 +01:00
Luck dcf207f5e5 Fix exception, resolves #20 and #22 2016-10-10 22:39:24 +01:00
Luck 11facab71c Fix NPE, resolves #19 2016-10-10 22:26:39 +01:00
Luck 5b4975f34c Implement persisting subjects (+ defaults) 2016-10-10 18:58:43 +01:00
Luck 069e535f1a Avoid odd behavior where Sponge prevents User subjects from being GCed 2016-10-09 21:34:05 +01:00
Luck 1eb053447e Make refresh calls in SpongeListener run async 2016-10-09 20:49:17 +01:00
Luck 93c7c06b2d Cleanup some Sponge subject classes 2016-10-09 13:14:58 +01:00
Luck b0085bc1e1 Update readme 2016-10-09 10:58:14 +01:00
Luck 9133951446 I'm dumb 2016-10-08 16:45:18 +01:00
Luck 7853d4686a Fix NPE with LPPermissibles & refreshing users 2016-10-08 16:43:57 +01:00
Luck ed5892eb56 add option to ignore vault world parameters 2016-10-08 15:01:54 +01:00
Luck 687804de2c small change to BukkitConfig 2016-10-08 09:05:48 +01:00
Luck 9d166041ab Account for inheritance and wildcards in SimpleSubject 2016-10-07 18:11:59 +01:00
Luck 924041bff4 Add server and world args to clear command, and add clearmeta command 2016-10-06 21:23:27 +01:00
Luck e9216d3e0f Allow the prefix to be modified by the lang file 2016-10-06 19:54:52 +01:00
Luck db8f1bca66 Fix API javadoc & fix Bukkit legacy build 2016-10-06 17:21:04 +01:00
Luck 2429831153 API changes for 2.11 2016-10-05 21:40:54 +01:00
Luck 80d2246634 Fix exporting users not in the default group 2016-10-04 20:48:58 +01:00
Luck 56df2477ca Fix UnsupportedOperationExceptions with #getPermissions 2016-10-04 17:30:27 +01:00
Luck 9b4384dc33 Revert: Merge dummy implementations 2016-10-04 17:15:10 +01:00
Luck bc65857831 Fix CME 2016-10-04 17:13:32 +01:00
Luck de462d2385 Merge dummy implementations 2016-10-04 16:58:54 +01:00
Luck 90150428be Inject a dummy on quit instead of a real PermBase instance 2016-10-04 16:19:41 +01:00
Luck 2a7f951f4f Fix NPE / crash on player quit 2016-10-04 16:10:06 +01:00
Luck 82789328c1 Pre-process permissions and apply immediately on login events 2016-10-04 14:57:00 +01:00
Luck 13bccd1d3f Fix users not receiving permission updates 2016-10-03 21:41:47 +01:00
Luck 43c514132d Cache node deserialization 2016-10-03 21:18:59 +01:00
Luck 9e0fdcbfee Fix memory leak, woops 2016-10-03 20:43:36 +01:00
Luck 008dc3fc52 Move some classes around 2016-10-03 20:42:11 +01:00
Luck 9fde237d9b Swap slow get method 2016-10-03 19:53:43 +01:00
Luck d626c08b4c Small fix for #getAllWithPermission 2016-10-03 16:45:52 +01:00
Luck 58e223e74b Implement bulk prefix/suffix removal 2016-10-02 21:17:59 +01:00
Luck 1da1df156e Fix issues related to Node comparisons 2016-10-02 15:44:46 +01:00
Luck 3442372281 Cleanup random debug messages 2016-10-02 15:43:44 +01:00
Luck 1b3b50c113 Refactor PermissionHolder & Sponge subjects 2016-10-02 15:05:53 +01:00
Luck 18e126b781 Fix issue with permissions sometimes not being removed 2016-10-02 10:33:58 +01:00
Luck c88600adda Make User constructor protected. Closes #18. 2016-10-01 22:56:57 +01:00
Luck 4787361e66 Implement locks to hopefully resolve race conditions with I/O - experimental 2016-10-01 19:03:05 +01:00
Luck b5ece8b5bd Fix exception 2016-09-30 22:26:39 +01:00
Luck 7ee3116f29 Maybe fix broken inheritance 2016-09-30 20:43:08 +01:00
Luck 1fc0d77971 Cache & fix (and maybe break) Vault Chat implementation 2016-09-30 20:08:59 +01:00
Luck a32ab4bf7f Show where a permission was inherited from in the inheritsperm command 2016-09-30 19:21:19 +01:00
Luck 2d8083bf18 Another Vault fix & cleanup User / Group classes 2016-09-30 17:45:29 +01:00
Luck 63b9be9845 Change Vault behavior & cleanup Node class 2016-09-30 17:09:19 +01:00
Luck 85a83d8c71 Add logging output to Vault 2016-09-29 22:04:46 +01:00
Luck 71b33a296c Calculate Sponge defaults using context 2016-09-29 20:38:03 +01:00
Luck becdfd6f03 Silence subject load errors unless debug-permission-checks is true 2016-09-29 20:02:38 +01:00
Luck 41744f9416 Add clone commands 2016-09-29 18:07:41 +01:00
Luck cb89a92ba9 Message colour changes and standardization 2016-09-28 21:16:07 +01:00
Luck 93d54373c6 Add detailed command info system 2016-09-28 20:59:08 +01:00
Luck 385addfe08 cleanup 2016-09-27 20:21:13 +01:00
Luck 4c21957b04 Cache Vault lookups & properly sort listnodes output 2016-09-27 20:18:06 +01:00
Luck ab6b011225 Fix permission lookups being case sensitive 2016-09-26 18:52:01 +01:00
Luck e72d91503c Maybe nicer looking messages 2016-09-26 18:42:46 +01:00
Luck b9ee206908 Add some more bulk edit commands 2016-09-25 21:23:48 +01:00
Luck 4c9abba5fa Maybe fix vault (again) 2016-09-25 19:26:36 +01:00
Luck 3424b0542a Release 2.10 2016-09-25 17:32:58 +01:00
Luck fec90b8958 Add bulkchange commands 2016-09-25 17:29:11 +01:00
Luck 4da7fe4ff9 Small fixes to context system 2016-09-25 16:55:14 +01:00
Luck c53623dd67 Make command usage output easier to read 2016-09-25 14:34:27 +01:00
Luck 6540ffa4dd Refactor storage 2016-09-25 12:47:15 +01:00
Luck 5ee6db02ff Proper context implementation - WIP 2016-09-25 10:19:36 +01:00
Luck d8221f466a Fix messages not being formatted correctly 2016-09-24 17:22:33 +01:00
Luck 462bea2a09 Remove broken repo, maybe fix build 2016-09-24 16:58:45 +01:00
Luck f452a97278 fix typo 2016-09-23 22:19:17 +01:00
Luck eb9d2be587 Add locale file support 2016-09-23 22:14:40 +01:00
Luck df1e747861 Add commands to view inherited groups 2016-09-23 19:23:31 +01:00
Luck f48595b1ff Add world and group name rewriting, refactor configs 2016-09-23 18:58:19 +01:00
Luck 12cd4e19cb Update config comments 2016-09-22 18:25:49 +01:00
Luck a195e94522 Calculate defaults after all plugins have loaded 2016-09-22 17:34:51 +01:00
Luck 02abb39dc2 Change #isPermissionSet to be more like the Bukkit impl 2016-09-21 22:04:18 +01:00
Luck f7a0b1ef78 Refactor default permissions to be less bad 2016-09-21 21:30:42 +01:00
Luck 7678f25879 Fix default permissions not properly applying on Bukkit 2016-09-21 17:40:05 +01:00
462 changed files with 36611 additions and 18995 deletions

No files matched your search

+151 -24
View File
@@ -1,31 +1,27 @@
# built application files
*.jar
# Created by https://www.gitignore.io/
# Java class files
*.class
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# Local configuration file (sdk path, etc)
logs/
# Eclipse project files
.classpath
.project
# Intellij project files
*.iml
*.ipr
*.iws
# User-specific stuff:
.idea/
*.iws
/out/
*.iml
.idea_modules/
# Gradle
.gradletasknamecache
.gradle/
build/
bin/
gradle/
gradlew*
# JIRA plugin
atlassian-ide-plugin.xml
# Maven
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
@@ -34,4 +30,135 @@ pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/timing.properties
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+14 -6
View File
@@ -8,19 +8,27 @@ An advanced permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
* **Wiki** - <https://github.com/lucko/LuckPerms/wiki>
## Description
LuckPerms is an advanced permissions implementation aiming to be a flexible, performant and reliable alternative to other permission systems, providing all the features you could ever possibly need in a permissions plugin, along with an extensive and well documented API for developers.
LuckPerms is an advanced permissions implementation aiming to be a fast, reliable and flexible alternative to existing permission plugins. The project's main goals are centered around high performance and a wide feature set, filling the gaps of functionality and building upon existing features found in other plugins.
* [Spigot Plugin Page](https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-system.28140/ "Spigot Plugin Page")
* [Sponge Plugin Page](https://forums.spongepowered.org/t/luckperms-an-advanced-permissions-system/14274 "Sponge Plugin Page")
LuckPerms also includes an extensive API for developers, and support for a variety of Minecraft server software & data storage options.
See the charts below for a partial list of features & comparison with other providers.
* [Spigot Plugin Release Page](https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-system.28140/ "Spigot Plugin Page")
* [Sponge Plugin Release Page](https://forums.spongepowered.org/t/luckperms-an-advanced-permissions-system/14274 "Sponge Plugin Page")
## LuckPerms vs Other Plugins
### Bukkit
![alt text](https://static.lucko.me/luckperms-compare-copy.png "Feature comparison")
_Features checked on 23rd Aug 2016. If you find any inaccuracies, please do let me know. I tried to be as fair as possible, and copy all major features from the respective plugin pages._
### Sponge
![alt text](https://static.lucko.me/luckperms-compare-sponge.png "Feature comparison")
_Features checked on 8th Oct 2016. If you find any inaccuracies, please do let me know. I tried to be as fair as possible, and copy all major features from the respective plugin pages._
## License
See LICENSE.md.
LuckPerms is licensed under the permissive MIT license. See LICENSE.md for more info.
## Thanks to:
## Sponsor
[![JProfiler](https://www.ej-technologies.com/images/product_banners/jprofiler_large.png)](http://www.ej-technologies.com/products/jprofiler/overview.html)
Thanks to ej-technologies for granting LuckPerms an open source licence to their [java profiling software](http://www.ej-technologies.com/products/jprofiler/overview.html "Java Profiler").
Thanks to ej-technologies for granting LuckPerms an open source licence to their [Java Profiling Software](http://www.ej-technologies.com/products/jprofiler/overview.html "Java Profiler").
+3 -3
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.9-SNAPSHOT</version>
<version>2.17-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -27,13 +27,17 @@ import me.lucko.luckperms.api.LuckPermsApi;
import java.util.Optional;
/**
* Static access to LuckPerms
* Singleton for the {@link LuckPermsApi}.
*
* <p> Ideally, the ServiceManager for the platform should be used to obtain and cache an instance, however, this can be
* used if you need static access.
*/
public final class LuckPerms {
private static LuckPermsApi api = null;
/**
* Gets an instance of {@link LuckPermsApi}
* Gets an instance of {@link LuckPermsApi}, throwing {@link IllegalStateException} if the API is not loaded.
*
* @return an api instance
* @throws IllegalStateException if the api is not loaded
*/
@@ -45,8 +49,11 @@ public final class LuckPerms {
}
/**
* Gets an instance of {@link LuckPermsApi} safely. Unlike {@link LuckPerms#getApi}, this method will not throw an
* {@link IllegalStateException} if the api is not loaded, rather return an empty {@link Optional}.
* Gets an instance of {@link LuckPermsApi} safely.
*
* <p> Unlike {@link LuckPerms#getApi}, this method will not throw an {@link IllegalStateException} if the API is
* not loaded, rather return an empty {@link Optional}.
*
* @return an optional api instance
*/
public static Optional<LuckPermsApi> getApiSafe() {
@@ -0,0 +1,263 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.Map;
/**
* Context and options for a permission lookup.
*
* <p> All values are immutable.
*
* @since 2.11
*/
public class Contexts {
public static final String SERVER_KEY = "server";
public static final String WORLD_KEY = "world";
private static final Contexts ALLOW_ALL = new Contexts(ContextSet.empty(), true, true, true, true, true, true);
/**
* Gets a context that will allow all nodes
*
* @return a context that will not apply any filters
*/
public static Contexts allowAll() {
return ALLOW_ALL;
}
public static Contexts of(ContextSet context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
@SuppressWarnings("deprecation")
@Deprecated
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups);
}
@SuppressWarnings("deprecation")
@Deprecated
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
/**
* The contexts that apply for this lookup
* The keys for servers and worlds are defined as static values.
*/
private final ContextSet context;
/**
* The mode to parse defaults on Bukkit
*
* @since 2.12
*/
private final boolean op;
/**
* If global or non server specific nodes should be applied
*/
private final boolean includeGlobal;
/**
* If global or non world specific nodes should be applied
*/
private final boolean includeGlobalWorld;
/**
* If parent groups should be applied
*/
private final boolean applyGroups;
/**
* If global or non server specific group memberships should be applied
*/
private final boolean applyGlobalGroups;
/**
* If global or non world specific group memberships should be applied
*/
private final boolean applyGlobalWorldGroups;
public Contexts(ContextSet context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
if (context == null) {
throw new NullPointerException("context");
}
this.context = context.makeImmutable();
this.includeGlobal = includeGlobal;
this.includeGlobalWorld = includeGlobalWorld;
this.applyGroups = applyGroups;
this.applyGlobalGroups = applyGlobalGroups;
this.applyGlobalWorldGroups = applyGlobalWorldGroups;
this.op = op;
}
@Deprecated
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
this(context == null ? null : ContextSet.fromMap(context), includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, op);
}
@SuppressWarnings("deprecation")
@Deprecated
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
this(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups, false);
}
/**
* Gets the contexts that apply for this lookup
*
* @return an immutable set of context key value pairs
* @since 2.13
*/
public ContextSet getContexts() {
return this.context;
}
/**
* Gets the contexts that apply for this lookup
*
* @return an immutable map of context key value pairs
* @deprecated in favour of {@link #getContexts()}
*/
@Deprecated
public Map<String, String> getContext() {
return this.context.toMap();
}
/**
* Gets if OP defaults should be included
*
* @return true if op defaults should be included
*/
public boolean isOp() {
return this.op;
}
/**
* Gets if global or non server specific nodes should be applied
*
* @return true if global or non server specific nodes should be applied
*/
public boolean isIncludeGlobal() {
return this.includeGlobal;
}
/**
* Gets if global or non world specific nodes should be applied
*
* @return true if global or non world specific nodes should be applied
*/
public boolean isIncludeGlobalWorld() {
return this.includeGlobalWorld;
}
/**
* Gets if parent groups should be applied
*
* @return true if parent groups should be applied
*/
public boolean isApplyGroups() {
return this.applyGroups;
}
/**
* Gets if global or non server specific group memberships should be applied
*
* @return true if global or non server specific group memberships should be applied
*/
public boolean isApplyGlobalGroups() {
return this.applyGlobalGroups;
}
/**
* Gets if global or non world specific group memberships should be applied
*
* @return true if global or non world specific group memberships should be applied
*/
public boolean isApplyGlobalWorldGroups() {
return this.applyGlobalWorldGroups;
}
public String toString() {
return "Contexts(" +
"context=" + this.getContexts() + ", " +
"op=" + this.isOp() + ", " +
"includeGlobal=" + this.isIncludeGlobal() + ", " +
"includeGlobalWorld=" + this.isIncludeGlobalWorld() + ", " +
"applyGroups=" + this.isApplyGroups() + ", " +
"applyGlobalGroups=" + this.isApplyGlobalGroups() + ", " +
"applyGlobalWorldGroups=" + this.isApplyGlobalWorldGroups() +
")";
}
/*
* Ugly auto-generated lombok code
*/
/**
* Check for equality
*
* @param o the other
* @return true if equal
* @since 2.12
*/
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Contexts)) return false;
final Contexts other = (Contexts) o;
final Object this$context = this.getContexts();
final Object other$context = other.getContexts();
if (this$context == null ? other$context != null : !this$context.equals(other$context)) return false;
if (this.isOp() != other.isOp()) return false;
if (this.isIncludeGlobal() != other.isIncludeGlobal()) return false;
if (this.isIncludeGlobalWorld() != other.isIncludeGlobalWorld()) return false;
if (this.isApplyGroups() != other.isApplyGroups()) return false;
if (this.isApplyGlobalGroups() != other.isApplyGlobalGroups()) return false;
if (this.isApplyGlobalWorldGroups() != other.isApplyGlobalWorldGroups()) return false;
return true;
}
/**
* Gets the hashcode
*
* @return the hashcode
* @since 2.12
*/
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $context = this.getContexts();
result = result * PRIME + ($context == null ? 43 : $context.hashCode());
result = result * PRIME + (this.isOp() ? 79 : 97);
result = result * PRIME + (this.isIncludeGlobal() ? 79 : 97);
result = result * PRIME + (this.isIncludeGlobalWorld() ? 79 : 97);
result = result * PRIME + (this.isApplyGroups() ? 79 : 97);
result = result * PRIME + (this.isApplyGlobalGroups() ? 79 : 97);
result = result * PRIME + (this.isApplyGlobalWorldGroups() ? 79 : 97);
return result;
}
}
@@ -28,281 +28,158 @@ import java.util.Set;
import java.util.UUID;
/**
* Interface for the internal Datastore instance
* Deprecated Storage interface. Use {@link Storage} instead.
*
* @deprecated as of version 2.14 in favour of {@link Storage}.
*/
@SuppressWarnings("unused")
@Deprecated
public interface Datastore {
String getName();
boolean isAcceptingLogins();
/**
* Gets the {@link Sync} interface.
*
* All operations through this interface are called immediately and in the same thread as they are called.
* Datastore operations are thread blocking, and Sync operations should not be called on the main server thread.
* @return the sync interface
*/
@Deprecated
Sync sync();
/**
* Gets the {@link Async} interface.
*
* All operations through this interface are called in a new, separate asynchronous thread.
* When the operation is complete, the provided callback method is called, in applicable, in the main server thread.
* @return the async interface
*/
@Deprecated
Async async();
/**
* Gets the {@link Future} interface.
*
* All operations through this interface are called in a new, separate asynchronous thread, similar to {@link Async}.
* The only difference is that instead of providing a callback, a {@link java.util.concurrent.Future} is returned.
* See the Oracle JavaDocs for usage of the Future class.
* @return the future interface
*/
@Deprecated
Future future();
/**
* All operations through this interface are called immediately and in the same thread as they are called.
* Datastore operations are thread blocking, and Sync operations should not be called on the main server thread.
*/
@Deprecated
interface Sync {
/**
* Saves an action to the datastore
* @param entry the log entry to be saved
* @return true if the operation completed successfully.
* @throws NullPointerException if entry is null
*/
boolean logAction(LogEntry entry);
/**
* Loads and returns the log from the datastore
* @return a log instance, could be null
*/
Log getLog();
/**
* Either loads or creates a user object, and loads it into the plugins internal storage
* @param uuid the uuid of the user
* @param username the users username. (if you want to specify <code>null</code> here, just input "null" as a string.)
* @return true if the operation completed successfully.
* @throws NullPointerException if uuid or username is null
* @throws IllegalArgumentException if either of the parameters are invalid
* @deprecated functionality of this method is taken on by {@link #loadUser(UUID, String)}
*/
@Deprecated
boolean loadOrCreateUser(UUID uuid, String username);
/**
* Loads a user from the datastore into the plugins internal storage.
* @param uuid the uuid of the user to load
* @return true if the user exists, and was loaded correctly.
* @throws NullPointerException if uuid is null
* @deprecated replaced by {@link #loadUser(UUID, String)}
*/
@Deprecated
boolean loadUser(UUID uuid);
/**
* Loads a user's data into the plugins internal storage.
* @param uuid the uuid of the user to load
* @param username the users username. (if you want to specify <code>null</code> here, just input "null" as a string.)
* @return if the operation was performed successfully
* @throws NullPointerException if uuid or username is null
* @since 2.6
*/
boolean loadUser(UUID uuid, String username);
/**
* Saves a user object into the datastore. You should call this after you make any changes to a user.
* @param user the user to save
* @return true if the operation completed successfully.
* @throws NullPointerException if user is null
* @throws IllegalStateException if the user instance was not obtained from LuckPerms.
*/
boolean saveUser(User user);
/**
* Removes users from the datastore who are "default". This is called every time the plugin loads.
* @return true if the operation completed successfully
* @since 2.6
*/
boolean cleanupUsers();
/**
* Gets a set all user's UUIDs who are "unique", and aren't just a member of the "default" group.
* @return a set of uuids, or null if the operation failed.
* @since 2.6
*/
Set<UUID> getUniqueUsers();
/**
* Creates and loads a group into the plugins internal storage
* @param name the name of the group
* @return true if the operation completed successfully
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
boolean createAndLoadGroup(String name);
/**
* Loads a group into the plugins internal storage.
* @param name the name of the group
* @return true if the operation completed successfully
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
boolean loadGroup(String name);
/**
* Loads all groups from the datastore into the plugins internal storage
* @return true if the operation completed successfully.
*/
boolean loadAllGroups();
/**
* Saves a group back to the datastore. You should call this after you make any changes to a group.
* @param group the group to save
* @return true if the operation completed successfully.
* @throws NullPointerException if group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
boolean saveGroup(Group group);
/**
* Permanently deletes a group from the datastore
* @param group the group to delete
* @return true if the operation completed successfully.
* @throws NullPointerException if group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
boolean deleteGroup(Group group);
/**
* Creates and loads a track into the plugins internal storage
* @param name the name of the track
* @return true if the operation completed successfully
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
boolean createAndLoadTrack(String name);
/**
* Loads a track into the plugins internal storage.
* @param name the name of the track
* @return true if the operation completed successfully
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
boolean loadTrack(String name);
/**
* Loads all tracks from the datastore into the plugins internal storage
* @return true if the operation completed successfully.
*/
boolean loadAllTracks();
/**
* Saves a track back to the datastore. You should call this after you make any changes to a track.
* @param track the track to save
* @return true if the operation completed successfully.
* @throws NullPointerException if track is null
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
*/
boolean saveTrack(Track track);
/**
* Permanently deletes a track from the datastore
* @param track the track to delete
* @return true if the operation completed successfully.
* @throws NullPointerException if track is null
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
*/
boolean deleteTrack(Track track);
/**
* Saves UUID caching data to the datastore
* @param username the users username
* @param uuid the users mojang unique id
* @return true if the operation completed successfully.
* @throws NullPointerException if either parameters are null
* @throws IllegalArgumentException if the username is invalid
*/
boolean saveUUIDData(String username, UUID uuid);
/**
* Gets a UUID from a username
* @param username the corresponding username
* @return a uuid object, could be null
* @throws NullPointerException if either parameters are null
* @throws IllegalArgumentException if the username is invalid
*/
UUID getUUID(String username);
}
/**
* All operations through this interface are called in a new, separate asynchronous thread.
* When the operation is complete, the provided callback method is called, in applicable, in the main server thread.
*
* See {@link Sync} for method documentation.
*/
@Deprecated
interface Async {
void logAction(LogEntry entry, Callback<Boolean> callback);
void getLog(Callback<Log> callback);
@Deprecated
void loadOrCreateUser(UUID uuid, String username, Callback<Boolean> callback);
@Deprecated
void loadUser(UUID uuid, Callback<Boolean> callback);
void loadUser(UUID uuid, String username, Callback<Boolean> callback);
void saveUser(User user, Callback<Boolean> callback);
void cleanupUsers(Callback<Boolean> callback);
void getUniqueUsers(Callback<Set<UUID>> callback);
void createAndLoadGroup(String name, Callback<Boolean> callback);
void loadGroup(String name, Callback<Boolean> callback);
void loadAllGroups(Callback<Boolean> callback);
void saveGroup(Group group, Callback<Boolean> callback);
void deleteGroup(Group group, Callback<Boolean> callback);
void createAndLoadTrack(String name, Callback<Boolean> callback);
void loadTrack(String name, Callback<Boolean> callback);
void loadAllTracks(Callback<Boolean> callback);
void saveTrack(Track track, Callback<Boolean> callback);
void deleteTrack(Track track, Callback<Boolean> callback);
void saveUUIDData(String username, UUID uuid, Callback<Boolean> callback);
void getUUID(String username, Callback<UUID> callback);
}
/**
* All operations through this interface are called in a new, separate asynchronous thread, similar to {@link Async}.
* The only difference is that instead of providing a callback, a {@link java.util.concurrent.Future} is returned.
* See the Oracle JavaDocs for usage of the Future class.
*
* See {@link Sync} for method documentation.
*/
@Deprecated
interface Future {
java.util.concurrent.Future<Boolean> logAction(LogEntry entry);
java.util.concurrent.Future<Log> getLog();
@Deprecated
java.util.concurrent.Future<Boolean> loadOrCreateUser(UUID uuid, String username);
@Deprecated
java.util.concurrent.Future<Boolean> loadUser(UUID uuid);
java.util.concurrent.Future<Boolean> loadUser(UUID uuid, String username);
java.util.concurrent.Future<Boolean> saveUser(User user);
java.util.concurrent.Future<Boolean> cleanupUsers();
java.util.concurrent.Future<Set<UUID>> getUniqueUsers();
java.util.concurrent.Future<Boolean> createAndLoadGroup(String name);
java.util.concurrent.Future<Boolean> loadGroup(String name);
java.util.concurrent.Future<Boolean> loadAllGroups();
java.util.concurrent.Future<Boolean> saveGroup(Group group);
java.util.concurrent.Future<Boolean> deleteGroup(Group group);
java.util.concurrent.Future<Boolean> createAndLoadTrack(String name);
java.util.concurrent.Future<Boolean> loadTrack(String name);
java.util.concurrent.Future<Boolean> loadAllTracks();
java.util.concurrent.Future<Boolean> saveTrack(Track track);
java.util.concurrent.Future<Boolean> deleteTrack(Track track);
java.util.concurrent.Future<Boolean> saveUUIDData(String username, UUID uuid);
java.util.concurrent.Future<UUID> getUUID(String username);
}
}
@@ -26,212 +26,237 @@ import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
import java.util.OptionalInt;
/**
* Interface for internal Group instances
* A group which holds permission data.
*/
@SuppressWarnings("unused")
public interface Group extends PermissionHolder {
/**
* Get the name of the group
*
* @return the name of the group
*/
String getName();
/**
* Check to see if a group inherits a group
*
* @param group The group to check membership of
* @return true if the group inherits the other group
* @throws NullPointerException if the group is null
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
boolean inheritsGroup(Group group);
/**
* Check to see if the group inherits a group on a specific server
* @param group The group to check membership of
*
* @param group The group to check membership of
* @param server The server to check on
* @return true if the group inherits the group on the server
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server is invalid
*/
boolean inheritsGroup(Group group, String server);
/**
* Check to see if the group inherits a group on a specific server and world
* @param group The group to check membership of
*
* @param group The group to check membership of
* @param server The server to check on
* @param world The world to check on
* @param world The world to check on
* @return true if the group inherits the group on the server and world
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server or world is invalid
*/
boolean inheritsGroup(Group group, String server, String world);
/**
* Make this group inherit another group
*
* @param group the group to be inherited
* @throws ObjectAlreadyHasException if the group already inherits the group
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void setInheritGroup(Group group) throws ObjectAlreadyHasException;
/**
* Make this group inherit another group on a specific server
* @param group the group to be inherited
*
* @param group the group to be inherited
* @param server The server to inherit the group on
* @throws ObjectAlreadyHasException if the group already inherits the group on that server
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server is invalid
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server is invalid
*/
void setInheritGroup(Group group, String server) throws ObjectAlreadyHasException;
/**
* Make this group inherit another group on a specific server and world
* @param group the group to be inherited
*
* @param group the group to be inherited
* @param server The server to inherit the group on
* @param world The world to inherit the group on
* @param world The world to inherit the group on
* @throws ObjectAlreadyHasException if the group already inherits the group on that server and world
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server or world is invalid
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server or world is invalid
*/
void setInheritGroup(Group group, String server, String world) throws ObjectAlreadyHasException;
/**
* Make this group inherit another group temporarily
* @param group the group to be inherited
*
* @param group the group to be inherited
* @param expireAt the unix time when the group should expire
* @throws ObjectAlreadyHasException if the group already inherits the group temporarily
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past
*/
void setInheritGroup(Group group, long expireAt) throws ObjectAlreadyHasException;
/**
* Make this group inherit another group on a specific server temporarily
* @param group the group to be inherited
* @param server The server inherit add the group on
*
* @param group the group to be inherited
* @param server The server inherit add the group on
* @param expireAt when the group should expire
* @throws ObjectAlreadyHasException if the group already inherits the group on that server temporarily
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server is invalid
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server is invalid
*/
void setInheritGroup(Group group, String server, long expireAt) throws ObjectAlreadyHasException;
/**
* Make this group inherit another group on a specific server and world temporarily
* @param group the group to be inherited
* @param server The server to inherit the group on
* @param world The world to inherit the group on
*
* @param group the group to be inherited
* @param server The server to inherit the group on
* @param world The world to inherit the group on
* @param expireAt when the group should expire
* @throws ObjectAlreadyHasException if the group already inherits the group on that server and world temporarily
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server/world is invalid
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server/world is invalid
*/
void setInheritGroup(Group group, String server, String world, long expireAt) throws ObjectAlreadyHasException;
/**
* Remove a previously set inheritance rule
*
* @param group the group to uninherit
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group is null
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void unsetInheritGroup(Group group) throws ObjectLacksException;
/**
* Remove a previously set inheritance rule
* @param group the group to uninherit
*
* @param group the group to uninherit
* @param temporary if the group being removed is temporary
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group is null
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void unsetInheritGroup(Group group, boolean temporary) throws ObjectLacksException;
/**
* Remove a previously set inheritance rule on a specific server
* @param group the group to uninherit
*
* @param group the group to uninherit
* @param server The server to remove the group on
* @throws ObjectLacksException if the group does not already inherit the group on that server
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws ObjectLacksException if the group does not already inherit the group on that server
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server is invalid
*/
void unsetInheritGroup(Group group, String server) throws ObjectLacksException;
/**
* Remove a previously set inheritance rule on a specific server and world
* @param group the group to uninherit
*
* @param group the group to uninherit
* @param server The server to remove the group on
* @param world The world to remove the group on
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @param world The world to remove the group on
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server or world is invalid
*/
void unsetInheritGroup(Group group, String server, String world) throws ObjectLacksException;
/**
* Remove a previously set inheritance rule on a specific server
* @param group the group to uninherit
* @param server The server to remove the group on
*
* @param group the group to uninherit
* @param server The server to remove the group on
* @param temporary if the group being removed is temporary
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server is invalid
*/
void unsetInheritGroup(Group group, String server, boolean temporary) throws ObjectLacksException;
/**
* Remove a previously set inheritance rule on a specific server and world
* @param group the group to uninherit
* @param server The server to remove the group on
* @param world The world to remove the group on
*
* @param group the group to uninherit
* @param server The server to remove the group on
* @param world The world to remove the group on
* @param temporary if the group being removed was set temporarily
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws ObjectLacksException if the group does not already inherit the group
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server/world is invalid
*/
void unsetInheritGroup(Group group, String server, String world, boolean temporary) throws ObjectLacksException;
/**
* Clear all of the groups permission nodes
*/
void clearNodes();
/**
* Get a {@link List} of all of the groups the group inherits, on all servers
*
* @return a {@link List} of group names
*/
List<String> getGroupNames();
/**
* Get a {@link List} of the groups the group inherits on a specific server
*
* @param server the server to check
* @param world the world to check
* @return a {@link List} of group names
* @throws NullPointerException if the server or world is null
* @throws NullPointerException if the server is null
* @throws IllegalArgumentException if the server is invalid
*/
List<String> getLocalGroups(String server);
/**
* Get a {@link List} of the groups the group inherits on a specific server and world
*
* @param server the server to check
* @param world the world to check
* @return a {@link List} of group names
* @throws NullPointerException if the server or world is null
* @throws IllegalArgumentException if the server or world is invalid
*/
List<String> getLocalGroups(String server, String world);
/**
* Get a {@link List} of the groups the group inherits on a specific server
* @param server the server to check
* @return a {@link List} of group names
* @throws NullPointerException if the server is null
* @throws IllegalArgumentException if the server is invalid
* Gets the weight of this group, is present.
*
* @return the group weight
* @since 2.17
*/
List<String> getLocalGroups(String server);
OptionalInt getWeight();
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import com.google.common.collect.Multimap;
import java.util.Optional;
import java.util.OptionalLong;
/**
* A relationship between a Holder and a permission
*
* @param <T> the identifier type of the holder
* @since 2.17
*/
public interface HeldPermission<T> {
/**
* Gets the holder of the permission
*
* @return the holder
*/
T getHolder();
/**
* Gets the permission being held
*
* @return the permission
*/
String getPermission();
/**
* Gets the value of the permission
*
* @return the value
*/
boolean getValue();
/**
* Gets the server where the permission is held
*
* @return the server
*/
Optional<String> getServer();
/**
* Gets the world where the permission is held
*
* @return the world
*/
Optional<String> getWorld();
/**
* Gets the time in unix time when the permission will expire
*
* @return the expiry time
*/
OptionalLong getExpiry();
/**
* Gets the context for the permission.
*
* @return the context
*/
Multimap<String, String> getContext();
/**
* Converts this permission into a Node
*
* @return a Node copy of this permission
*/
Node asNode();
}
@@ -28,22 +28,24 @@ import me.lucko.luckperms.api.data.MySQLConfiguration;
import java.util.Map;
/**
* A wrapper interface for the internal LuckPerms configuration, providing read only access.
* Read-only access to the LuckPerms configuration settings
*/
@SuppressWarnings("unused")
public interface LPConfiguration {
/**
* Returns the name of this server
* @return the name of this server
*/
String getServer();
/**
* Returns how often a sync task will run in minutes
* @return how often a sync task will run in minutes
*/
int getSyncTime();
/**
* Returns the default group, in a node representation
* @return the default group, in a node representation
* @deprecated as of 2.6, the default group is always "default"
*/
@@ -51,6 +53,7 @@ public interface LPConfiguration {
String getDefaultGroupNode();
/**
* Returns the name of the default group
* @return the name of the default group
* @deprecated as of 2.6, the default group is always "default"
*/
@@ -58,91 +61,109 @@ public interface LPConfiguration {
String getDefaultGroupName();
/**
* Returns if the users on this server will have their global permissions applied
* @return if the users on this server will have their global permissions applied
*/
boolean getIncludeGlobalPerms();
/**
* Returns if the users on this server will have their global world permissions applied
* @return if the users on this server will have their global world permissions applied
* @since 2.9
*/
boolean getIncludeGlobalWorldPerms();
/**
* Returns true if the platform is applying global groups
* @return true if the platform is applying global groups
* @since 2.9
*/
boolean getApplyGlobalGroups();
/**
* Returns true if the platform is applying global world groups
* @return true if the platform is applying global world groups
* @since 2.9
*/
boolean getApplyGlobalWorldGroups();
/**
* @return the online mode setting in the config
* Returns the online mode setting
* @return the online mode setting
*/
boolean getOnlineMode();
/**
* Returns if LuckPerms is applying wildcard permissions
* @return if LuckPerms is applying wildcard permissions
*/
boolean getApplyWildcards();
/**
* Returns if LuckPerms is resolving and applying regex permissions
* @return if LuckPerms is resolving and applying regex permissions
*/
boolean getApplyRegex();
/**
* Returns if LuckPerms is expanding shorthand permissions
* @return if LuckPerms is expanding shorthand permissions
*/
boolean getApplyShorthand();
/**
* Returns if LuckPerms will send notifications to users when permissions are modified
* @return if LuckPerms will send notifications to users when permissions are modified
* @since 2.7
*/
boolean getLogNotify();
/**
* Returns true if permission checks are being recorded / debugged
* @return true if permission checks are being recorded / debugged
* @since 2.9
* @deprecated as this value is now always false. Functionality was replaced by the verbose command.
*/
@Deprecated
boolean getDebugPermissionChecks();
/**
* Returns true if the vanilla op system is enabled
* @return true if the vanilla op system is enabled
* @since 2.8
*/
boolean getEnableOps();
/**
* Returns true if opped players are allowed to use LuckPerms commands
* @return true if opped players are allowed to use LuckPerms commands
* @since 2.8
*/
boolean getCommandsAllowOp();
/**
* Returns true if auto op is enabled
* @return true if auto op is enabled
* @since 2.9
*/
boolean getAutoOp();
/**
* Returns the name of the server used within Vault operations
* @return the name of the server used within Vault operations
* @since 2.7
*/
String getVaultServer();
/**
* Returns true if global permissions should be considered when retrieving meta or player groups
* @return true if global permissions should be considered when retrieving meta or player groups
* @since 2.7
*/
boolean getVaultIncludeGlobal();
/**
* Returns the database values set in the configuration
* @return the database values set in the configuration
* @deprecated use {@link #getDatastoreConfig()}
*/
@@ -151,24 +172,28 @@ public interface LPConfiguration {
MySQLConfiguration getDatabaseValues();
/**
* Returns the values set for data storage in the configuration
* @return the values set for data storage in the configuration
*/
DatastoreConfiguration getDatastoreConfig();
/**
* Returns the storage method string from the configuration
* @return the storage method string from the configuration
*/
String getStorageMethod();
/**
* Returns true if split storage is enabled
* @return true if split storage is enabled
* @since 2.7
*/
boolean getSplitStorage();
/**
* @return a map of split storage options, where the key is the storage section, and the value is the storage method.
* For example: key = user, value = json
* Returns a map of split storage options
* @return a map of split storage options, where the key is the storage section, and the value is the storage
* method. For example: key = user, value = json
* @since 2.7
*/
Map<String, String> getSplitStorageOptions();
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
/**
* A node with a traceable origin
*
* @since 2.11
*/
public interface LocalizedNode extends Node {
/**
* Gets the delegate node
*
* @return the node this instance is representing
*/
Node getNode();
/**
* Gets the location where the {@link Node} is inherited from
*
* @return where the node was inherited from. Will not return null.
* @see PermissionHolder#getObjectName()
*/
String getLocation();
}
@@ -28,9 +28,10 @@ import java.util.UUID;
/**
* Represents the internal LuckPerms log.
* All content internally is immutable. You can add to the log using the {@link Datastore}, and then request an updated copy.
*
* <p> The returned instance provides a copy of the data at the time of retrieval. Any changes made to log entries will
* only apply to this instance of the log. You can add to the log using the {@link Storage}, and then request an updated copy.
*/
@SuppressWarnings("unused")
public interface Log {
/**
@@ -45,8 +46,11 @@ public interface Log {
/**
* Gets the recent content separated by page
*
* @param pageNo the page number
* @return the page content
* @throws IllegalArgumentException if the pageNo is less than 1
* @throws IllegalStateException if the log doesn't contain enough entries to populate the page. See {@link #getRecentMaxPages()}}
*/
SortedMap<Integer, LogEntry> getRecent(int pageNo);
@@ -64,9 +68,12 @@ public interface Log {
/**
* Gets the recent content for the uuid, separated into pages
*
* @param pageNo the page number
* @param actor the uuid of the actor to filter by
* @param actor the uuid of the actor to filter by
* @return the page content
* @throws IllegalArgumentException if the pageNo is less than 1
* @throws IllegalStateException if the log doesn't contain enough entries to populate the page. See {@link #getRecentMaxPages(UUID)}}
*/
SortedMap<Integer, LogEntry> getRecent(int pageNo, UUID actor);
@@ -85,9 +92,12 @@ public interface Log {
/**
* Gets the user history content, separated by pages
*
* @param pageNo the page number
* @param uuid the uuid of the acted user to filter by
* @param uuid the uuid of the acted user to filter by
* @return the page content
* @throws IllegalArgumentException if the pageNo is less than 1
* @throws IllegalStateException if the log doesn't contain enough entries to populate the page. See {@link #getUserHistoryMaxPages(UUID)}}
*/
SortedMap<Integer, LogEntry> getUserHistory(int pageNo, UUID uuid);
@@ -97,7 +107,7 @@ public interface Log {
*/
int getUserHistoryMaxPages(UUID uuid);
/**
* @param name the name to filter by
* @return all content in this log where the group = name
@@ -106,9 +116,12 @@ public interface Log {
/**
* Gets the group history content, separated by pages
*
* @param pageNo the page number
* @param name the name of the acted group to filter by
* @param name the name of the acted group to filter by
* @return the page content
* @throws IllegalArgumentException if the pageNo is less than 1
* @throws IllegalStateException if the log doesn't contain enough entries to populate the page. See {@link #getGroupHistoryMaxPages(String)}}
*/
SortedMap<Integer, LogEntry> getGroupHistory(int pageNo, String name);
@@ -127,8 +140,9 @@ public interface Log {
/**
* Gets the track history content, separated by pages
*
* @param pageNo the page number
* @param name the name of the acted track to filter by
* @param name the name of the acted track to filter by
* @return the page content
*/
SortedMap<Integer, LogEntry> getTrackHistory(int pageNo, String name);
@@ -136,9 +150,12 @@ public interface Log {
/**
* @param name the name to filter by
* @return the max page number allowed in the {@link #getTrackHistory(int, String)} method
* @throws IllegalArgumentException if the pageNo is less than 1
* @throws IllegalStateException if the log doesn't contain enough entries to populate the page. See {@link #getTrackHistoryMaxPages(String)}}
*/
int getTrackHistoryMaxPages(String name);
/**
* @param query the query to filter by
* @return all content in this log where the content matches query
@@ -147,9 +164,12 @@ public interface Log {
/**
* Gets the search content, separated by pages
*
* @param pageNo the page number
* @param query the query to filter by
* @param query the query to filter by
* @return the page content
* @throws IllegalArgumentException if the pageNo is less than 1
* @throws IllegalStateException if the log doesn't contain enough entries to populate the page. See {@link #getSearchMaxPages(String)}}
*/
SortedMap<Integer, LogEntry> getSearch(int pageNo, String query);
@@ -25,16 +25,17 @@ package me.lucko.luckperms.api;
import java.util.UUID;
/**
* Represents a single entry in a log
* A single entry in the log
*
* <p> Implements {@link Comparable} ordering based upon the timestamp of the entry.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class LogEntry implements Comparable<LogEntry> {
private static final String FORMAT = "&8(&e%s&8) [&a%s&8] (&b%s&8) &7--> &f%s";
public static LogEntryBuilder builder() {
return new LogEntryBuilder();
}
private static final String FORMAT = "&8(&e%s&8) [&a%s&8] (&b%s&8) &7--> &f%s";
private long timestamp;
private UUID actor;
private String actorName;
@@ -100,63 +101,69 @@ public class LogEntry implements Comparable<LogEntry> {
return timestamp;
}
public UUID getActor() {
return actor;
}
public String getActorName() {
return actorName;
}
public char getType() {
return type;
}
public UUID getActed() {
return acted;
}
public String getActedName() {
return actedName;
}
public String getAction() {
return action;
}
void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public UUID getActor() {
return actor;
}
void setActor(UUID actor) {
this.actor = actor;
}
public String getActorName() {
return actorName;
}
void setActorName(String actorName) {
this.actorName = actorName;
}
public char getType() {
return type;
}
void setType(char type) {
this.type = type;
}
public UUID getActed() {
return acted;
}
void setActed(UUID acted) {
this.acted = acted;
}
public String getActedName() {
return actedName;
}
void setActedName(String actedName) {
this.actedName = actedName;
}
public String getAction() {
return action;
}
void setAction(String action) {
this.action = action;
}
@Override
public String toString() {
return "LogEntry(timestamp=" + this.getTimestamp() + ", actor=" + this.getActor() + ", actorName=" +
this.getActorName() + ", type=" + this.getType() + ", acted=" + this.getActed() + ", actedName=" +
this.getActedName() + ", action=" + this.getAction() + ")";
return "LogEntry(" +
"timestamp=" + this.getTimestamp() + ", " +
"actor=" + this.getActor() + ", " +
"actorName=" + this.getActorName() + ", " +
"type=" + this.getType() + ", " +
"acted=" + this.getActed() + ", " +
"actedName=" + this.getActedName() + ", " +
"action=" + this.getAction() +
")";
}
@Override
@@ -165,41 +172,25 @@ public class LogEntry implements Comparable<LogEntry> {
if (!(o instanceof LogEntry)) return false;
final LogEntry other = (LogEntry) o;
if (this.getTimestamp() != other.getTimestamp()) return false;
final Object this$actor = this.getActor();
final Object other$actor = other.getActor();
if (this$actor == null ? other$actor != null : !this$actor.equals(other$actor)) return false;
final Object this$actorName = this.getActorName();
final Object other$actorName = other.getActorName();
if (this$actorName == null ? other$actorName != null : !this$actorName.equals(other$actorName)) return false;
if (this.getActor() == null ? other.getActor() != null : !this.getActor().equals(other.getActor())) return false;
if (this.getActorName() == null ? other.getActorName() != null : !this.getActorName().equals(other.getActorName())) return false;
if (this.getType() != other.getType()) return false;
final Object this$acted = this.getActed();
final Object other$acted = other.getActed();
if (this$acted == null ? other$acted != null : !this$acted.equals(other$acted)) return false;
final Object this$actedName = this.getActedName();
final Object other$actedName = other.getActedName();
if (this$actedName == null ? other$actedName != null : !this$actedName.equals(other$actedName)) return false;
final Object this$action = this.getAction();
final Object other$action = other.getAction();
return this$action == null ? other$action == null : this$action.equals(other$action);
if (this.getActed() == null ? other.getActed() != null : !this.getActed().equals(other.getActed())) return false;
if (this.getActedName() == null ? other.getActedName() != null : !this.getActedName().equals(other.getActedName())) return false;
return this.getAction() == null ? other.getAction() == null : this.getAction().equals(other.getAction());
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final long $timestamp = this.getTimestamp();
result = result * PRIME + (int) ($timestamp >>> 32 ^ $timestamp);
final Object $actor = this.getActor();
result = result * PRIME + ($actor == null ? 43 : $actor.hashCode());
final Object $actorName = this.getActorName();
result = result * PRIME + ($actorName == null ? 43 : $actorName.hashCode());
result = result * PRIME + (int) (this.getTimestamp() >>> 32 ^ this.getTimestamp());
result = result * PRIME + (this.getActor() == null ? 43 : this.getActor().hashCode());
result = result * PRIME + (this.getActorName() == null ? 43 : this.getActorName().hashCode());
result = result * PRIME + this.getType();
final Object $acted = this.getActed();
result = result * PRIME + ($acted == null ? 43 : $acted.hashCode());
final Object $actedName = this.getActedName();
result = result * PRIME + ($actedName == null ? 43 : $actedName.hashCode());
final Object $action = this.getAction();
result = result * PRIME + ($action == null ? 43 : $action.hashCode());
result = result * PRIME + (this.getActed() == null ? 43 : this.getActed().hashCode());
result = result * PRIME + (this.getActedName() == null ? 43 : this.getActedName().hashCode());
result = result * PRIME + (this.getAction() == null ? 43 : this.getAction().hashCode());
return result;
}
@@ -214,6 +205,7 @@ public class LogEntry implements Comparable<LogEntry> {
protected LogEntryBuilder getThis() {
return this;
}
}
public static abstract class AbstractLogEntryBuilder<T extends LogEntry, B extends AbstractLogEntryBuilder<T, B>> {
@@ -226,6 +218,7 @@ public class LogEntry implements Comparable<LogEntry> {
}
protected abstract T createObj();
protected abstract B getThis();
public long getTimestamp() {
@@ -310,9 +303,15 @@ public class LogEntry implements Comparable<LogEntry> {
@Override
public String toString() {
return "LogEntry.LogEntryBuilder(timestamp=" + getTimestamp() + ", actor=" + getActor() + ", actorName=" +
getActorName() + ", type=" + getType() + ", acted=" + getActed() + ", actedName=" + getActedName() +
", action=" + getAction() + ")";
return "LogEntry.LogEntryBuilder(" +
"timestamp=" + this.getTimestamp() + ", " +
"actor=" + this.getActor() + ", " +
"actorName=" + this.getActorName() + ", " +
"type=" + this.getType() + ", " +
"acted=" + this.getActed() + ", " +
"actedName=" + this.getActedName() + ", " +
"action=" + this.getAction() +
")";
}
}
@@ -23,15 +23,17 @@
package me.lucko.luckperms.api;
/**
* A wrapper interface for platform logger instances.
* A wrapper interface for platform logger instances
*
* <p> Bukkit/Bungee both use java.util.logging, and Sponge uses org.slf4j. This class wraps those classes so the commons
* module can access a logger.
* <p> Bukkit/Bungee both use java.util.logging, and Sponge uses org.slf4j. This class wraps those classes so the
* commons module can access a logger.
*/
public interface Logger {
void info(String s);
void warn(String s);
void severe(String s);
}
@@ -22,6 +22,9 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.context.ContextListener;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.IContextCalculator;
import me.lucko.luckperms.api.event.LPListener;
import java.util.Optional;
@@ -29,9 +32,8 @@ import java.util.Set;
import java.util.UUID;
/**
* The root API interface in LuckPerms
* The root API interface for LuckPerms
*/
@SuppressWarnings("unused")
public interface LuckPermsApi {
/**
@@ -58,6 +60,7 @@ public interface LuckPermsApi {
/**
* Registers a listener to be sent LuckPerms events
*
* @param listener the listener instance
* @throws NullPointerException if the listener is null
*/
@@ -65,6 +68,7 @@ public interface LuckPermsApi {
/**
* Unregisters a previously registered listener from the EventBus
*
* @param listener the listener instance to unregister
* @throws NullPointerException if the listener is null
*/
@@ -72,30 +76,53 @@ public interface LuckPermsApi {
/**
* Gets a wrapped {@link LPConfiguration} instance, with read only access
*
* @return a configuration instance
*/
LPConfiguration getConfiguration();
/**
* Gets a wrapped {@link Datastore} instance, with somewhat limited access
* @return a datastore instance
* Gets a wrapped {@link Storage} instance.
*
* @return a storage instance
* @since 2.14
*/
Storage getStorage();
/**
* Gets a wrapped Datastore instance.
*
* @return a datastore instance
* @deprecated in favour of {@link #getStorage()}
*/
@SuppressWarnings("deprecation")
@Deprecated
Datastore getDatastore();
/**
* Gets the messaging service in use on the platform, if present.
*
* @return an optional that may contain a messaging service instance.
*/
Optional<MessagingService> getMessagingService();
/**
* Gets the {@link Logger} wrapping used by the platform
*
* @return the logger instance
*/
Logger getLogger();
/**
* Gets a wrapped {@link UuidCache} instance, providing read access to the LuckPerms internal uuid caching system
* @return a uuidcache instance
*
* @return a uuid cache instance
*/
UuidCache getUuidCache();
/**
* Gets a wrapped user object from the user storage
*
* @param uuid the uuid of the user to get
* @return a {@link User} object, if one matching the uuid is loaded, or null if not
* @throws NullPointerException if the uuid is null
@@ -104,6 +131,7 @@ public interface LuckPermsApi {
/**
* Gets a wrapped user object from the user storage. This method does not return null, unlike {@link #getUser(UUID)}
*
* @param uuid the uuid of the user to get
* @return an optional {@link User} object
* @throws NullPointerException if the uuid is null
@@ -112,6 +140,7 @@ public interface LuckPermsApi {
/**
* Gets a wrapped user object from the user storage
*
* @param name the username of the user to get
* @return a {@link User} object, if one matching the uuid is loaded, or null if not
* @throws NullPointerException if the name is null
@@ -119,7 +148,9 @@ public interface LuckPermsApi {
User getUser(String name);
/**
* Gets a wrapped user object from the user storage. This method does not return null, unlike {@link #getUser(String)}
* Gets a wrapped user object from the user storage. This method does not return null, unlike {@link
* #getUser(String)}
*
* @param name the username of the user to get
* @return an optional {@link User} object
* @throws NullPointerException if the name is null
@@ -128,12 +159,14 @@ public interface LuckPermsApi {
/**
* Gets a set of all loaded users.
*
* @return a {@link Set} of {@link User} objects
*/
Set<User> getUsers();
/**
* Check if a user is loaded in memory
*
* @param uuid the uuid to check for
* @return true if the user is loaded
* @throws NullPointerException if the uuid is null
@@ -142,6 +175,7 @@ public interface LuckPermsApi {
/**
* Unload a user from the internal storage, if they're not currently online.
*
* @param user the user to unload
* @throws NullPointerException if the user is null
* @since 2.6
@@ -150,6 +184,7 @@ public interface LuckPermsApi {
/**
* Gets a wrapped group object from the group storage
*
* @param name the name of the group to get
* @return a {@link Group} object, if one matching the name exists, or null if not
* @throws NullPointerException if the name is null
@@ -158,6 +193,7 @@ public interface LuckPermsApi {
/**
* Gets a wrapped group object from the group storage. This method does not return null, unlike {@link #getGroup}
*
* @param name the name of the group to get
* @return an optional {@link Group} object
* @throws NullPointerException if the name is null
@@ -166,12 +202,14 @@ public interface LuckPermsApi {
/**
* Gets a set of all loaded groups.
*
* @return a {@link Set} of {@link Group} objects
*/
Set<Group> getGroups();
/**
* Check if a group is loaded in memory
*
* @param name the name to check for
* @return true if the group is loaded
* @throws NullPointerException if the name is null
@@ -180,6 +218,7 @@ public interface LuckPermsApi {
/**
* Gets a wrapped track object from the track storage
*
* @param name the name of the track to get
* @return a {@link Track} object, if one matching the name exists, or null if not
* @throws NullPointerException if the name is null
@@ -188,6 +227,7 @@ public interface LuckPermsApi {
/**
* Gets a wrapped track object from the track storage. This method does not return null, unlike {@link #getTrack}
*
* @param name the name of the track to get
* @return an optional {@link Track} object
* @throws NullPointerException if the name is null
@@ -196,26 +236,71 @@ public interface LuckPermsApi {
/**
* Gets a set of all loaded tracks.
*
* @return a {@link Set} of {@link Track} objects
*/
Set<Track> getTracks();
/**
* Check if a track is loaded in memory
*
* @param name the name to check for
* @return true if the track is loaded
* @throws NullPointerException if the name is null
*/
boolean isTrackLoaded(String name);
/**
* Gets the node factory instance for the platform
*
* @return the node factory
*/
NodeFactory getNodeFactory();
/**
* Returns a permission builder instance
*
* @param permission the main permission node to build
* @return a {@link Node.Builder} instance
* @throws IllegalArgumentException if the permission is invalid
* @throws NullPointerException if the permission is null
* @throws NullPointerException if the permission is null
* @since 2.6
*/
Node.Builder buildNode(String permission) throws IllegalArgumentException;
/**
* Register a custom context calculator to the server
*
* @param contextCalculator the context calculator to register. The type MUST be the player class of the platform.
* @throws ClassCastException if the type is not the player class of the platform.
*/
void registerContextCalculator(IContextCalculator<?> contextCalculator);
/**
* Registers a custom context listener to the server,
*
* @param contextListener the context listener to register. The type MUST be the player class of the platform.
* @throws ClassCastException if the type is not the player class of the platform.
*/
void registerContextListener(ContextListener<?> contextListener);
/**
* Gets a calculated context instance for the user using the rules of the platform.
*
* <p> These values are calculated using the options in the configuration, and the provided calculators.
*
* @param user the user to get contexts for
* @return an optional containing contexts. Will return empty if the user is not online.
*/
Optional<Contexts> getContextForUser(User user);
/**
* Gets set of contexts applicable to a player using the platforms {@link IContextCalculator}s.
*
* @param player the player to calculate for. Must be the player instance for the platform.
* @return a set of contexts.
* @since 2.17
*/
ContextSet getContextForPlayer(Object player);
}
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
/**
* A means to push changes to other servers using the platforms networking
*
* @since 2.14
*/
public interface MessagingService {
/**
* Uses the messaging service to inform other servers about changes.
*
* <p> This will push the update asynchronously, and this method will return immediately. Calling this method is
* equivalent to running "/lp networksync", except will not sync this server.
*/
void pushUpdate();
}
@@ -32,14 +32,14 @@ import java.util.Set;
/**
* A collection of utilities to help retrieve meta values for {@link PermissionHolder}s
*
* @since 2.7
*/
public class MetaUtils {
private MetaUtils(){}
/**
* Escapes special characters used within LuckPerms, so the string can be saved without issues
*
* @param s the string to escape
* @return an escaped string
* @throws NullPointerException if the string is null
@@ -48,7 +48,7 @@ public class MetaUtils {
if (s == null) {
throw new NullPointerException();
}
s = s.replace(".", "{SEP}");
s = s.replace("/", "{FSEP}");
s = s.replace("$", "{DSEP}");
@@ -58,6 +58,7 @@ public class MetaUtils {
/**
* Unescapes special characters used within LuckPerms, the inverse of {@link #escapeCharacters(String)}
*
* @param s the string to unescape
* @return an unescaped string
* @throws NullPointerException if the string is null
@@ -66,7 +67,7 @@ public class MetaUtils {
if (s == null) {
throw new NullPointerException();
}
s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$");
@@ -76,19 +77,20 @@ public class MetaUtils {
/**
* Sets a meta value on a holder
*
* @param holder the holder to apply the meta node to
* @param server the server to apply the meta on, can be null
* @param world the world to apply the meta on, can be null
* @param node the meta node
* @param value the meta value
* @throws NullPointerException if the holder, node or value is null
* @param world the world to apply the meta on, can be null
* @param node the meta node
* @param value the meta value
* @throws NullPointerException if the holder, node or value is null
* @throws IllegalArgumentException if the node or value is empty
*/
public static void setMeta(PermissionHolder holder, String server, String world, String node, String value) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (node == null) {
throw new NullPointerException("node");
}
@@ -108,7 +110,7 @@ public class MetaUtils {
if (server == null || server.equals("")) {
server = "global";
}
node = escapeCharacters(node);
value = escapeCharacters(value);
@@ -118,11 +120,12 @@ public class MetaUtils {
toRemove.add(n);
}
}
for (Node n : toRemove) {
try {
holder.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
} catch (ObjectLacksException ignored) {
}
}
Node.Builder metaNode = LuckPerms.getApi().buildNode("meta." + node + "." + value).setValue(true);
@@ -135,19 +138,21 @@ public class MetaUtils {
try {
holder.setPermission(metaNode.build());
} catch (ObjectAlreadyHasException ignored) {}
} catch (ObjectAlreadyHasException ignored) {
}
}
/**
* Gets a meta value for the holder
* @param holder the holder to get the meta from
* @param server the server to retrieve the meta on, can be null
* @param world the world to retrieve the meta on, can be null
* @param node the node to get
* @param defaultValue the default value to return if the node is not set
*
* @param holder the holder to get the meta from
* @param server the server to retrieve the meta on, can be null
* @param world the world to retrieve the meta on, can be null
* @param node the node to get
* @param defaultValue the default value to return if the node is not set
* @param includeGlobal if global nodes should be considered when retrieving the meta
* @return a meta string, or the default value if the user does not have the meta node
* @throws NullPointerException if the holder or node is null
* @throws NullPointerException if the holder or node is null
* @throws IllegalArgumentException if the node is empty
*/
public static String getMeta(PermissionHolder holder, String server, String world, String node, String defaultValue, boolean includeGlobal) {
@@ -217,17 +222,19 @@ public class MetaUtils {
try {
holder.setPermission(node.build());
} catch (ObjectAlreadyHasException ignored) {}
} catch (ObjectAlreadyHasException ignored) {
}
}
/**
* Adds a prefix to a holder on a specific server and world
* @param holder the holder to set the prefix for
* @param prefix the prefix value
*
* @param holder the holder to set the prefix for
* @param prefix the prefix value
* @param priority the priority to set the prefix at
* @param server the server to set the prefix on, can be null
* @param world the world to set the prefix on, can be null
* @throws NullPointerException if the holder is null
* @param server the server to set the prefix on, can be null
* @param world the world to set the prefix on, can be null
* @throws NullPointerException if the holder is null
* @throws IllegalArgumentException if the prefix is null or empty
*/
public static void setPrefix(PermissionHolder holder, String prefix, int priority, String server, String world) {
@@ -236,12 +243,13 @@ public class MetaUtils {
/**
* Adds a suffix to a holder on a specific server and world
* @param holder the holder to set the suffix for
* @param suffix the suffix value
*
* @param holder the holder to set the suffix for
* @param suffix the suffix value
* @param priority the priority to set the suffix at
* @param server the server to set the suffix on, can be null
* @param world the world to set the suffix on, can be null
* @throws NullPointerException if the holder is null
* @param server the server to set the suffix on, can be null
* @param world the world to set the suffix on, can be null
* @throws NullPointerException if the holder is null
* @throws IllegalArgumentException if the suffix is null or empty
*/
public static void setSuffix(PermissionHolder holder, String suffix, int priority, String server, String world) {
@@ -258,7 +266,7 @@ public class MetaUtils {
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes()) {
for (Node n : holder.getAllNodes(Contexts.allowAll())) {
if (!n.getValue()) {
continue;
}
@@ -272,7 +280,7 @@ public class MetaUtils {
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
@@ -289,9 +297,10 @@ public class MetaUtils {
/**
* Returns a holders highest priority prefix, if they have one
* @param holder the holder
* @param server the server to retrieve the prefix on
* @param world the world to retrieve the prefix on
*
* @param holder the holder
* @param server the server to retrieve the prefix on
* @param world the world to retrieve the prefix on
* @param includeGlobal if global nodes should be considered when retrieving the prefix
* @return a prefix string, if the holder has one, or an empty string if not.
* @throws NullPointerException if the holder is null
@@ -302,9 +311,10 @@ public class MetaUtils {
/**
* Returns a holders highest priority suffix, if they have one
* @param holder the holder
* @param server the server to retrieve the suffix on
* @param world the world to retrieve the suffix on
*
* @param holder the holder
* @param server the server to retrieve the suffix on
* @param world the world to retrieve the suffix on
* @param includeGlobal if global nodes should be considered when retrieving the suffix
* @return a suffix string, if the holder has one, or an empty string if not.
* @throws NullPointerException if the holder is null
@@ -312,5 +322,8 @@ public class MetaUtils {
public static String getSuffix(PermissionHolder holder, String server, String world, boolean includeGlobal) {
return getChatMeta(false, holder, server, world, includeGlobal);
}
private MetaUtils() {
}
}
@@ -22,17 +22,21 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* Represents an immutable node object
* An immutable permission node
*
* <p> Use {@link LuckPermsApi#buildNode(String)} to get an instance.
*
* @since 2.6
*/
@SuppressWarnings("unused")
public interface Node extends Map.Entry<String, Boolean> {
/**
@@ -42,6 +46,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Get what value the permission is set to. A negated node would return <code>false</code>.
*
* @return the permission's value
*/
@Override
@@ -60,18 +65,21 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* If this node is set to override explicitly.
* This value does not persist across saves, and is therefore only useful for transient nodes
*
* @return true if this node is set to override explicitly
*/
boolean isOverride();
/**
* Gets the server this node applies on, if the node is server specific
*
* @return an {@link Optional} containing the server, if one is defined
*/
Optional<String> getServer();
/**
* Gets the world this node applies on, if the node is world specific
*
* @return an {@link Optional} containing the world, if one is defined
*/
Optional<String> getWorld();
@@ -88,32 +96,72 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* If this node should apply on a specific server
* @param server the name of the server
*
* @param server the name of the server
* @param includeGlobal if global permissions should apply
* @param applyRegex if regex should be applied
* @param applyRegex if regex should be applied
* @return true if the node should apply
*/
boolean shouldApplyOnServer(String server, boolean includeGlobal, boolean applyRegex);
/**
* If this node should apply on a specific world
* @param world the name of the world
*
* @param world the name of the world
* @param includeGlobal if global permissions should apply
* @param applyRegex if regex should be applied
* @param applyRegex if regex should be applied
* @return true if the node should apply
*/
boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex);
/**
* If this node should apply given the specific context
* If this node should apply in the given context
*
* @param context the context key value pairs
* @param worldAndServer if world and server contexts should be checked
* @return true if the node should apply
* @since 2.13
*/
boolean shouldApplyWithContext(ContextSet context, boolean worldAndServer);
/**
* If this node should apply in the given context
*
* @param context the context key value pairs
* @return true if the node should apply
* @since 2.13
*/
boolean shouldApplyWithContext(Map<String, String> context);
boolean shouldApplyWithContext(ContextSet context);
/**
* If this node should apply in the given context
*
* @param context the context key value pairs
* @param worldAndServer if world and server contexts should be checked
* @return true if the node should apply
* @deprecated in favour of {@link #shouldApplyWithContext(ContextSet, boolean)}
*/
@Deprecated
default boolean shouldApplyWithContext(Map<String, String> context, boolean worldAndServer) {
return shouldApplyWithContext(ContextSet.fromMap(context), worldAndServer);
}
/**
* If this node should apply in the given context
*
* @param context the context key value pairs
* @return true if the node should apply
* @deprecated in favour of {@link #shouldApplyWithContext(ContextSet)}
*/
@Deprecated
default boolean shouldApplyWithContext(Map<String, String> context) {
return shouldApplyWithContext(ContextSet.fromMap(context));
}
/**
* Similar to {@link #shouldApplyOnServer(String, boolean, boolean)}, except this method accepts a List
* @param servers the list of servers
*
* @param servers the list of servers
* @param includeGlobal if global permissions should apply
* @return true if the node should apply
*/
@@ -121,7 +169,8 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Similar to {@link #shouldApplyOnWorld(String, boolean, boolean)}, except this method accepts a List
* @param worlds the list of world
*
* @param worlds the list of world
* @param includeGlobal if global permissions should apply
* @return true if the node should apply
*/
@@ -129,6 +178,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Resolves a list of wildcards that match this node
*
* @param possibleNodes a list of possible permission nodes
* @return a list of permissions that match this wildcard
*/
@@ -136,6 +186,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Resolves any shorthand parts of this node and returns the full list
*
* @return a list of full nodes
*/
List<String> resolveShorthand();
@@ -178,11 +229,22 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* @return the extra contexts required for this node to apply
* @deprecated in favour of {@link #getContexts()}
*/
Map<String, String> getExtraContexts();
@Deprecated
default Map<String, String> getExtraContexts() {
return getContexts().toMap();
}
/**
* @return the extra contexts required for this node to apply
* @since 2.13
*/
ContextSet getContexts();
/**
* Converts this node into a serialized form
*
* @return a serialized node string
*/
String toSerializedNode();
@@ -205,6 +267,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Gets the level of this wildcard, higher is more specific
*
* @return the wildcard level
* @throws IllegalStateException if this is not a wildcard
*/
@@ -217,6 +280,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Gets the meta value from this node
*
* @return the meta value
* @throws IllegalStateException if this node is not a meta node
*/
@@ -229,6 +293,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Gets the prefix value from this node
*
* @return the prefix value
* @throws IllegalStateException if this node is a not a prefix node
*/
@@ -241,6 +306,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Gets the suffix value from this node
*
* @return the suffix value
* @throws IllegalStateException if this node is a not a suffix node
*/
@@ -248,6 +314,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Checks if this Node is equal to another node
*
* @param obj the other node
* @return true if this node is equal to the other provided
* @see #equalsIgnoringValue(Node) for a less strict implementation of this method
@@ -256,6 +323,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the value
*
* @param other the other node
* @return true if the two nodes are almost equal
*/
@@ -263,6 +331,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the expiry time or value
*
* @param other the other node
* @return true if the two nodes are almost equal
*/
@@ -270,6 +339,7 @@ public interface Node extends Map.Entry<String, Boolean> {
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the value or if the node is temporary
*
* @param other the other node
* @return true if the two nodes are almost equal
* @since 2.8
@@ -281,6 +351,7 @@ public interface Node extends Map.Entry<String, Boolean> {
*/
interface Builder {
Builder setNegated(boolean negated);
Builder setValue(boolean value);
/**
@@ -290,11 +361,21 @@ public interface Node extends Map.Entry<String, Boolean> {
Builder setOverride(boolean override);
Builder setExpiry(long expireAt);
Builder setWorld(String world);
Builder setServer(String server) throws IllegalArgumentException;
Builder withExtraContext(String key, String value);
Builder withExtraContext(Map<String, String> map);
Builder withExtraContext(Set<Map.Entry<String, String>> context);
Builder withExtraContext(Map.Entry<String, String> entry);
Builder withExtraContext(ContextSet set);
Node build();
}
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
/**
* Builds {@link Node} instances
*
* @since 2.17
*/
public interface NodeFactory {
/**
* Creates a node from a serialised node string
*
* @param serialisedPermission the serialised permission string
* @param value the value of the node
* @return a node instance
* @throws NullPointerException if the permission is null
*/
Node fromSerialisedNode(String serialisedPermission, boolean value);
/**
* Creates a new node builder from a given base permission string
*
* @param permission the permission
* @return a node builder instance
* @throws NullPointerException if the permission is null
*/
Node.Builder newBuilder(String permission);
/**
* Creates a node builder instance from an existing node
*
* @param other the other node
* @return a node builder instance
* @throws NullPointerException if the other node is null
*/
Node.Builder newBuilderFromExisting(Node other);
/**
* Creates a node builder from a serialised node string
* @param serialisedPermission the serialised permission string
* @param value the value of the node
* @return a node builder instance
* @throws NullPointerException if the permission is null
*/
Node.Builder newBuilderFromSerialisedNode(String serialisedPermission, boolean value);
/**
* Creates a node builder from a key value pair
*
* @param key the key
* @param value the value
* @return a node builder instance
* @throws NullPointerException if the key or value is null
*/
Node.Builder makeMetaNode(String key, String value);
/**
* Creates a node builder from a prefix string and priority
*
* @param priority the priority
* @param prefix the prefix string
* @return a node builder instance
* @throws NullPointerException if the prefix is null
*/
Node.Builder makePrefixNode(int priority, String prefix);
/**
* Creates a node builder from a prefix string and priority
*
* @param priority the priority
* @param suffix the suffix string
* @return a node builder instance
* @throws NullPointerException if the suffix is null
*/
Node.Builder makeSuffixNode(int priority, String suffix);
}
@@ -31,56 +31,109 @@ import java.util.Set;
import java.util.SortedSet;
/**
* Interface for internal PermissionHolder (user/group) instances
* An object capable of holding permissions
*
* <p> Any changes made will be lost unless the instance is saved back to the {@link Storage}.
*/
@SuppressWarnings("unused")
public interface PermissionHolder {
/**
* @return the identifier for this object. either a uuid string or name
* However, you should really just use {@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()}
* Gets the objects name
*
* <p> {@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} should normally be used instead of
* this method.
*
* @return the identifier for this object. Either a uuid string or name.
*/
String getObjectName();
/**
* Gets an immutable Set of the objects permission nodes
* Gets a sorted set of all held permissions.
*
* @return an immutable set of permissions in priority order
* @since 2.6
*/
SortedSet<Node> getPermissions();
SortedSet<? extends Node> getPermissions();
/**
* Similar to {@link #getPermissions()}, except excluding transient permissions
* Similar to {@link #getPermissions()}, except without transient permissions
*
* @return a set of nodes
* @since 2.6
*/
Set<Node> getEnduringPermissions();
Set<? extends Node> getEnduringPermissions();
/**
* Similar to {@link #getPermissions()}, except excluding non-transient permissions
* Gets an immutable set of all transiently held permissions.
*
* <p> Transient permissions only exist for the duration of the session.
*
* @return a set of nodes
* @since 2.6
*/
Set<Node> getTransientPermissions();
Set<? extends Node> getTransientPermissions();
/**
* Gets an immutable set of the nodes that this object has and inherits
*
* @return an immutable set of permissions
* @since 2.6
* @deprecated in favour of {@link #getAllNodes(Contexts)}
*/
@Deprecated
Set<Node> getAllNodes();
/**
* Gets a mutable sorted set of the nodes that this object has and inherits, filtered by context
* Unlike {@link #getAllNodesFiltered(Contexts)}, this method will not filter individual nodes. The context is only
* used to determine which groups should apply.
* Nodes are sorted into priority order.
*
* @param contexts the context for the lookup,
* @return a mutable sorted set of permissions
* @throws NullPointerException if the context is null
* @since 2.11
*/
SortedSet<LocalizedNode> getAllNodes(Contexts contexts);
/**
* Gets a mutable set of the nodes that this object has and inherits, filtered by context.
* Unlike {@link #getAllNodes(Contexts)}, this method WILL filter individual nodes, and only return ones that fully
* meet the context provided.
*
* @param contexts the context for the lookup
* @return a mutable set of permissions
* @throws NullPointerException if the context is null
* @since 2.11
*/
Set<LocalizedNode> getAllNodesFiltered(Contexts contexts);
/**
* Converts the output of {@link #getAllNodesFiltered(Contexts)}, and expands shorthand permissions.
* @param contexts the context for the lookup
* @param lowerCase if the keys should be made lowercase whilst being exported
* @return a mutable map of permissions
*/
Map<String, Boolean> exportNodes(Contexts contexts, boolean lowerCase);
/**
* Gets an immutable Map of the objects permission nodes
*
* @return an immutable map of permissions
* @deprecated in favour of {@link #getPermissions()}
*/
@Deprecated
Map<String, Boolean> getNodes();
/**
* Removes temporary permissions that have expired
*/
void auditTemporaryPermissions();
/**
* Checks to see if the object has a certain permission
*
* @param node the node to check for
* @return a Tristate for the holders permission status for the node
* @throws NullPointerException if the node is null
@@ -90,6 +143,7 @@ public interface PermissionHolder {
/**
* Checks to see if the object has a certain permission
*
* @param node the node to check for
* @return a Tristate for the holders permission status for the node
* @throws NullPointerException if the node is null
@@ -99,75 +153,82 @@ public interface PermissionHolder {
/**
* Checks to see if the object has a certain permission
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param b If the node is true/false(negated)
* @return true if the object has the permission
* @throws NullPointerException if the node is null
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
*/
boolean hasPermission(String node, boolean b);
/**
* Checks to see the the object has a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @return true if the object has the permission
* @throws NullPointerException if the node or server is null
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
*/
boolean hasPermission(String node, boolean b, String server);
/**
* Checks to see the the object has a permission on a certain server and world
* @param node The permission node
* @param b If the node is true/false(negated)
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
* @param world The world
* @return true if the object has the permission
* @throws NullPointerException if the node, server or world is null
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
*/
boolean hasPermission(String node, boolean b, String server, String world);
/**
* Checks to see the the object has a permission
* @param node The permission node
* @param b If the node is true/false(negated)
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param temporary if the permission is temporary
* @return true if the object has the permission
* @throws NullPointerException if the node is null
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
*/
boolean hasPermission(String node, boolean b, boolean temporary);
/**
* Checks to see the the object has a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server to check on
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server to check on
* @param temporary if the permission is temporary
* @return true if the object has the permission
* @throws NullPointerException if the node or server is null
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
*/
boolean hasPermission(String node, boolean b, String server, boolean temporary);
/**
* Checks to see the the object has a permission on a certain server and world
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server to check on
* @param world The world to check on
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server to check on
* @param world The world to check on
* @param temporary if the permission is temporary
* @return true if the object has the permission
* @throws NullPointerException if the node, server or world is null
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
*/
boolean hasPermission(String node, boolean b, String server, String world, boolean temporary);
/**
* Cheks to see if the object inherits a certain permission
*
* @param node the node to check for
* @return a Tristate for the holders inheritance status for the node
* @throws NullPointerException if the node is null
@@ -177,78 +238,85 @@ public interface PermissionHolder {
/**
* Checks to see if the object inherits a certain permission
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param b If the node is true/false(negated)
* @return true if the object inherits the permission
* @throws NullPointerException if the node is null
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
*/
boolean inheritsPermission(String node, boolean b);
/**
* Checks to see the the object inherits a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @return true if the object inherits the permission
* @throws NullPointerException if the node or server is null
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
*/
boolean inheritsPermission(String node, boolean b, String server);
/**
* Checks to see the the object inherits a permission on a certain server and world
* @param node The permission node
* @param b If the node is true/false(negated)
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
* @param world The world
* @return true if the object inherits the permission
* @throws NullPointerException if the node, server or world is null
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node server or world is invalid
*/
boolean inheritsPermission(String node, boolean b, String server, String world);
/**
* Checks to see if the object inherits a permission
* @param node The permission node
* @param b If the node is true/false(negated)
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param temporary if the permission is temporary
* @return true if the object inherits the permission
* @throws NullPointerException if the node is null
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
*/
boolean inheritsPermission(String node, boolean b, boolean temporary);
/**
* Checks to see if the object inherits a permission on a certain server
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param temporary if the permission is temporary
* @return true if the object inherits the permission
* @throws NullPointerException if the node or server is null
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
*/
boolean inheritsPermission(String node, boolean b, String server, boolean temporary);
/**
* Checks to see if the object inherits a permission on a certain server and world
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
*
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
* @param temporary if the permission is temporary
* @return true if the object inherits the permission
* @throws NullPointerException if the node, server or world is null
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world if invalid
*/
boolean inheritsPermission(String node, boolean b, String server, String world, boolean temporary);
/**
* Sets a permission for the object
*
* @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @throws NullPointerException if the node is null
* @since 2.6
*/
void setPermission(Node node) throws ObjectAlreadyHasException;
@@ -264,20 +332,22 @@ public interface PermissionHolder {
* want it to persist, and have to worry about removing it when they log out.
*
* <p> For unsetting a transient permission, see {@link #unsetTransientPermission(Node)}
*
* @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @throws NullPointerException if the node is null
* @since 2.6
*/
void setTransientPermission(Node node) throws ObjectAlreadyHasException;
/**
* Sets a permission for the object
* @param node The node to be set
*
* @param node The node to be set
* @param value What to set the node to - true/false(negated)
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
@@ -285,12 +355,13 @@ public interface PermissionHolder {
/**
* Sets a permission for the object on a specific server
* @param node The node to set
* @param value What to set the node to - true/false(negated)
*
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
@@ -298,13 +369,14 @@ public interface PermissionHolder {
/**
* Sets a permission for the object on a specific server and world
* @param node The node to set
* @param value What to set the node to - true/false(negated)
*
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @param world The world to set the permission on
* @param world The world to set the permission on
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
@@ -312,12 +384,13 @@ public interface PermissionHolder {
/**
* Sets a temporary permission for the object
* @param node The node to set
* @param value What to set the node to - true/false(negated)
*
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param expireAt The time in unixtime when the permission will expire
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid or if the expiry time is in the past
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid or if the expiry time is in the past
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
@@ -325,13 +398,14 @@ public interface PermissionHolder {
/**
* Sets a temporary permission for the object on a specific server
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
*
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @param expireAt The time in unixtime when the permission will expire
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node/server is invalid or if the expiry time is in the past
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node/server is invalid or if the expiry time is in the past
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
@@ -339,14 +413,15 @@ public interface PermissionHolder {
/**
* Sets a temporary permission for the object on a specific server and world
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @param world The world to set the permission on
*
* @param node The node to set
* @param value What to set the node to - true/false(negated)
* @param server The server to set the permission on
* @param world The world to set the permission on
* @param expireAt The time in unixtime when the permission will expire
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node/server/world is invalid, or if the expiry time is in the past
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node/server/world is invalid, or if the expiry time is in the past
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
@@ -354,6 +429,7 @@ public interface PermissionHolder {
/**
* Unsets a permission for the object
*
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
@@ -363,6 +439,7 @@ public interface PermissionHolder {
/**
* Unsets a transient permission for the object
*
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
@@ -372,10 +449,11 @@ public interface PermissionHolder {
/**
* Unsets a permission for the object
* @param node The node to be unset
*
* @param node The node to be unset
* @param temporary if the permission being removed is temporary
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
* @deprecated in favour of {@link #unsetPermission(Node)}
*/
@@ -384,9 +462,10 @@ public interface PermissionHolder {
/**
* Unsets a permission for the object
*
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
* @deprecated in favour of {@link #unsetPermission(Node)}
*/
@@ -395,10 +474,11 @@ public interface PermissionHolder {
/**
* Unsets a permission for the object on a specific server
* @param node The node to be unset
*
* @param node The node to be unset
* @param server The server to unset the node on
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node or server is null
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
* @deprecated in favour of {@link #unsetPermission(Node)}
*/
@@ -407,11 +487,12 @@ public interface PermissionHolder {
/**
* Unsets a permission for the object on a specific server and world
* @param node The node to be unset
*
* @param node The node to be unset
* @param server The server to unset the node on
* @param world The world to unset the node on
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node, server or world is null
* @param world The world to unset the node on
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
* @deprecated in favour of {@link #unsetPermission(Node)}
*/
@@ -420,11 +501,12 @@ public interface PermissionHolder {
/**
* Unsets a permission for the object on a specific server
* @param node The node to be unset
* @param server The server to unset the node on
*
* @param node The node to be unset
* @param server The server to unset the node on
* @param temporary if the permission being unset is temporary
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node or server is null
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
* @deprecated in favour of {@link #unsetPermission(Node)}
*/
@@ -433,24 +515,129 @@ public interface PermissionHolder {
/**
* Unsets a permission for the object on a specific server and world
* @param node The node to be unset
* @param server The server to unset the node on
* @param world The world to unset the node on
*
* @param node The node to be unset
* @param server The server to unset the node on
* @param world The world to unset the node on
* @param temporary if the permission being unset is temporary
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node, server or world is null
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
* @deprecated in favour of {@link #unsetPermission(Node)}
*/
@Deprecated
void unsetPermission(String node, String server, String world, boolean temporary) throws ObjectLacksException;
/**
* Clears all nodes held by the object
*
* @since 2.17
*/
void clearNodes();
/**
* Clears all nodes held by the object on a specific server
*
* @param server the server to filter by, can be null
* @since 2.17
*/
void clearNodes(String server);
/**
* Clears all nodes held by the object on a specific server and world
*
* @param server the server to filter by, can be null
* @param world the world to filter by, can be null
* @since 2.17
*/
void clearNodes(String server, String world);
/**
* Clears all parent groups
*
* @since 2.17
*/
void clearParents();
/**
* Clears all parents on a specific server
*
* @param server the server to filter by, can be null
* @since 2.17
*/
void clearParents(String server);
/**
* Clears all parents on a specific server and world
*
* @param server the server to filter by, can be null
* @param world the world to filter by, can be null
* @since 2.17
*/
void clearParents(String server, String world);
/**
* Clears all meta held by the object
*
* @since 2.17
*/
void clearMeta();
/**
* Clears all meta held by the object on a specific server
*
* @param server the server to filter by, can be null
* @since 2.17
*/
void clearMeta(String server);
/**
* Clears all meta held by the object on a specific server and world
*
* @param server the server to filter by, can be null
* @param world the world to filter by, can be null
* @since 2.17
*/
void clearMeta(String server, String world);
/**
* Clears all meta for a given key.
*
* @param key the meta key
* @param server the server to filter by, can be null
* @param world the world to filter by, can be null
* @param temporary whether the query is for temporary nodes or not.
*/
void clearMetaKeys(String key, String server, String world, boolean temporary);
/**
* Clears all transient permissions the holder has.
*/
void clearTransientNodes();
/**
* Processes the nodes and returns the non-temporary ones.
*
* @return a set of permanent nodes
* @since 2.6
*/
Set<Node> getPermanentPermissionNodes();
/**
* Processes the nodes and returns the temporary ones.
*
* @return a set of temporary nodes
* @since 2.6
*/
Set<Node> getTemporaryPermissionNodes();
/**
* Gets the permissions and inherited permissions that apply to a specific server and world
* @param server The server to get nodes for
* @param world The world to get nodes for
*
* @param server The server to get nodes for
* @param world The world to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @param possibleNodes A list of possible permission nodes for wildcard permission handling
* @param possibleNodes A list of possible permission nodes for wildcard permission handling
* @return a {@link Map} of the permissions
* @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)}
*/
@@ -459,8 +646,9 @@ public interface PermissionHolder {
/**
* Gets the permissions and inherited permissions that apply to a specific server and world
* @param server The server to get nodes for
* @param world The world to get nodes for
*
* @param server The server to get nodes for
* @param world The world to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @return a {@link Map} of the permissions
* @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)}
@@ -470,9 +658,10 @@ public interface PermissionHolder {
/**
* Gets the permissions and inherited permissions that apply to a specific server
* @param server The server to get nodes for
*
* @param server The server to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @param possibleNodes A list of possible permission nodes for wildcard permission handling
* @param possibleNodes A list of possible permission nodes for wildcard permission handling
* @return a {@link Map} of the permissions
* @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)}
*/
@@ -481,7 +670,8 @@ public interface PermissionHolder {
/**
* Gets the permissions and inherited permissions that apply to a specific server
* @param server The server to get nodes for
*
* @param server The server to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @return a {@link Map} of the permissions
* @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)}
@@ -491,50 +681,36 @@ public interface PermissionHolder {
/**
* Convert the holders nodes into a Map of permissions to be applied on the platform
* @param server the server
* @param world the world
* @param extraContext any extra context to filter by
*
* @param server the server
* @param world the world
* @param extraContext any extra context to filter by
* @param includeGlobal whether to include global nodes
* @param possibleNodes a list of possible permissions for resolving wildcards
* @param applyGroups if inherited group permissions should be included
* @param applyGroups if inherited group permissions should be included
* @return a map of permissions
* @since 2.6
* @deprecated in favour of {@link #getAllNodesFiltered(Contexts)}
*/
@Deprecated
Map<String, Boolean> getPermissions(String server, String world, Map<String, String> extraContext, boolean includeGlobal, List<String> possibleNodes, boolean applyGroups);
/**
* Processes the nodes and returns the temporary ones.
*
* @return a map of temporary nodes
* @deprecated in favour of {@link #getTemporaryPermissionNodes()}
*/
@Deprecated
Map<Map.Entry<String, Boolean>, Long> getTemporaryNodes();
/**
* Processes the nodes and returns the temporary ones.
* @return a set of temporary nodes
* @since 2.6
*/
Set<Node> getTemporaryPermissionNodes();
/**
* Processes the nodes and returns the non-temporary ones.
*
* @return a map of permanent nodes
* @deprecated in favour of {@link #getPermanentPermissionNodes()}
*/
@Deprecated
Map<String, Boolean> getPermanentNodes();
/**
* Processes the nodes and returns the non-temporary ones.
* @return a set of permanent nodes
* @since 2.6
*/
Set<Node> getPermanentPermissionNodes();
/**
* Removes temporary permissions that have expired
*/
void auditTemporaryPermissions();
}
@@ -23,7 +23,8 @@
package me.lucko.luckperms.api;
/**
* Represents the platform type that LuckPerms is running on
* The platforms which LuckPerms can run on
*
* @since 2.7
*/
public enum PlatformType {
@@ -0,0 +1,273 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* A means of loading and saving data to/from the Storage provider.
*
* <p>All methods return {@link CompletableFuture}s, which will be populated with the result once the data has been
* loaded asynchronously. Care should be taken when using the methods to ensure that the main server thread is not
* blocked.
*
* <p>Methods such as {@link CompletableFuture#get()} and equivalent should <strong>not</strong> be called on the main
* server thread. If you need to use the result of these operations on the main server thread, please register a
* callback using {@link CompletableFuture#thenAcceptAsync(Consumer, Executor)} and {@link #getSyncExecutor()}.
*
* @since 2.14
*/
public interface Storage {
/**
* Get the name of the storage implementation.
*
* @return the name of the implementation
*/
String getName();
/**
* Return whether the storage instance is allowing logins on the platform.
*
* @return true if logins are enabled
*/
boolean isAcceptingLogins();
/**
* Returns an executor which will run all passed runnables on the main server thread.
*
* @return an executor instance
*/
Executor getSyncExecutor();
/**
* Returns an executor which will run all passed runnables asynchronously using the platforms scheduler and thread
* pools.
*
* @return an executor instance
*/
Executor getAsyncExecutor();
/**
* Saves an action to storage
*
* @param entry the log entry to be saved
* @return true if the operation completed successfully.
* @throws NullPointerException if entry is null
*/
CompletableFuture<Boolean> logAction(LogEntry entry);
/**
* Loads and returns the entire log from storage
*
* @return a log instance, could be null if loading failed
*/
CompletableFuture<Log> getLog();
/**
* Loads a user's data from the main storage into the plugins local storage.
*
* @param uuid the uuid of the user to load
* @param username the users username. (if you want to specify <code>null</code> here, just input "null" as a
* string.)
* @return if the operation completed successfully
* @throws NullPointerException if uuid or username is null
*/
CompletableFuture<Boolean> loadUser(UUID uuid, String username);
/**
* Saves a user object back to storage. You should call this after you make any changes to a user.
*
* @param user the user to save
* @return true if the operation completed successfully.
* @throws NullPointerException if user is null
* @throws IllegalStateException if the user instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> saveUser(User user);
/**
* Removes users from the main storage who are "default". This is called every time the plugin loads.
*
* @return true if the operation completed successfully
*/
CompletableFuture<Boolean> cleanupUsers();
/**
* Gets a set all "unique" user UUIDs.
* "Unique" meaning the user isn't just a member of the "default" group.
*
* @return a set of uuids, or null if the operation failed.
*/
CompletableFuture<Set<UUID>> getUniqueUsers();
/**
* Searches for a list of users with a given permission.
*
* @param permission the permission to search for
* @return a list of held permissions, or null if the operation failed
* @throws NullPointerException if the permission is null
* @since 2.17
*/
CompletableFuture<List<HeldPermission<UUID>>> getUsersWithPermission(String permission);
/**
* Creates and loads a group into the plugins local storage
*
* @param name the name of the group
* @return true if the operation completed successfully
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
CompletableFuture<Boolean> createAndLoadGroup(String name);
/**
* Loads a group into the plugins local storage.
*
* @param name the name of the group
* @return true if the operation completed successfully
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
CompletableFuture<Boolean> loadGroup(String name);
/**
* Loads all groups from the storage into memory
*
* @return true if the operation completed successfully.
*/
CompletableFuture<Boolean> loadAllGroups();
/**
* Saves a group back to storage. You should call this after you make any changes to a group.
*
* @param group the group to save
* @return true if the operation completed successfully.
* @throws NullPointerException if group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> saveGroup(Group group);
/**
* Permanently deletes a group from storage.
*
* @param group the group to delete
* @return true if the operation completed successfully.
* @throws NullPointerException if group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> deleteGroup(Group group);
/**
* Searches for a list of groups with a given permission.
*
* @param permission the permission to search for
* @return a list of held permissions, or null if the operation failed
* @throws NullPointerException if the permission is null
* @since 2.17
*/
CompletableFuture<List<HeldPermission<String>>> getGroupsWithPermission(String permission);
/**
* Creates and loads a track into the plugins local storage
*
* @param name the name of the track
* @return true if the operation completed successfully
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
CompletableFuture<Boolean> createAndLoadTrack(String name);
/**
* Loads a track into the plugins local storage.
*
* @param name the name of the track
* @return true if the operation completed successfully
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
CompletableFuture<Boolean> loadTrack(String name);
/**
* Loads all tracks from the storage into memory
*
* @return true if the operation completed successfully.
*/
CompletableFuture<Boolean> loadAllTracks();
/**
* Saves a track back to storage. You should call this after you make any changes to a track.
*
* @param track the track to save
* @return true if the operation completed successfully.
* @throws NullPointerException if track is null
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> saveTrack(Track track);
/**
* Permanently deletes a track from storage
*
* @param track the track to delete
* @return true if the operation completed successfully.
* @throws NullPointerException if track is null
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> deleteTrack(Track track);
/**
* Saves UUID caching data to the global cache
*
* @param username the users username
* @param uuid the users mojang unique id
* @return true if the operation completed successfully.
* @throws NullPointerException if either parameters are null
* @throws IllegalArgumentException if the username is invalid
*/
CompletableFuture<Boolean> saveUUIDData(String username, UUID uuid);
/**
* Gets a UUID from a username
*
* @param username the corresponding username
* @return a uuid object, could be null
* @throws NullPointerException if either parameters are null
* @throws IllegalArgumentException if the username is invalid
*/
CompletableFuture<UUID> getUUID(String username);
/**
* Gets a username from a UUID
*
* @param uuid the corresponding uuid
* @return a name string, could be null
* @throws NullPointerException if either parameters are null
* @since 2.17
*/
CompletableFuture<String> getName(UUID uuid);
}
@@ -28,80 +28,89 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
/**
* Interface for internal Track instances
* An ordered collection of groups for easy promotions and demotions
*/
@SuppressWarnings("unused")
public interface Track {
/**
* Gets the name of this track
* @return the name of this track
*/
String getName();
/**
* Gets an ordered list of the groups on this track
* Index 0 is the first/lowest group in (or start of) the track
*
* <p> Index 0 is the first/lowest group in (or start of) the track
*
* @return an ordered {@link List} of the groups on this track
*/
List<String> getGroups();
/**
* Gets the number of groups on this track
*
* @return the number of groups on this track
*/
int getSize();
/**
* Gets the next group on the track, after the one provided
*
* @param current the group before the group being requested
* @return the group name, or null if the end of the track has been reached
* @throws ObjectLacksException if the track does not contain the group given
* @throws NullPointerException if the group is null
* @throws ObjectLacksException if the track does not contain the group given
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
String getNext(Group current) throws ObjectLacksException;
/**
* Gets the previous group on the track, before the one provided
*
* @param current the group after the group being requested
* @return the group name, or null if the start of the track has been reached
* @throws ObjectLacksException if the track does not contain the group given
* @throws NullPointerException if the group is null
* @throws ObjectLacksException if the track does not contain the group given
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
String getPrevious(Group current) throws ObjectLacksException;
/**
* Appends a group to the end of this track
*
* @param group the group to append
* @throws ObjectAlreadyHasException if the group is already on this track somewhere
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void appendGroup(Group group) throws ObjectAlreadyHasException;
/**
* Inserts a group at a certain position on this track
* @param group the group to be inserted
*
* @param group the group to be inserted
* @param position the index position (a value of 0 inserts at the start)
* @throws ObjectAlreadyHasException if the group is already on this track somewhere
* @throws IndexOutOfBoundsException if the position is less than 0 or greater than the size of the track
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void insertGroup(Group group, int position) throws ObjectAlreadyHasException, IndexOutOfBoundsException;
/**
* Removes a group from this track
*
* @param group the group to remove
* @throws ObjectLacksException if the group is not on this track
* @throws NullPointerException if the group is null
* @throws ObjectLacksException if the group is not on this track
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void removeGroup(Group group) throws ObjectLacksException;
/**
* Removes a group from this track
*
* @param group the group to remove
* @throws ObjectLacksException if the group is not on this track
* @throws NullPointerException if the group is null
@@ -110,15 +119,17 @@ public interface Track {
/**
* Checks if a group features on this track
*
* @param group the group to check
* @return true if the group is on this track
* @throws NullPointerException if the group is null
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
boolean containsGroup(Group group);
/**
* Checks if a group features on this track
*
* @param group the group to check
* @return true if the group is on this track
* @throws NullPointerException if the group is null
@@ -126,7 +137,7 @@ public interface Track {
boolean containsGroup(String group);
/**
* Clear all of the groups within this track
* Clear all of the groups from this track
*/
void clearGroups();
@@ -23,25 +23,51 @@
package me.lucko.luckperms.api;
/**
* Represents a permission value
* Represents a permission setting.
*
* <p> Consider a value of {@link #FALSE} to be a "negated" setting, and a value of {@link #UNDEFINED} to be a
* non-existent value.
*/
public enum Tristate {
/**
* A value indicating a holder has a permission set.
*/
TRUE(true),
/**
* A value indicating a holder has a negated value for a permission.
*/
FALSE(false),
/**
* A value indicating a holder doesn't have a value for a permission set.
*/
UNDEFINED(false);
/**
* Converts from {@link Boolean} a boolean
*
* @param b the boolean
* @return {@link #TRUE} or {@link #FALSE}, depending on the value of the boolean.
*/
public static Tristate fromBoolean(boolean b) {
return b ? TRUE : FALSE;
}
private final boolean booleanValue;
Tristate(boolean booleanValue) {
this.booleanValue = booleanValue;
}
/**
* Returns the value of the Tristate as a boolean.
* <p> A value of {@link #UNDEFINED} converts to false.
*
* @return a boolean representation of the Tristate.
*/
public boolean asBoolean() {
return booleanValue;
}
public static Tristate fromBoolean(boolean b) {
return b ? TRUE : FALSE;
}
}
@@ -22,50 +22,76 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* Interface for internal User instances
* A player holding permission data
*/
@SuppressWarnings("unused")
public interface User extends PermissionHolder {
/**
* Gets the users unique ID
*
* @return the users Mojang assigned unique id
*/
UUID getUuid();
/**
* Gets the users username
*
* @return the Users Username
*/
String getName();
/**
* Gets the users primary group
*
* @return the users primary group
*/
String getPrimaryGroup();
/**
* Sets a users primary group
*
* @param group the new primary group
* @throws ObjectAlreadyHasException if the user already has this set as their primary group
* @throws IllegalStateException if the user is not a member of that group
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the user is not a member of that group
* @throws NullPointerException if the group is null
*/
void setPrimaryGroup(String group) throws ObjectAlreadyHasException;
/**
* Refresh and re-assign the users permissions
* Refresh and re-assign the users permissions.
*
* <p> This request is not buffered, and the refresh call will be ran directly. This should ideally be called on
* an asynchronous thread.
*/
void refreshPermissions();
/**
* Gets the user's {@link UserData} cache, if they have one setup.
*
* @return an optional, possibly containing the user's cached lookup data.
* @since 2.13
*/
Optional<UserData> getUserDataCache();
/**
* Sets up the users data cache, if the don't have one setup already.
*
* @since 2.17
*/
void setupDataCache();
/**
* Check to see if the user is a member of a group
*
* @param group The group to check membership of
* @return true if the user is a member of the group
* @throws NullPointerException if the group is null
@@ -74,186 +100,198 @@ public interface User extends PermissionHolder {
/**
* Check to see if a user is a member of a group on a specific server
* @param group The group to check membership of
*
* @param group The group to check membership of
* @param server The server to check on
* @return true if the user is a member of the group
* @throws NullPointerException if the group or server is null
* @throws NullPointerException if the group or server is null
* @throws IllegalArgumentException if the server is invalid
*/
boolean isInGroup(Group group, String server);
/**
* Check to see if a user is a member of a group on a specific server and world
* @param group The group to check membership of
*
* @param group The group to check membership of
* @param server The server to check on
* @param world The world to check on
* @param world The world to check on
* @return true if the user is a member of the group
* @throws NullPointerException if the group, server or world is null
* @throws NullPointerException if the group, server or world is null
* @throws IllegalArgumentException if the server or world is invalid
*/
boolean isInGroup(Group group, String server, String world);
/**
* Add a user to a group
*
* @param group The group to add the user to
* @throws ObjectAlreadyHasException if the user is already a member of the group
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void addGroup(Group group) throws ObjectAlreadyHasException;
/**
* Add a user to a group on a specific server
* @param group The group to add the user to
*
* @param group The group to add the user to
* @param server The server to add the group on
* @throws ObjectAlreadyHasException if the user is already a member of the group on that server
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server is invalid
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server is invalid
*/
void addGroup(Group group, String server) throws ObjectAlreadyHasException;
/**
* Add a user to a group on a specific server and world
* @param group The group to add the user to
*
* @param group The group to add the user to
* @param server The server to add the group on
* @param world The world to add the group on
* @param world The world to add the group on
* @throws ObjectAlreadyHasException if the user is already a member of the group on that server
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server or world is invalid
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server or world is invalid
*/
void addGroup(Group group, String server, String world) throws ObjectAlreadyHasException;
/**
* Add a user to a group temporarily on a specific server
* @param group The group to add the user to
*
* @param group The group to add the user to
* @param expireAt when the group should expire
* @throws ObjectAlreadyHasException if the user is already a member of the group on that server
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past
*/
void addGroup(Group group, long expireAt) throws ObjectAlreadyHasException;
/**
* Add a user to a group temporarily on a specific server
* @param group The group to add the user to
* @param server The server to add the group on
*
* @param group The group to add the user to
* @param server The server to add the group on
* @param expireAt when the group should expire
* @throws ObjectAlreadyHasException if the user is already a member of the group on that server
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server is invalid
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server is invalid
*/
void addGroup(Group group, String server, long expireAt) throws ObjectAlreadyHasException;
/**
* Add a user to a group temporarily on a specific server and world
* @param group The group to add the user to
* @param server The server to add the group on
* @param world The world to add the group on
*
* @param group The group to add the user to
* @param server The server to add the group on
* @param world The world to add the group on
* @param expireAt when the group should expire
* @throws ObjectAlreadyHasException if the user is already a member of the group on that server
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server/world is invalid
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server/world is invalid
*/
void addGroup(Group group, String server, String world, long expireAt) throws ObjectAlreadyHasException;
/**
* Remove the user from a group
*
* @param group the group to remove the user from
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group is null
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void removeGroup(Group group) throws ObjectLacksException;
/**
* Remove the user from a group
* @param group the group to remove the user from
*
* @param group the group to remove the user from
* @param temporary if the group being removed is temporary
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group is null
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void removeGroup(Group group, boolean temporary) throws ObjectLacksException;
/**
* Remove the user from a group on a specific server
* @param group The group to remove the user from
*
* @param group The group to remove the user from
* @param server The server to remove the group on
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server is invalid
*/
void removeGroup(Group group, String server) throws ObjectLacksException;
/**
* Remove the user from a group on a specific server and world
* @param group The group to remove the user from
*
* @param group The group to remove the user from
* @param server The server to remove the group on
* @param world The world to remove the group on
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @param world The world to remove the group on
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the server or world is invalid
*/
void removeGroup(Group group, String server, String world) throws ObjectLacksException;
/**
* Remove the user from a group on a specific server
* @param group The group to remove the user from
* @param server The server to remove the group on
*
* @param group The group to remove the user from
* @param server The server to remove the group on
* @param temporary if the group being removed is temporary
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group or server is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server is invalid
*/
void removeGroup(Group group, String server, boolean temporary) throws ObjectLacksException;
/**
* Remove the user from a group on a specific server and world
* @param group The group to remove the user from
* @param server The server to remove the group on
* @param world The world to remove the group on
*
* @param group The group to remove the user from
* @param server The server to remove the group on
* @param world The world to remove the group on
* @param temporary if the group being removed is temporary
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws ObjectLacksException if the user isn't a member of the group
* @throws NullPointerException if the group, server or world is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @throws IllegalArgumentException if the expiry time is in the past or the server/world is invalid
*/
void removeGroup(Group group, String server, String world, boolean temporary) throws ObjectLacksException;
/**
* Clear all of the users permission nodes
*/
void clearNodes();
/**
* Get a {@link List} of all of the groups the user is a member of, on all servers
*
* @return a {@link List} of group names
*/
List<String> getGroupNames();
/**
* Get a {@link List} of the groups the user is a member of on a specific server
*
* @param server the server to check
* @param world the world to check
* @param world the world to check
* @return a {@link List} of group names
* @throws NullPointerException if the server or world is null
* @throws NullPointerException if the server or world is null
* @throws IllegalArgumentException if the server or world is invalid
*/
List<String> getLocalGroups(String server, String world);
/**
* Get a {@link List} of the groups the user is a member of on a specific server
*
* @param server the server to check
* @return a {@link List} of group names
* @throws NullPointerException if the server is null
* @throws NullPointerException if the server is null
* @throws IllegalArgumentException if the server is invalid
*/
List<String> getLocalGroups(String server);
@@ -27,21 +27,21 @@ import java.util.UUID;
/**
* A UUID cache for online users, between external Mojang UUIDs, and internal LuckPerms UUIDs.
*
* <p> This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode servers
* or mixed offline mode and online mode servers. Platforms running in offline mode generate a random UUID for a user when
* they first join the server, but this UUID will then not be consistent across the network. LuckPerms will instead check
* the datastore cache, to get a UUID for a user that is consistent across an entire network.
* <p> This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode
* servers or mixed offline mode and online mode servers. Platforms running in offline mode generate a UUID for a
* user when they first join the server, but this UUID will then not be consistent across the network. LuckPerms will
* instead check the datastore cache, to get a UUID for a user that is consistent across an entire network.
*
* <p> If you want to get a user object from the datastore using the api on a server in offline mode, you will need to use this cache,
* OR use Datastore#getUUID, for users that are not online.
* <p> If you want to get a user object from the Storage using the api on a server in offline mode, you will need to use
* this cache, OR use Storage#getUUID, for users that are not online.
*
* <p> WARNING: THIS IS ONLY EFFECTIVE FOR ONLINE PLAYERS. USE THE DATASTORE METHODS FOR OFFLINE PLAYERS.
* <p> THIS IS ONLY EFFECTIVE FOR ONLINE PLAYERS. USE THE DATASTORE METHODS FOR OFFLINE PLAYERS.
*/
@SuppressWarnings("unused")
public interface UuidCache {
/**
* Gets a users internal "LuckPerms" UUID, from the one given by the server.
*
* @param external the UUID assigned by the server, through Player#getUniqueId or ProxiedPlayer#getUniqueId
* @return the corresponding internal UUID
*/
@@ -49,6 +49,7 @@ public interface UuidCache {
/**
* Gets a users external, server assigned or Mojang assigned unique id, from the internal one used within LuckPerms.
*
* @param internal the UUID used within LuckPerms, through User#getUuid
* @return the corresponding external UUID
*/
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.caching;
import java.util.Map;
import java.util.SortedMap;
/**
* Holds cached Meta lookup data for a specific set of contexts
*
* @since 2.13
*/
public interface MetaData {
/**
* Gets an immutable copy of the meta this user has
*
* @return an immutable map of meta
*/
Map<String, String> getMeta();
/**
* Gets an immutable sorted map of all of the prefixes the user has, whereby the first value is the highest priority
* prefix.
*
* @return a sorted map of prefixes
*/
SortedMap<Integer, String> getPrefixes();
/**
* Gets an immutable sorted map of all of the suffixes the user has, whereby the first value is the highest priority
* suffix.
*
* @return a sorted map of suffixes
*/
SortedMap<Integer, String> getSuffixes();
/**
* Gets the user's highest priority prefix, or null if the user has no prefixes
*
* @return a prefix string, or null
*/
String getPrefix();
/**
* Gets the user's highest priority suffix, or null if the user has no suffixes
*
* @return a suffix string, or null
*/
String getSuffix();
}
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.caching;
import me.lucko.luckperms.api.Tristate;
import java.util.Map;
/**
* Holds cached Permission lookup data for a specific set of contexts
*
* @since 2.13
*/
public interface PermissionData {
/**
* Gets a permission value for the given permission node
*
* @param permission the permission node
* @return a tristate result
* @throws NullPointerException if permission is null
*/
Tristate getPermissionValue(String permission);
/**
* Invalidates the underlying permission calculator cache.
*
* <p> Can be called to allow for an update in defaults.
*/
void invalidateCache();
/**
* Gets an immutable copy of the permission map backing the permission calculator
*
* @return an immutable set of permissions
*/
Map<String, Boolean> getImmutableBacking();
}
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.caching;
import me.lucko.luckperms.api.Contexts;
import java.util.Set;
/**
* Holds cached permission and meta lookup data for a {@link me.lucko.luckperms.api.User}.
*
* <p>Data is only likely to be available for online users. All calls will account for inheritance, as well as any
* default data provided by the platform. This calls are heavily cached and are therefore fast.
*
* @since 2.13
*/
public interface UserData {
/**
* Gets PermissionData from the cache, given a specified context.
*
* <p> If the data is not cached, it is calculated. Therefore, this call could be costly.
*
* @param contexts the contexts to get the permission data in
* @return a permission data instance
* @throws NullPointerException if contexts is null
*/
PermissionData getPermissionData(Contexts contexts);
/**
* Gets MetaData from the cache, given a specified context.
*
* <p> If the data is not cached, it is calculated. Therefore, this call could be costly.
*
* @param contexts the contexts to get the permission data in
* @return a meta data instance
* @throws NullPointerException if contexts is null
*/
MetaData getMetaData(Contexts contexts);
/**
* Calculates permission data, bypassing the cache.
*
* @param contexts the contexts to get permission data in
* @return a permission data instance
* @throws NullPointerException if contexts is null
*/
PermissionData calculatePermissions(Contexts contexts);
/**
* Calculates meta data, bypassing the cache.
*
* @param contexts the contexts to get meta data in
* @return a meta data instance
* @throws NullPointerException if contexts is null
*/
MetaData calculateMeta(Contexts contexts);
/**
* Calculates permission data and stores it in the cache.
*
* <p> If there is already data cached for the given contexts, and if the resultant output is different,
* the cached value is updated.
*
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
*/
void recalculatePermissions(Contexts contexts);
/**
* Calculates meta data and stores it in the cache.
*
* <p> If there is already data cached for the given contexts, and if the resultant output is different,
* the cached value is updated.
*
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
*/
void recalculateMeta(Contexts contexts);
/**
* Calls {@link #recalculatePermissions(Contexts)} for all current loaded contexts
*/
void recalculatePermissions();
/**
* Calls {@link #recalculateMeta(Contexts)} for all current loaded contexts
*/
void recalculateMeta();
/**
* Calls {@link #preCalculate(Contexts)} for the given contexts
*
* @param contexts a set of contexts
* @throws NullPointerException if contexts is null
*/
void preCalculate(Set<Contexts> contexts);
/**
* Ensures that PermissionData and MetaData is cached for a context.
*
* <p> If the cache does not contain any data for the context, it will be calculated and saved.
*
* @param contexts the contexts to pre-calculate for
* @throws NullPointerException if contexts is null
*/
void preCalculate(Contexts contexts);
/**
* Invalidates all of the underlying Permission calculators.
* Can be called to allow for an update in defaults.
*/
void invalidatePermissionCalculators();
}
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A simple implementation of the listener aspects of {@link IContextCalculator}
*
* @param <T> the subject type
*/
public abstract class ContextCalculator<T> implements IContextCalculator<T> {
private final List<ContextListener<T>> listeners = new CopyOnWriteArrayList<>();
/**
* Pushes an update to all registered {@link ContextListener}s.
* Make sure any changes are applied internally before this method is called.
*
* @param subject the subject that changed
* @param before the context state before the change
* @param current the context state after the change (now)
* @throws NullPointerException if any parameters are null
*/
protected void pushUpdate(T subject, Map.Entry<String, String> before, Map.Entry<String, String> current) {
if (subject == null) {
throw new NullPointerException("subject");
}
if (before == null) {
throw new NullPointerException("before");
}
if (current == null) {
throw new NullPointerException("current");
}
for (ContextListener<T> listener : listeners) {
try {
listener.onContextChange(subject, before, current);
} catch (Exception e) {
System.out.println("Exception whilst passing context change to listener: " + listener);
e.printStackTrace();
}
}
}
@Override
public void addListener(ContextListener<T> listener) {
if (listener == null) {
throw new NullPointerException("listener");
}
listeners.add(listener);
}
}
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import java.util.Map;
/**
* Listens to context changes
*
* @param <T> the subject type, Is ALWAYS the player class of the platform.
*/
public interface ContextListener<T> {
/**
* Called whenever a context changes on the
*
* @param subject the subject that had context changed
* @param before the context state before the change
* @param current the context state after the change (now)
*/
void onContextChange(T subject, Map.Entry<String, String> before, Map.Entry<String, String> current) throws Exception;
}
@@ -0,0 +1,204 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import com.google.common.collect.Multimap;
import java.util.Map;
import java.util.Set;
/**
* Holds contexts.
* Implementations may be either mutable or immutable.
*
* @since 2.13
*/
public interface ContextSet {
/**
* Make a singleton ImmutableContextSet from a context pair
*
* @param key the key
* @param value the value
* @return a new ImmutableContextSet containing one KV pair
* @throws NullPointerException if key or value is null
*/
static ImmutableContextSet singleton(String key, String value) {
return ImmutableContextSet.singleton(key, value);
}
/**
* Creates a ImmutableContextSet from an existing map
*
* @param map the map to copy from
* @return a new ImmutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
static ImmutableContextSet fromMap(Map<String, String> map) {
return ImmutableContextSet.fromMap(map);
}
/**
* Creates a ImmutableContextSet from an existing iterable of Map Entries
*
* @param iterable the iterable to copy from
* @return a new ImmutableContextSet representing the pairs in the iterable
* @throws NullPointerException if the iterable is null
*/
static ImmutableContextSet fromEntries(Iterable<? extends Map.Entry<String, String>> iterable) {
return ImmutableContextSet.fromEntries(iterable);
}
/**
* Creates a ImmutableContextSet from an existing multimap
*
* @param multimap the multimap to copy from
* @return a new ImmutableContextSet representing the pairs in the multimap
* @throws NullPointerException if the multimap is null
* @since 2.16
*/
static ImmutableContextSet fromMultimap(Multimap<String, String> multimap) {
return ImmutableContextSet.fromMultimap(multimap);
}
/**
* Creates a new ImmutableContextSet from an existing set.
* Only really useful for converting between mutable and immutable types.
*
* @param contextSet the context set to copy from
* @return a new ImmutableContextSet with the same content and the one provided
* @throws NullPointerException if contextSet is null
*/
static ImmutableContextSet fromSet(ContextSet contextSet) {
return ImmutableContextSet.fromSet(contextSet);
}
/**
* Creates a new empty ImmutableContextSet.
*
* @return a new ImmutableContextSet
*/
static ImmutableContextSet empty() {
return ImmutableContextSet.empty();
}
/**
* Check to see if this set is in an immutable form
*
* @return true if the set is immutable
*/
boolean isImmutable();
/**
* If the set is mutable, this method will return an immutable copy. Otherwise just returns itself.
*
* @return an immutable ContextSet
*/
ImmutableContextSet makeImmutable();
/**
* Creates a mutable copy of this set.
*
* @return a mutable ContextSet
* @since 2.16
*/
MutableContextSet mutableCopy();
/**
* Converts this ContextSet to an immutable {@link Set} of {@link Map.Entry}s.
*
* @return an immutable set
*/
Set<Map.Entry<String, String>> toSet();
/**
* Converts this ContextSet to an immutable {@link Map}
*
* <b>NOTE: Use of this method may result in data being lost. ContextSets can contain lots of different values for
* one key.</b>
*
* @return an immutable map
*/
Map<String, String> toMap();
/**
* Converts this ContextSet to an immutable {@link Multimap}
*
* @return a multimap
* @since 2.16
*/
Multimap<String, String> toMultimap();
/**
* Check if the set contains at least one value for the given key.
*
* @param key the key to check for
* @return true if the set contains a value for the key
* @throws NullPointerException if the key is null
*/
boolean containsKey(String key);
/**
* Gets a set of all of the values mapped to the given key
*
* @param key the key to find values for
* @return a set of values
* @throws NullPointerException if the key is null
*/
Set<String> getValues(String key);
/**
* Check if thr set contains a given key mapped to a given value
*
* @param key the key to look for
* @param value the value to look for (case sensitive)
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
*/
boolean has(String key, String value);
/**
* Same as {@link #has(String, String)}, except ignores the case of the value.
*
* @param key the key to look for
* @param value the value to look for
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
*/
boolean hasIgnoreCase(String key, String value);
/**
* Check if the set is empty
*
* @return true if the set is empty
*/
boolean isEmpty();
/**
* Gets the number of key-value context pairs in the set
*
* @return the size of the set
*/
int size();
}
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import java.util.Map;
/**
* Calculates whether contexts are applicable to {@link T}
*
* <p>Somewhat inspired by the system used on Sponge.
*
* @param <T> the subject type. Is ALWAYS the player class of the platform.
*/
public interface IContextCalculator<T> {
/**
* Gives the subject all of the applicable contexts they meet
*
* @param subject the subject to add contexts to
* @param accumulator a map of contexts to add to
* @return the map
* @since 2.13
*/
MutableContextSet giveApplicableContext(T subject, MutableContextSet accumulator);
/**
* Checks to see if a context is applicable to a subject
*
* @param subject the subject to check against
* @param context the context to check for
* @return true if met, or false if not. If this calculator does not calculate the given context, return false.
*/
boolean isContextApplicable(T subject, Map.Entry<String, String> context);
/**
* Adds a listener to be called whenever a context handled by this calculator changes
*
* @param listener the listener instance
* @throws NullPointerException if listener is null
*/
void addListener(ContextListener<T> listener);
}
@@ -0,0 +1,257 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* An immutable implementation of {@link ContextSet}.
*
* @since 2.16
*/
public final class ImmutableContextSet implements ContextSet {
private static final ImmutableContextSet EMPTY = new ImmutableContextSet(ImmutableMultimap.of());
/**
* Make a singleton ImmutableContextSet from a context pair
*
* @param key the key
* @param value the value
* @return a new ImmutableContextSet containing one KV pair
* @throws NullPointerException if key or value is null
*/
public static ImmutableContextSet singleton(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
return new ImmutableContextSet(ImmutableMultimap.of(key.toLowerCase(), value));
}
/**
* Creates a ImmutableContextSet from an existing map
*
* @param map the map to copy from
* @return a new ImmutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
public static ImmutableContextSet fromMap(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("map");
}
ImmutableMultimap.Builder<String, String> b = ImmutableMultimap.builder();
for (Map.Entry<String, String> e : map.entrySet()) {
b.put(e.getKey(), e.getValue());
}
return new ImmutableContextSet(b.build());
}
/**
* Creates a ImmutableContextSet from an existing iterable of Map Entries
*
* @param iterable the iterable to copy from
* @return a new ImmutableContextSet representing the pairs in the iterable
* @throws NullPointerException if the iterable is null
*/
public static ImmutableContextSet fromEntries(Iterable<? extends Map.Entry<String, String>> iterable) {
if (iterable == null) {
throw new NullPointerException("iterable");
}
return MutableContextSet.fromEntries(iterable).makeImmutable();
}
/**
* Creates a ImmutableContextSet from an existing multimap
*
* @param multimap the multimap to copy from
* @return a new ImmutableContextSet representing the pairs in the multimap
* @throws NullPointerException if the multimap is null
*/
public static ImmutableContextSet fromMultimap(Multimap<String, String> multimap) {
if (multimap == null) {
throw new NullPointerException("multimap");
}
return MutableContextSet.fromMultimap(multimap).makeImmutable();
}
/**
* Creates a new ImmutableContextSet from an existing set.
* Only really useful for converting between mutable and immutable types.
*
* @param contextSet the context set to copy from
* @return a new ImmutableContextSet with the same content and the one provided
* @throws NullPointerException if contextSet is null
*/
public static ImmutableContextSet fromSet(ContextSet contextSet) {
return contextSet.makeImmutable();
}
/**
* Creates a new empty ContextSet.
*
* @return a new ContextSet
*/
public static ImmutableContextSet empty() {
return EMPTY;
}
private final Multimap<String, String> map;
ImmutableContextSet(Multimap<String, String> contexts) {
this.map = ImmutableMultimap.copyOf(contexts);
}
@Override
public boolean isImmutable() {
return true;
}
@Override
@Deprecated // This set is already immutable!
public ImmutableContextSet makeImmutable() {
return this;
}
@Override
public MutableContextSet mutableCopy() {
return MutableContextSet.fromSet(this);
}
@Override
public Set<Map.Entry<String, String>> toSet() {
return ImmutableSet.copyOf(map.entries());
}
@Override
public Map<String, String> toMap() {
ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
for (Map.Entry<String, String> e : map.entries()) {
m.put(e.getKey(), e.getValue());
}
return m.build();
}
@Override
public Multimap<String, String> toMultimap() {
return map;
}
@Override
public boolean containsKey(String key) {
if (key == null) {
throw new NullPointerException("key");
}
return map.containsKey(key);
}
@Override
public Set<String> getValues(String key) {
if (key == null) {
throw new NullPointerException("key");
}
Collection<String> c = map.get(key);
return c != null && !c.isEmpty() ? ImmutableSet.copyOf(c) : ImmutableSet.of();
}
@Override
public boolean has(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
return map.containsEntry(key, value);
}
@Override
public boolean hasIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
Collection<String> c = map.get(key);
if (c == null || c.isEmpty()) {
return false;
}
for (String val : c) {
if (val.equalsIgnoreCase(value)) {
return true;
}
}
return false;
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public int size() {
return map.size();
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
final Multimap<String, String> thisContexts = this.toMultimap();
final Multimap<String, String> otherContexts = other.toMultimap();
return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts);
}
@Override
public int hashCode() {
return 59 + (this.map == null ? 43 : this.map.hashCode());
}
@Override
public String toString() {
return "ImmutableContextSet(contexts=" + this.map + ")";
}
}
@@ -0,0 +1,399 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* A mutable implementation of {@link ContextSet}.
*
* @since 2.16
*/
public final class MutableContextSet implements ContextSet {
/**
* Make a singleton MutableContextSet from a context pair
*
* @param key the key
* @param value the value
* @return a new MutableContextSet containing one KV pair
* @throws NullPointerException if key or value is null
*/
public static MutableContextSet singleton(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
MutableContextSet set = new MutableContextSet();
set.add(key, value);
return set;
}
/**
* Creates a MutableContextSet from an existing map
*
* @param map the map to copy from
* @return a new MutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
public static MutableContextSet fromMap(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("map");
}
MutableContextSet set = new MutableContextSet();
set.addAll(map);
return set;
}
/**
* Creates a MutableContextSet from an existing iterable of Map Entries
*
* @param iterable the iterable to copy from
* @return a new MutableContextSet representing the pairs in the iterable
* @throws NullPointerException if the iterable is null
*/
public static MutableContextSet fromEntries(Iterable<? extends Map.Entry<String, String>> iterable) {
if (iterable == null) {
throw new NullPointerException("iterable");
}
MutableContextSet set = new MutableContextSet();
set.addAll(iterable);
return set;
}
/**
* Creates a MutableContextSet from an existing multimap
*
* @param multimap the multimap to copy from
* @return a new MutableContextSet representing the pairs in the multimap
* @throws NullPointerException if the multimap is null
*/
public static MutableContextSet fromMultimap(Multimap<String, String> multimap) {
if (multimap == null) {
throw new NullPointerException("multimap");
}
return fromEntries(multimap.entries());
}
/**
* Creates a new MutableContextSet from an existing set.
* Only really useful for converting between mutable and immutable types.
*
* @param contextSet the context set to copy from
* @return a new MutableContextSet with the same content and the one provided
* @throws NullPointerException if contextSet is null
*/
public static MutableContextSet fromSet(ContextSet contextSet) {
if (contextSet == null) {
throw new NullPointerException("contextSet");
}
MutableContextSet set = new MutableContextSet();
set.addAll(contextSet.toSet());
return set;
}
/**
* Creates a new empty MutableContextSet.
*
* @return a new MutableContextSet
*/
public static MutableContextSet create() {
return new MutableContextSet();
}
private final Multimap<String, String> map;
public MutableContextSet() {
this.map = Multimaps.synchronizedMultimap(HashMultimap.create());
}
private MutableContextSet(Multimap<String, String> contexts) {
this.map = Multimaps.synchronizedMultimap(HashMultimap.create(contexts));
}
@Override
public boolean isImmutable() {
return false;
}
@Override
public ImmutableContextSet makeImmutable() {
return new ImmutableContextSet(map);
}
@Override
public MutableContextSet mutableCopy() {
return new MutableContextSet(map);
}
@Override
public Set<Map.Entry<String, String>> toSet() {
return ImmutableSet.copyOf(map.entries());
}
@Override
public Map<String, String> toMap() {
ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
for (Map.Entry<String, String> e : map.entries()) {
m.put(e.getKey(), e.getValue());
}
return m.build();
}
@Override
public Multimap<String, String> toMultimap() {
return map;
}
@Override
public boolean containsKey(String key) {
if (key == null) {
throw new NullPointerException("key");
}
return map.containsKey(key);
}
@Override
public Set<String> getValues(String key) {
if (key == null) {
throw new NullPointerException("key");
}
Collection<String> c = map.get(key);
return c != null && !c.isEmpty() ? ImmutableSet.copyOf(c) : ImmutableSet.of();
}
@Override
public boolean has(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
return map.containsEntry(key, value);
}
@Override
public boolean hasIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
Collection<String> c = map.get(key);
if (c == null || c.isEmpty()) {
return false;
}
for (String val : c) {
if (val.equalsIgnoreCase(value)) {
return true;
}
}
return false;
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public int size() {
return map.size();
}
/**
* Adds a new key value pair to the set
*
* @param key the key to add
* @param value the value to add
* @throws NullPointerException if the key or value is null
*/
public void add(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
map.put(key.toLowerCase(), value);
}
/**
* Adds a new key value pair to the set
*
* @param entry the entry to add
* @throws NullPointerException if the entry is null
*/
public void add(Map.Entry<String, String> entry) {
if (entry == null) {
throw new NullPointerException("context");
}
map.put(entry.getKey().toLowerCase(), entry.getValue());
}
/**
* Adds an iterable containing contexts to the set
*
* @param iterable an iterable of key value context pairs
* @throws NullPointerException if iterable is null
*/
public void addAll(Iterable<? extends Map.Entry<String, String>> iterable) {
if (iterable == null) {
throw new NullPointerException("contexts");
}
for (Map.Entry<String, String> e : iterable) {
this.map.put(e.getKey().toLowerCase(), e.getValue());
}
}
/**
* Adds the entry set of a map to the set
*
* @param map the map to add from
* @throws NullPointerException if the map is null
*/
public void addAll(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("contexts");
}
addAll(map.entrySet());
}
/**
* Adds of of the values in another ContextSet to this set
*
* @param contextSet the set to add from
* @throws NullPointerException if the contextSet is null
*/
public void addAll(ContextSet contextSet) {
if (contextSet == null) {
throw new NullPointerException("contextSet");
}
this.map.putAll(contextSet.toMultimap());
}
/**
* Remove a key value pair from this set
*
* @param key the key to remove
* @param value the value to remove (case sensitive)
* @throws NullPointerException if the key or value is null
*/
public void remove(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
map.entries().removeIf(entry -> entry.getKey().equalsIgnoreCase(key) && entry.getValue().equals(value));
}
/**
* Same as {@link #remove(String, String)}, except ignores the case of the value
*
* @param key the key to remove
* @param value the value to remove
* @throws NullPointerException if the key or value is null
*/
public void removeIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
map.entries().removeIf(e -> e.getKey().equalsIgnoreCase(key) && e.getValue().equalsIgnoreCase(value));
}
/**
* Removes all pairs with the given key
*
* @param key the key to remove
* @throws NullPointerException if the key is null
*/
public void removeAll(String key) {
if (key == null) {
throw new NullPointerException("key");
}
map.removeAll(key.toLowerCase());
}
/**
* Clears the set
*/
public void clear() {
map.clear();
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
final Multimap<String, String> thisContexts = this.toMultimap();
final Multimap<String, String> otherContexts = other.toMultimap();
return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts);
}
@Override
public int hashCode() {
return 59 + (this.map == null ? 43 : this.map.hashCode());
}
@Override
public String toString() {
return "MutableContextSet(contexts=" + this.map + ")";
}
}
@@ -27,18 +27,16 @@ import java.util.function.Consumer;
/**
* A callback used to wait for the completion of asynchronous operations.
* All callbacks are ran on the main server thread.
*
* @param <T> the return type
* @deprecated in favour of {@link Consumer}
*/
@Deprecated
public interface Callback<T> {
/**
* Called when the operation completes.
* @param t the return value, may be null
*/
void onComplete(T t);
static <T> Callback<T> empty() {
return t -> {};
return t -> {
};
}
static <T> Callback<T> of(Runnable runnable) {
@@ -55,4 +53,26 @@ public interface Callback<T> {
return consumer::accept;
}
/**
* Helper method for converting old {@link Callback}s to use the new {@link me.lucko.luckperms.api.Storage}
* interface.
*
* @param callback the callback to convert
* @param <T> the return type
* @return a consumer instance
* @since 2.14
* @deprecated in favour of just using {@link Consumer}s.
*/
@Deprecated
static <T> Consumer<T> convertToConsumer(Callback<T> callback) {
return callback::onComplete;
}
/**
* Called when the operation completes.
*
* @param t the return value, may be null
*/
void onComplete(T t);
}
@@ -30,8 +30,11 @@ package me.lucko.luckperms.api.data;
public interface DatastoreConfiguration extends MySQLConfiguration {
String getAddress();
String getDatabase();
String getUsername();
String getPassword();
}
@@ -29,7 +29,10 @@ package me.lucko.luckperms.api.data;
public interface MySQLConfiguration {
String getAddress();
String getDatabase();
String getUsername();
String getPassword();
}
@@ -29,15 +29,14 @@ import me.lucko.luckperms.api.LuckPermsApi;
*/
public abstract class LPEvent {
/**
* A link to the API instance provided for convenience.
*/
private LuckPermsApi api = null;
/**
* A friendly name of the event
*/
private final String eventName;
/**
* A link to the API instance provided for convenience.
*/
private LuckPermsApi api = null;
protected LPEvent(String eventName) {
this.eventName = eventName;
@@ -24,6 +24,7 @@ package me.lucko.luckperms.api.event;
/**
* Represents an event acting upon a target
*
* @param <T> the target type
*/
public class TargetedEvent<T> extends LPEvent {
@@ -27,6 +27,7 @@ import me.lucko.luckperms.api.event.TargetedEvent;
/**
* Called when a permission expires for an object.
*
* @deprecated in favour of {@link PermissionNodeExpireEvent}
*/
@Deprecated
@@ -28,6 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a temporary permission node expires
*
* @since 2.6
*/
public class PermissionNodeExpireEvent extends AbstractPermissionEvent {
@@ -28,6 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a permission node is set on a holder
*
* @since 2.6
*/
public class PermissionNodeSetEvent extends AbstractPermissionEvent {
@@ -28,6 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a permission node is unset from a holder
*
* @since 2.6
*/
public class PermissionNodeUnsetEvent extends AbstractPermissionEvent {
@@ -30,6 +30,7 @@ import java.util.Map;
/**
* Called whenever a user or group has a permission set.
*
* @deprecated in favour of {@link PermissionNodeSetEvent}
*/
@Deprecated
@@ -27,6 +27,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionRemoveEvent;
/**
* Called whenever a user or group has a permission unset.
*
* @deprecated in favour of {@link PermissionNodeUnsetEvent}
*/
@Deprecated
@@ -27,11 +27,11 @@ import me.lucko.luckperms.api.event.LPEvent;
import java.util.UUID;
/**
* Called when the user logs into the network for the first time.
* Particularly useful for networks with multiple lobbies, who want to welcome a user when they join for the first time.
* Called when the user logs into the network for the first time. Particularly useful for networks with multiple
* lobbies, who want to welcome a user when they join for the first time.
*
* <p>This event is fired before the player has actually joined the game on the async login / auth event.
* If you want to do something with the user, store the UUID in a set, and then check the set in the PlayerJoinEvent o.e.
* <p>This event is fired before the player has actually joined the game on the async login / auth event. If you want to
* do something with the user, store the UUID in a set, and then check the set in the PlayerJoinEvent o.e.
*/
public class UserFirstLoginEvent extends LPEvent {
@@ -26,8 +26,8 @@ import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.event.UserEvent;
/**
* Called after a user has their permissions refreshed.
* If you cache user permissions within your own plugin, it's a good idea to update said cache whenever this event is called.
* Called after a user has their permissions refreshed. If you cache user permissions within your own plugin, it's a
* good idea to update said cache whenever this event is called.
*/
public class UserPermissionRefreshEvent extends UserEvent {
@@ -23,7 +23,9 @@
package me.lucko.luckperms.exceptions;
/**
* Thrown when a permission holding object doesn't / already has a permission or isn't / is already is a member of a group
* Thrown when a permission holding object doesn't / already has a permission or isn't / is already is a member of a
* group
*
* @since 2.7
*/
public abstract class MembershipException extends Exception {
+10 -18
View File
@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.9-SNAPSHOT</version>
<version>2.17-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms-bukkit-legacy</artifactId>
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPerms-bukkit-legacy-${release.version}.${git.closest.tag.commit.count}</finalName>
<finalName>LuckPerms-Bukkit-Legacy-${release.version}.${git.closest.tag.commit.count}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
@@ -48,24 +48,16 @@
</artifactSet>
<relocations>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>me.lucko.luckperms.lib.slf4j</shadedPattern>
<pattern>com.google.common</pattern>
<shadedPattern>me.lucko.luckperms.lib.guava</shadedPattern>
</relocation>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
<pattern>com.google.gson</pattern>
<shadedPattern>me.lucko.luckperms.lib.gson</shadedPattern>
</relocation>
<relocation>
<pattern>com.mongodb</pattern>
<shadedPattern>me.lucko.luckperms.lib.mongodb</shadedPattern>
</relocation>
<relocation>
<pattern>org.bson</pattern>
<shadedPattern>me.lucko.luckperms.lib.bson</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
<pattern>io.github.mkremins.fanciful</pattern>
<shadedPattern>me.lucko.luckperms.lib.fanciful</shadedPattern>
</relocation>
</relocations>
</configuration>
+4 -4
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.9-SNAPSHOT</version>
<version>2.17-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,7 +30,7 @@
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId>
<version>2.8</version>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<!-- BukkitAPI -->
@@ -25,17 +25,26 @@ package me.lucko.luckperms.api.placeholders;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.util.TimeUtil;
import me.lucko.luckperms.api.*;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Track;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.api.context.MutableContextSet;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Optional;
import java.util.stream.Collectors;
/*
* PlaceholderAPI Expansion for LuckPerms, implemented using the LuckPerms API.
*
* Placeholders:
* - group_name
* - groups
* - has_permission_<node>
* - inherits_permission_<node>
* - in_group_<name>
@@ -52,6 +61,10 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
private static final String PLUGIN_NAME = "LuckPerms";
private static final String AUTHOR = "Luck";
private static String formatBoolean(boolean b) {
return b ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse();
}
private LuckPermsApi api = null;
@Override
@@ -71,7 +84,14 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
@Override
public String getVersion() {
return "2.8";
return "2.13";
}
private Contexts makeContexts(Player player) {
MutableContextSet contextSet = new MutableContextSet();
contextSet.add("server", api.getConfiguration().getVaultServer());
contextSet.add("world", player.getWorld().getName());// ignore world?
return Contexts.of(contextSet.makeImmutable(), api.getConfiguration().getVaultIncludeGlobal(), true, true, true, true, player.isOp());
}
@Override
@@ -84,14 +104,24 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (!u.isPresent()) {
return "";
}
final User user = u.get();
Optional<UserData> d = user.getUserDataCache();
if (!d.isPresent()) {
return "";
}
final UserData data = d.get();
identifier = identifier.toLowerCase();
final User user = u.get();
if (identifier.equalsIgnoreCase("group_name")) {
return user.getPrimaryGroup();
}
if (identifier.equalsIgnoreCase("groups")) {
return user.getGroupNames().stream().collect(Collectors.joining(", "));
}
if (identifier.startsWith("has_permission_") && identifier.length() > "has_permission_".length()) {
String node = identifier.substring("has_permission_".length());
return formatBoolean(user.hasPermission(node, true));
@@ -99,7 +129,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (identifier.startsWith("inherits_permission_") && identifier.length() > "inherits_permission_".length()) {
String node = identifier.substring("inherits_permission_".length());
return formatBoolean(user.inheritsPermission(node, true));
return formatBoolean(data.getPermissionData(makeContexts(player)).getPermissionValue(node).asBoolean());
}
if (identifier.startsWith("in_group_") && identifier.length() > "in_group_".length()) {
@@ -109,7 +139,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (identifier.startsWith("inherits_group_") && identifier.length() > "inherits_group_".length()) {
String groupName = identifier.substring("inherits_group_".length());
return formatBoolean(user.getLocalGroups("global").contains(groupName));
return formatBoolean(data.getPermissionData(makeContexts(player)).getPermissionValue("group." + groupName).asBoolean());
}
if (identifier.startsWith("on_track_") && identifier.length() > "on_track_".length()) {
@@ -125,7 +155,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
String node = identifier.substring("expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.getPermission().equalsIgnoreCase(node)) {
if (n.toSerializedNode().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
}
@@ -146,16 +176,28 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
}
if (identifier.equalsIgnoreCase("prefix")) {
return MetaUtils.getPrefix(user, null, null, true);
String prefix = data.calculateMeta(makeContexts(player)).getPrefix();
if (prefix == null) {
prefix = "";
}
return prefix;
}
if (identifier.equalsIgnoreCase("suffix")) {
return MetaUtils.getSuffix(user, null, null, true);
String suffix = data.calculateMeta(makeContexts(player)).getSuffix();
if (suffix == null) {
suffix = "";
}
return suffix;
}
if (identifier.startsWith("meta_") && identifier.length() > "meta_".length()) {
String node = identifier.substring("meta_".length());
return MetaUtils.getMeta(user, null, null, node, "", true);
String meta = data.getMetaData(makeContexts(player)).getMeta().get(node);
if (meta == null) {
meta = "";
}
return meta;
}
return null;
@@ -175,8 +217,4 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
public String getAuthor() {
return AUTHOR;
}
private static String formatBoolean(boolean b) {
return b ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse();
}
}
+56 -29
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.9-SNAPSHOT</version>
<version>2.17-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -14,7 +14,7 @@
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPerms-${release.version}.${git.closest.tag.commit.count}</finalName>
<finalName>LuckPerms-Bukkit-${release.version}.${git.closest.tag.commit.count}</finalName>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<resources>
<resource>
@@ -44,31 +44,10 @@
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<artifactSet>
<excludes>
<exclude>org.xerial:*</exclude>
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>me.lucko.luckperms.lib.slf4j</shadedPattern>
</relocation>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>com.mongodb</pattern>
<shadedPattern>me.lucko.luckperms.lib.mongodb</shadedPattern>
</relocation>
<relocation>
<pattern>org.bson</pattern>
<shadedPattern>me.lucko.luckperms.lib.bson</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
<pattern>io.github.mkremins.fanciful</pattern>
<shadedPattern>me.lucko.luckperms.lib.fanciful</shadedPattern>
</relocation>
</relocations>
</configuration>
@@ -96,8 +75,8 @@
<dependencies>
<!-- BukkitAPI -->
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
@@ -122,6 +101,13 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- HikariCP - needed as source -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.5.1</version>
<scope>provided</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
@@ -129,5 +115,46 @@
<version>1.16.10</version>
<scope>provided</scope>
</dependency>
<!-- **************Migration Plugins************** -->
<dependency>
<groupId>org.tyrannyofheaven.bukkit</groupId>
<artifactId>zPermissions</artifactId>
<version>1.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ru.tehkode</groupId>
<artifactId>PermissionsEx</artifactId>
<version>1.23</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>net.gravitydevelopment.updater</groupId>
<artifactId>updater</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.cheesesoftware</groupId>
<artifactId>PowerfulPermsAPI</artifactId>
<version>1.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.anjocaido</groupId>
<artifactId>GroupManager</artifactId>
<version>1.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.bananaco</groupId>
<artifactId>bpermissions-api</artifactId>
<version>2.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -1,122 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.inject.Injector;
import me.lucko.luckperms.inject.LPPermissible;
import me.lucko.luckperms.users.BukkitUser;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.AbstractListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.*;
import java.util.UUID;
class BukkitListener extends AbstractListener implements Listener {
private final LPBukkitPlugin plugin;
BukkitListener(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) {
if (!plugin.getDatastore().isAcceptingLogins()) {
// The datastore is disabled, prevent players from joining the server
e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString());
return;
}
onAsyncLogin(e.getUniqueId(), e.getName());
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent e) {
final Player player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) {
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString());
return;
}
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
try {
LPPermissible lpPermissible = new LPPermissible(player, plugin);
Injector.inject(player, lpPermissible);
u.setLpPermissible(lpPermissible);
} catch (Throwable t) {
t.printStackTrace();
}
}
plugin.doAsync(user::refreshPermissions);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent e) {
// Refresh permissions again
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
plugin.getUserManager().getWorldCache().put(internal, e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(internal));
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerChangedWorld(PlayerChangedWorldEvent e) {
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
plugin.getUserManager().getWorldCache().put(internal, e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(internal));
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerQuit(PlayerQuitEvent e) {
plugin.getUserManager().getWorldCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
onLeave(e.getPlayer().getUniqueId());
}
@EventHandler
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
if (plugin.getConfiguration().getEnableOps()) {
return;
}
String s = e.getMessage()
.replace("/", "")
.replace("bukkit:", "")
.replace("spigot:", "")
.replace("minecraft:", "");
if (s.startsWith("op") || s.startsWith("deop")) {
e.setCancelled(true);
e.getPlayer().sendMessage(Message.OP_DISABLED.toString());
}
}
}
@@ -1,280 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms;
import lombok.Getter;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.api.vault.VaultHook;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.data.Importer;
import me.lucko.luckperms.groups.GroupManager;
import me.lucko.luckperms.runnables.ExpireTemporaryTask;
import me.lucko.luckperms.runnables.UpdateTask;
import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.storage.StorageFactory;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.BukkitUserManager;
import me.lucko.luckperms.utils.LogFactory;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Getter
public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private final Set<UUID> ignoringLogs = ConcurrentHashMap.newKeySet();
private LPConfiguration configuration;
private BukkitUserManager userManager;
private GroupManager groupManager;
private TrackManager trackManager;
private Datastore datastore;
private UuidCache uuidCache;
private ApiProvider apiProvider;
private Logger log;
private Importer importer;
private ConsecutiveExecutor consecutiveExecutor;
@Override
public void onEnable() {
log = LogFactory.wrap(getLogger());
getLog().info("Loading configuration...");
configuration = new BukkitConfig(this);
// register events
PluginManager pm = getServer().getPluginManager();
pm.registerEvents(new BukkitListener(this), this);
// register commands
getLog().info("Registering commands...");
BukkitCommand commandManager = new BukkitCommand(this);
PluginCommand main = getServer().getPluginCommand("luckperms");
main.setExecutor(commandManager);
main.setTabCompleter(commandManager);
main.setAliases(Arrays.asList("perms", "lp", "permissions", "p", "perm"));
datastore = StorageFactory.getDatastore(this, "h2");
getLog().info("Loading internal permission managers...");
uuidCache = new UuidCache(getConfiguration().getOnlineMode());
userManager = new BukkitUserManager(this);
groupManager = new GroupManager(this);
trackManager = new TrackManager();
importer = new Importer(commandManager);
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
int mins = getConfiguration().getSyncTime();
if (mins > 0) {
long ticks = mins * 60 * 20;
getServer().getScheduler().runTaskTimerAsynchronously(this, new UpdateTask(this), ticks, ticks);
}
getServer().getScheduler().runTaskTimer(this, BukkitSenderFactory.get(), 1L, 1L);
getServer().getScheduler().runTaskTimerAsynchronously(this, new ExpireTemporaryTask(this), 60L, 60L);
getServer().getScheduler().runTaskTimerAsynchronously(this, consecutiveExecutor, 20L, 20L);
// Provide vault support
getLog().info("Attempting to hook into Vault...");
try {
if (getServer().getPluginManager().isPluginEnabled("Vault")) {
VaultHook.hook(this);
getLog().info("Registered Vault permission & chat hook.");
} else {
getLog().info("Vault not found.");
}
} catch (Exception e) {
getLog().severe("Error occurred whilst hooking into Vault.");
e.printStackTrace();
}
getLog().info("Registering API...");
apiProvider = new ApiProvider(this);
LuckPerms.registerProvider(apiProvider);
getServer().getServicesManager().register(LuckPermsApi.class, apiProvider, this, ServicePriority.Normal);
// Run update task to refresh any online users
getLog().info("Scheduling Update Task to refresh any online users.");
try {
new UpdateTask(this).run();
} catch (Exception e) {
e.printStackTrace();
}
registerPermissions(getConfiguration().getCommandsAllowOp() ? PermissionDefault.OP : PermissionDefault.FALSE);
if (!getConfiguration().getEnableOps()) {
getServer().getOperators().forEach(o -> o.setOp(false));
}
getLog().info("Successfully loaded.");
}
@Override
public void onDisable() {
getLog().info("Closing datastore...");
datastore.shutdown();
getLog().info("Unregistering API...");
LuckPerms.unregisterProvider();
getServer().getServicesManager().unregisterAll(this);
}
@Override
public void doAsync(Runnable r) {
getServer().getScheduler().runTaskAsynchronously(this, r);
}
@Override
public void doSync(Runnable r) {
getServer().getScheduler().runTask(this, r);
}
@Override
public String getVersion() {
return getDescription().getVersion();
}
@Override
public PlatformType getType() {
return PlatformType.BUKKIT;
}
@Override
public File getMainDir() {
return getDataFolder();
}
@Override
public Message getPlayerStatus(UUID uuid) {
return getServer().getPlayer(getUuidCache().getExternalUUID(uuid)) != null ? Message.PLAYER_ONLINE : Message.PLAYER_OFFLINE;
}
@Override
public int getPlayerCount() {
return getServer().getOnlinePlayers().size();
}
@Override
public List<String> getPlayerList() {
return getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
}
@Override
public List<Sender> getNotifyListeners() {
return getServer().getOnlinePlayers().stream()
.map(p -> BukkitSenderFactory.get().wrap(p, Collections.singleton(me.lucko.luckperms.constants.Permission.LOG_NOTIFY)))
.filter(me.lucko.luckperms.constants.Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
public Sender getConsoleSender() {
return BukkitSenderFactory.get().wrap(getServer().getConsoleSender());
}
@Override
public List<String> getPossiblePermissions() {
final List<String> perms = new ArrayList<>();
getServer().getPluginManager().getPermissions().forEach(p -> {
perms.add(p.getName());
p.getChildren().keySet().forEach(perms::add);
});
return perms;
}
@Override
public Object getPlugin(String name) {
return getServer().getPluginManager().getPlugin(name);
}
@Override
public Object getService(Class clazz) {
return getServer().getServicesManager().load(clazz);
}
@SuppressWarnings("deprecation")
@Override
public UUID getUUID(String playerName) {
try {
return getServer().getOfflinePlayer(playerName).getUniqueId();
} catch (Exception e) {
return null;
}
}
@Override
public boolean isPluginLoaded(String name) {
return getServer().getPluginManager().isPluginEnabled(name);
}
@Override
public void runUpdateTask() {
getServer().getScheduler().runTaskAsynchronously(this, new UpdateTask(this));
}
private void registerPermissions(PermissionDefault def) {
PluginManager pm = getServer().getPluginManager();
Map<String, List<String>> wildcards = new HashMap<>();
List<String> all = new ArrayList<>();
for (me.lucko.luckperms.constants.Permission p : me.lucko.luckperms.constants.Permission.values()) {
pm.addPermission(new Permission(p.getNode(), def));
if (p.getGroup() != null) {
if (!wildcards.containsKey(p.getGroup())) {
wildcards.put(p.getGroup(), new ArrayList<>());
}
wildcards.get(p.getGroup()).add(p.getTag());
}
all.add(p.getNode());
}
for (Map.Entry<String, List<String>> e : wildcards.entrySet()) {
pm.addPermission(new Permission(
"luckperms." + e.getKey() + ".*", def,
e.getValue().stream()
.map(tag -> "luckperms." + e.getKey() + "." + tag)
.collect(Collectors.toMap(s -> s, s -> true))
)
);
}
pm.addPermission(new Permission("luckperms.*", def, all.stream().collect(Collectors.toMap(s -> s, s -> true))));
}
}
@@ -1,337 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.vault;
import lombok.NonNull;
import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.Contexts;
import net.milkbowl.vault.chat.Chat;
import java.util.Iterator;
import java.util.Map;
import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters;
import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters;
/**
* Provides the Vault Chat service through the use of normal permission nodes.
*
* Prefixes / Suffixes:
* Normal inheritance rules apply.
* Permission Nodes = prefix.priority.value OR suffix.priority.value
* If a user/group has / inherits multiple prefixes and suffixes, the one with the highest priority is the one that
* will apply.
*
* Meta:
* Normal inheritance rules DO NOT apply.
* Permission Nodes = meta.node.value
*
* Node that special characters used within LuckPerms are escaped:
* See {@link me.lucko.luckperms.utils.ArgumentChecker#escapeCharacters(String)}
*
* Registered on normal priority so other plugins can override.
*/
public class VaultChatHook extends Chat {
@Setter
private LPBukkitPlugin plugin;
private final VaultPermissionHook perms;
VaultChatHook(VaultPermissionHook perms) {
super(perms);
this.perms = perms;
}
public String getName() {
return perms.getName();
}
public boolean isEnabled() {
return perms.isEnabled();
}
private void saveMeta(PermissionHolder holder, String world, String node, String value) {
if (holder == null) return;
if (node.equals("")) return;
node = escapeCharacters(node);
value = escapeCharacters(value);
Iterator<Node> nodes = holder.getNodes().iterator();
while (nodes.hasNext()) {
Node n = nodes.next();
if (n.isMeta() && n.getMeta().getKey().equals(node)) {
nodes.remove();
}
}
Node.Builder metaNode = new me.lucko.luckperms.utils.Node.Builder("meta." + node + "." + value).setValue(true);
if (!perms.getServer().equalsIgnoreCase("global")) {
metaNode.setServer(perms.getServer());
}
if (world != null && !world.equals("")) {
metaNode.setServer(perms.getServer()).setWorld(world);
}
try {
holder.setPermission(metaNode.build());
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(holder);
}
private String getMeta(PermissionHolder holder, String world, String node, String defaultValue) {
if (holder == null) return defaultValue;
if (node.equals("")) return defaultValue;
node = escapeCharacters(node);
for (Node n : holder.getPermissions(true)) {
if (!n.getValue()) {
continue;
}
if (!n.isMeta()) {
continue;
}
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
return unescapeCharacters(meta.getValue());
}
}
return defaultValue;
}
private void setChatMeta(boolean prefix, PermissionHolder holder, String value, String world) {
if (holder == null) return;
if (value.equals("")) return;
Node.Builder node = new me.lucko.luckperms.utils.Node.Builder(prefix ? "prefix" : "suffix" + ".1000." + escapeCharacters(value));
node.setValue(true);
if (!perms.getServer().equalsIgnoreCase("global")) {
node.setServer(perms.getServer());
}
if (world != null && !world.equals("")) {
node.setServer(perms.getServer()).setWorld(world);
}
try {
holder.setPermission(node.build());
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(holder);
}
private String getChatMeta(boolean prefix, PermissionHolder holder, String world) {
if (holder == null) return "";
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes(null, Contexts.allowAll())) {
if (!n.getValue()) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue;
}
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
}
}
return meta == null ? "" : unescapeCharacters(meta);
}
public String getPlayerPrefix(String world, @NonNull String player) {
final User user = plugin.getUserManager().get(player);
return getChatMeta(true, user, world);
}
public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) {
final User user = plugin.getUserManager().get(player);
setChatMeta(true, user, prefix, world);
}
public String getPlayerSuffix(String world, @NonNull String player) {
final User user = plugin.getUserManager().get(player);
return getChatMeta(false, user, world);
}
public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) {
final User user = plugin.getUserManager().get(player);
setChatMeta(false, user, suffix, world);
}
public String getGroupPrefix(String world, @NonNull String group) {
final Group g = plugin.getGroupManager().get(group);
return getChatMeta(false, g, world);
}
public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) {
final Group g = plugin.getGroupManager().get(group);
setChatMeta(true, g, prefix, world);
}
public String getGroupSuffix(String world, @NonNull String group) {
final Group g = plugin.getGroupManager().get(group);
return getChatMeta(false, g, world);
}
public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) {
final Group g = plugin.getGroupManager().get(group);
setChatMeta(false, g, suffix, world);
}
public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) {
final User user = plugin.getUserManager().get(player);
try {
return Integer.parseInt(getMeta(user, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) {
final User user = plugin.getUserManager().get(player);
saveMeta(user, world, node, String.valueOf(value));
}
public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) {
final Group g = plugin.getGroupManager().get(group);
try {
return Integer.parseInt(getMeta(g, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) {
final Group g = plugin.getGroupManager().get(group);
saveMeta(g, world, node, String.valueOf(value));
}
public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) {
final User user = plugin.getUserManager().get(player);
try {
return Double.parseDouble(getMeta(user, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) {
final User user = plugin.getUserManager().get(player);
saveMeta(user, world, node, String.valueOf(value));
}
public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) {
final Group g = plugin.getGroupManager().get(group);
try {
return Double.parseDouble(getMeta(g, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) {
final Group g = plugin.getGroupManager().get(group);
saveMeta(g, world, node, String.valueOf(value));
}
public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) {
final User user = plugin.getUserManager().get(player);
String s = getMeta(user, world, node, String.valueOf(defaultValue));
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
return defaultValue;
}
return Boolean.parseBoolean(s);
}
public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) {
final User user = plugin.getUserManager().get(player);
saveMeta(user, world, node, String.valueOf(value));
}
public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) {
final Group g = plugin.getGroupManager().get(group);
String s = getMeta(g, world, node, String.valueOf(defaultValue));
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
return defaultValue;
}
return Boolean.parseBoolean(s);
}
public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) {
final Group g = plugin.getGroupManager().get(group);
saveMeta(g, world, node, String.valueOf(value));
}
public String getPlayerInfoString(String world, @NonNull String player, @NonNull String node, String defaultValue) {
final User user = plugin.getUserManager().get(player);
return getMeta(user, world, node, defaultValue);
}
public void setPlayerInfoString(String world, @NonNull String player, @NonNull String node, String value) {
final User user = plugin.getUserManager().get(player);
saveMeta(user, world, node, value);
}
public String getGroupInfoString(String world, @NonNull String group, @NonNull String node, String defaultValue) {
final Group g = plugin.getGroupManager().get(group);
return getMeta(g, world, node, defaultValue);
}
public void setGroupInfoString(String world, @NonNull String group, @NonNull String node, String value) {
final Group g = plugin.getGroupManager().get(group);
saveMeta(g, world, node, value);
}
}
@@ -1,226 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.vault;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
import net.milkbowl.vault.permission.Permission;
public class VaultPermissionHook extends Permission {
@Setter
private LPBukkitPlugin plugin;
@Getter
@Setter
private String server = "global";
@Getter
@Setter
private boolean includeGlobal = true;
@Override
public String getName() {
return "LuckPerms";
}
@Override
public boolean isEnabled() {
return plugin.getDatastore().isAcceptingLogins();
}
@Override
public boolean hasSuperPermsCompat() {
return true;
}
private boolean objectHas(String world, PermissionHolder object, String permission) {
if (object == null) return false;
if (world != null && !world.equals("")) {
return object.hasPermission(permission, true, server, world);
} else {
return object.hasPermission(permission, true, server);
}
}
private boolean objectAdd(String world, PermissionHolder object, String permission) {
if (object == null) return false;
try {
if (world != null && !world.equals("")) {
object.setPermission(permission, true, server, world);
} else {
object.setPermission(permission, true, server);
}
} catch (ObjectAlreadyHasException ignored) {}
objectSave(object);
return true;
}
private boolean objectRemove(String world, PermissionHolder object, String permission) {
if (object == null) return false;
try {
if (world != null && !world.equals("")) {
object.unsetPermission(permission, server, world);
} else {
object.unsetPermission(permission, server);
}
} catch (ObjectLacksException ignored) {}
objectSave(object);
return true;
}
void objectSave(PermissionHolder t) {
if (t instanceof User) {
((User) t).refreshPermissions();
plugin.getDatastore().saveUser(((User) t), Callback.empty());
}
if (t instanceof Group) {
plugin.getDatastore().saveGroup(((Group) t), c -> plugin.runUpdateTask());
}
}
@Override
public boolean playerHas(String world, @NonNull String player, @NonNull String permission) {
return objectHas(world, plugin.getUserManager().get(player), permission);
}
@Override
public boolean playerAdd(String world, @NonNull String player, @NonNull String permission) {
final User user = plugin.getUserManager().get(player);
return objectAdd(world, user, permission);
}
@Override
public boolean playerRemove(String world, @NonNull String player, @NonNull String permission) {
final User user = plugin.getUserManager().get(player);
return objectRemove(world, user, permission);
}
@Override
public boolean groupHas(String world, @NonNull String groupName, @NonNull String permission) {
final Group group = plugin.getGroupManager().get(groupName);
return objectHas(world, group, permission);
}
@Override
public boolean groupAdd(String world, @NonNull String groupName, @NonNull String permission) {
final Group group = plugin.getGroupManager().get(groupName);
return objectAdd(world, group, permission);
}
@Override
public boolean groupRemove(String world, @NonNull String groupName, @NonNull String permission) {
final Group group = plugin.getGroupManager().get(groupName);
return objectRemove(world, group, permission);
}
@Override
public boolean playerInGroup(String world, @NonNull String player, @NonNull String group) {
final User user = plugin.getUserManager().get(player);
if (user == null) return false;
final Group group1 = plugin.getGroupManager().get(group);
if (group1 == null) return false;
if (world != null && !world.equals("")) {
return user.isInGroup(group1, server, world);
} else {
return user.isInGroup(group1);
}
}
@Override
public boolean playerAddGroup(String world, @NonNull String player, @NonNull String groupName) {
final User user = plugin.getUserManager().get(player);
if (user == null) return false;
final Group group = plugin.getGroupManager().get(groupName);
if (group == null) return false;
try {
if (world != null && !world.equals("")) {
user.addGroup(group, server, world);
} else {
user.addGroup(group);
}
} catch (ObjectAlreadyHasException ignored) {}
objectSave(user);
return true;
}
@Override
public boolean playerRemoveGroup(String world, @NonNull String player, @NonNull String groupName) {
final User user = plugin.getUserManager().get(player);
if (user == null) return false;
final Group group = plugin.getGroupManager().get(groupName);
if (group == null) return false;
try {
if (world != null && !world.equals("")) {
user.removeGroup(group, server, world);
} else {
user.removeGroup(group);
}
} catch (ObjectLacksException ignored) {}
objectSave(user);
return true;
}
@Override
public String[] getPlayerGroups(String world, @NonNull String player) {
final User user = plugin.getUserManager().get(player);
return (user == null) ? new String[0] :
world != null && !world.equals("") ? user.getGroups(server, world, includeGlobal).toArray(new String[0]) :
user.getGroupNames().toArray(new String[0]);
}
@Override
public String getPrimaryGroup(String world, @NonNull String player) {
final User user = plugin.getUserManager().get(player);
return (user == null) ? null : user.getPrimaryGroup();
}
@Override
public String[] getGroups() {
return plugin.getGroupManager().getAll().keySet().toArray(new String[0]);
}
@Override
public boolean hasGroupSupport() {
return true;
}
}
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit;
import lombok.AllArgsConstructor;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.bukkit.calculators.AttachmentProcessor;
import me.lucko.luckperms.bukkit.calculators.ChildProcessor;
import me.lucko.luckperms.bukkit.calculators.DefaultsProcessor;
import me.lucko.luckperms.bukkit.inject.Injector;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionProcessor;
import me.lucko.luckperms.common.calculators.processors.MapProcessor;
import me.lucko.luckperms.common.calculators.processors.RegexProcessor;
import me.lucko.luckperms.common.calculators.processors.WildcardProcessor;
import me.lucko.luckperms.common.core.model.User;
import java.util.UUID;
@AllArgsConstructor
public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
private final LPBukkitPlugin plugin;
@Override
public PermissionCalculator build(Contexts contexts, User user) {
UUID uuid = plugin.getUuidCache().getExternalUUID(user.getUuid());
ImmutableList.Builder<PermissionProcessor> processors = ImmutableList.builder();
processors.add(new MapProcessor());
processors.add(new ChildProcessor(plugin.getChildPermissionProvider()));
processors.add(new AttachmentProcessor(() -> {
LPPermissible permissible = Injector.getPermissible(uuid);
return permissible == null ? null : permissible.getAttachmentPermissions();
}));
if (plugin.getConfiguration().isApplyingWildcards()) {
processors.add(new WildcardProcessor());
}
if (plugin.getConfiguration().isApplyingRegex()) {
processors.add(new RegexProcessor());
}
processors.add(new DefaultsProcessor(contexts.isOp(), plugin.getDefaultsProvider()));
return registerCalculator(new PermissionCalculator(plugin, user.getName(), processors.build()));
}
}
@@ -20,14 +20,15 @@
* SOFTWARE.
*/
package me.lucko.luckperms;
package me.lucko.luckperms.bukkit;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.common.commands.CommandManager;
import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Patterns;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -37,24 +38,25 @@ import java.util.Arrays;
import java.util.List;
class BukkitCommand extends CommandManager implements CommandExecutor, TabExecutor {
BukkitCommand(LuckPermsPlugin plugin) {
private final LPBukkitPlugin plugin;
BukkitCommand(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
onCommand(
BukkitSenderFactory.get().wrap(sender),
plugin.getSenderFactory().wrap(sender),
label,
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args))),
Callback.empty()
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args)))
);
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
return onTabComplete(BukkitSenderFactory.get().wrap(sender), Arrays.asList(args));
return onTabComplete(plugin.getSenderFactory().wrap(sender), Arrays.asList(args));
}
}
@@ -20,16 +20,21 @@
* SOFTWARE.
*/
package me.lucko.luckperms;
package me.lucko.luckperms.bukkit;
import me.lucko.luckperms.core.LPConfiguration;
import org.bukkit.configuration.InvalidConfigurationException;
import me.lucko.luckperms.common.config.AbstractConfiguration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
class BukkitConfig extends LPConfiguration<LPBukkitPlugin> {
class BukkitConfig extends AbstractConfiguration<LPBukkitPlugin> {
private YamlConfiguration configuration;
BukkitConfig(LPBukkitPlugin plugin) {
@@ -46,18 +51,7 @@ class BukkitConfig extends LPConfiguration<LPBukkitPlugin> {
getPlugin().saveResource("config.yml", false);
}
configuration = new YamlConfiguration();
try {
configuration.load(configFile);
} catch (InvalidConfigurationException | IOException e) {
e.printStackTrace();
}
}
@Override
protected void set(String path, Object value) {
configuration.set(path, value);
configuration = YamlConfiguration.loadConfiguration(configFile);
}
@Override
@@ -74,4 +68,35 @@ class BukkitConfig extends LPConfiguration<LPBukkitPlugin> {
protected boolean getBoolean(String path, boolean def) {
return configuration.getBoolean(path, def);
}
@Override
protected List<String> getList(String path, List<String> def) {
return Optional.ofNullable(configuration.getStringList(path)).orElse(def);
}
@Override
protected List<String> getObjectList(String path, List<String> def) {
ConfigurationSection section = configuration.getConfigurationSection(path);
if (section == null) {
return def;
}
return Optional.ofNullable(section.getKeys(false).stream().collect(Collectors.toList())).orElse(def);
}
@SuppressWarnings("unchecked")
@Override
protected Map<String, String> getMap(String path, Map<String, String> def) {
Map<String, String> map = new HashMap<>();
ConfigurationSection section = configuration.getConfigurationSection(path);
if (section == null) {
return def;
}
for (String key : section.getKeys(false)) {
map.put(key, section.getString(key));
}
return map;
}
}
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit;
import me.lucko.luckperms.bukkit.inject.Injector;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import me.lucko.luckperms.common.constants.Message;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.utils.AbstractListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginEnableEvent;
import java.util.UUID;
class BukkitListener extends AbstractListener implements Listener {
private final LPBukkitPlugin plugin;
BukkitListener(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) {
if (!plugin.isStarted() || !plugin.getStorage().isAcceptingLogins()) {
// The datastore is disabled, prevent players from joining the server
e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString());
return;
}
// Process login
onAsyncLogin(e.getUniqueId(), e.getName());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerPreLoginMonitor(AsyncPlayerPreLoginEvent e) {
if (plugin.isStarted() && plugin.getStorage().isAcceptingLogins() && e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
// Login event was cancelled by another plugin
onLeave(e.getUniqueId());
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent e) {
final Player player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) {
// User wasn't loaded for whatever reason.
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString());
return;
}
try {
// Make a new permissible for the user
LPPermissible lpPermissible = new LPPermissible(player, user, plugin);
// Inject into the player
Injector.inject(player, lpPermissible);
} catch (Throwable t) {
t.printStackTrace();
}
if (player.isOp()) {
// We assume all users are not op, but those who are need extra calculation.
plugin.doAsync(() -> user.getUserData().preCalculate(plugin.getPreProcessContexts(true)));
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLoginMonitor(PlayerLoginEvent e) {
if (e.getResult() != PlayerLoginEvent.Result.ALLOWED) {
// The player got denied on sync login.
onLeave(e.getPlayer().getUniqueId());
}
}
@EventHandler(priority = EventPriority.MONITOR) // Allow other plugins to see data when this event gets called.
public void onPlayerQuit(PlayerQuitEvent e) {
final Player player = e.getPlayer();
final UUID internal = plugin.getUuidCache().getUUID(player.getUniqueId());
// Remove from World cache
plugin.getWorldCalculator().getWorldCache().remove(internal);
// Remove the custom permissible
Injector.unInject(player, true);
// Handle auto op
if (plugin.getConfiguration().isAutoOp()) {
player.setOp(false);
}
// Call internal leave handling
onLeave(player.getUniqueId());
}
@EventHandler
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
if (plugin.getConfiguration().isOpsEnabled()) {
return;
}
String s = e.getMessage()
.replace("/", "")
.replace("bukkit:", "")
.replace("spigot:", "")
.replace("minecraft:", "");
if (s.equals("op") || s.startsWith("op ") || s.equals("deop") || s.startsWith("deop ")) {
e.setCancelled(true);
e.getPlayer().sendMessage(Message.OP_DISABLED.toString());
}
}
@EventHandler
public void onPluginEnable(PluginEnableEvent e) {
if (e.getPlugin().getName().equalsIgnoreCase("Vault")) {
plugin.tryVaultHook(true);
}
}
}
@@ -20,25 +20,26 @@
* SOFTWARE.
*/
package me.lucko.luckperms;
package me.lucko.luckperms.bukkit;
import me.lucko.luckperms.bukkit.compat.MessageHandler;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.commands.sender.SenderFactory;
import me.lucko.luckperms.common.constants.Constants;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import me.lucko.luckperms.commands.SenderFactory;
import me.lucko.luckperms.constants.Constants;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import io.github.mkremins.fanciful.FancyMessage;
import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BukkitSenderFactory extends SenderFactory<CommandSender> {
private static BukkitSenderFactory instance = null;
public static synchronized BukkitSenderFactory get() {
if (instance == null) {
instance = new BukkitSenderFactory();
}
return instance;
private final MessageHandler messageHandler;
public BukkitSenderFactory(LuckPermsPlugin plugin) {
super(plugin);
messageHandler = new MessageHandler();
}
@Override
@@ -46,7 +47,7 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
if (sender instanceof Player) {
return sender.getName();
}
return Constants.getConsoleName();
return Constants.CONSOLE_NAME;
}
@Override
@@ -54,7 +55,7 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
if (sender instanceof Player) {
return ((Player) sender).getUniqueId();
}
return Constants.getConsoleUUID();
return Constants.CONSOLE_UUID;
}
@Override
@@ -62,6 +63,11 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
sender.sendMessage(s);
}
@Override
protected void sendMessage(CommandSender sender, FancyMessage message) {
messageHandler.sendJsonMessage(sender, message);
}
@Override
protected boolean hasPermission(CommandSender sender, String node) {
return sender.hasPermission(node);
@@ -0,0 +1,618 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit;
import lombok.Getter;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bukkit.calculators.AutoOPListener;
import me.lucko.luckperms.bukkit.inject.Injector;
import me.lucko.luckperms.bukkit.model.ChildPermissionProvider;
import me.lucko.luckperms.bukkit.model.DefaultsProvider;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import me.lucko.luckperms.bukkit.vault.VaultHook;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.api.ApiHandler;
import me.lucko.luckperms.common.api.ApiProvider;
import me.lucko.luckperms.common.caching.handlers.CachedStateManager;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.config.LPConfiguration;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.contexts.ContextManager;
import me.lucko.luckperms.common.contexts.ServerCalculator;
import me.lucko.luckperms.common.core.UuidCache;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.data.Importer;
import me.lucko.luckperms.common.dependencies.DependencyManager;
import me.lucko.luckperms.common.managers.GroupManager;
import me.lucko.luckperms.common.managers.TrackManager;
import me.lucko.luckperms.common.managers.UserManager;
import me.lucko.luckperms.common.managers.impl.GenericGroupManager;
import me.lucko.luckperms.common.managers.impl.GenericTrackManager;
import me.lucko.luckperms.common.managers.impl.GenericUserManager;
import me.lucko.luckperms.common.messaging.RedisMessaging;
import me.lucko.luckperms.common.storage.Storage;
import me.lucko.luckperms.common.storage.StorageFactory;
import me.lucko.luckperms.common.storage.StorageType;
import me.lucko.luckperms.common.tasks.CacheHousekeepingTask;
import me.lucko.luckperms.common.tasks.ExpireTemporaryTask;
import me.lucko.luckperms.common.tasks.UpdateTask;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.common.utils.DebugHandler;
import me.lucko.luckperms.common.utils.LocaleManager;
import me.lucko.luckperms.common.utils.LogFactory;
import me.lucko.luckperms.common.utils.PermissionCache;
import org.bukkit.World;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@Getter
public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private Set<UUID> ignoringLogs;
private Executor syncExecutor;
private Executor asyncExecutor;
private VaultHook vaultHook = null;
private LPConfiguration configuration;
private UserManager userManager;
private GroupManager groupManager;
private TrackManager trackManager;
private Storage storage;
private RedisMessaging redisMessaging = null;
private UuidCache uuidCache;
private BukkitListener listener;
private ApiProvider apiProvider;
private Logger log;
private Importer importer;
private DefaultsProvider defaultsProvider;
private ChildPermissionProvider childPermissionProvider;
private LocaleManager localeManager;
private CachedStateManager cachedStateManager;
private ContextManager<Player> contextManager;
private WorldCalculator worldCalculator;
private CalculatorFactory calculatorFactory;
private BufferedRequest<Void> updateTaskBuffer;
private boolean started = false;
private DebugHandler debugHandler;
private BukkitSenderFactory senderFactory;
private PermissionCache permissionCache;
@Override
public void onEnable() {
// Used whilst the server is still starting
asyncExecutor = Executors.newCachedThreadPool();
syncExecutor = r -> getServer().getScheduler().runTask(this, r);
Executor bukkitAsyncExecutor = r -> getServer().getScheduler().runTaskAsynchronously(this, r);
log = LogFactory.wrap(getLogger());
ignoringLogs = ConcurrentHashMap.newKeySet();
debugHandler = new DebugHandler(bukkitAsyncExecutor, getVersion());
senderFactory = new BukkitSenderFactory(this);
permissionCache = new PermissionCache(bukkitAsyncExecutor);
getLog().info("Loading configuration...");
configuration = new BukkitConfig(this);
Set<StorageType> storageTypes = StorageFactory.getRequiredTypes(this, StorageType.H2);
DependencyManager.loadDependencies(this, storageTypes);
// setup the Bukkit defaults hook
defaultsProvider = new DefaultsProvider();
childPermissionProvider = new ChildPermissionProvider();
// give all plugins a chance to load their defaults, then refresh.
getServer().getScheduler().runTaskLater(this, () -> {
defaultsProvider.refresh();
childPermissionProvider.setup();
getServer().getScheduler().runTaskAsynchronously(this, () -> {
for (Map.Entry<String, Boolean> e : defaultsProvider.getOpDefaults().entrySet()) {
permissionCache.offer(e.getKey());
}
for (Map.Entry<String, Boolean> e : defaultsProvider.getNonOpDefaults().entrySet()) {
permissionCache.offer(e.getKey());
}
ImmutableMap<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> permissions = childPermissionProvider.getPermissions();
for (Map.Entry<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> e : permissions.entrySet()) {
permissionCache.offer(e.getKey().getKey());
for (Map.Entry<String, Boolean> e1 : e.getValue().entrySet()) {
permissionCache.offer(e1.getKey());
}
}
});
}, 1L);
// register events
PluginManager pm = getServer().getPluginManager();
listener = new BukkitListener(this);
pm.registerEvents(listener, this);
// initialise datastore
storage = StorageFactory.getInstance(this, StorageType.H2);
// initialise redis
if (getConfiguration().isRedisEnabled()) {
getLog().info("Loading redis...");
redisMessaging = new RedisMessaging(this);
try {
redisMessaging.init(getConfiguration().getRedisAddress(), getConfiguration().getRedisPassword());
getLog().info("Loaded redis successfully...");
} catch (Exception e) {
getLog().info("Couldn't load redis...");
e.printStackTrace();
}
}
// setup the update task buffer
updateTaskBuffer = new BufferedRequest<Void>(1000L, this::doAsync) {
@Override
protected Void perform() {
new UpdateTask(LPBukkitPlugin.this).run();
return null;
}
};
// load locale
localeManager = new LocaleManager();
File locale = new File(getDataFolder(), "lang.yml");
if (locale.exists()) {
getLog().info("Found locale file. Attempting to load from it.");
try {
localeManager.loadFromFile(locale);
} catch (Exception e) {
e.printStackTrace();
}
}
// register commands
getLog().info("Registering commands...");
BukkitCommand commandManager = new BukkitCommand(this);
PluginCommand main = getServer().getPluginCommand("luckperms");
main.setExecutor(commandManager);
main.setTabCompleter(commandManager);
main.setAliases(Arrays.asList("perms", "lp", "permissions", "perm"));
// load internal managers
getLog().info("Loading internal permission managers...");
uuidCache = new UuidCache(getConfiguration().isOnlineMode());
userManager = new GenericUserManager(this);
groupManager = new GenericGroupManager(this);
trackManager = new GenericTrackManager();
importer = new Importer(commandManager);
calculatorFactory = new BukkitCalculatorFactory(this);
cachedStateManager = new CachedStateManager(this);
contextManager = new ContextManager<>();
worldCalculator = new WorldCalculator(this);
pm.registerEvents(worldCalculator, this);
contextManager.registerCalculator(worldCalculator);
contextManager.registerCalculator(new ServerCalculator<>(getConfiguration().getServer()));
// handle server operators
if (getConfiguration().isAutoOp()) {
contextManager.registerListener(new AutoOPListener());
}
// Provide vault support
tryVaultHook(false);
// register with the LP API
getLog().info("Registering API...");
apiProvider = new ApiProvider(this);
ApiHandler.registerProvider(apiProvider);
getServer().getServicesManager().register(LuckPermsApi.class, apiProvider, this, ServicePriority.Normal);
// schedule update tasks
int mins = getConfiguration().getSyncTime();
if (mins > 0) {
long ticks = mins * 60 * 20;
getServer().getScheduler().runTaskTimerAsynchronously(this, () -> updateTaskBuffer.request(), 40L, ticks);
}
// run an update instantly.
updateTaskBuffer.requestDirectly();
// register tasks
getServer().getScheduler().runTaskTimerAsynchronously(this, new ExpireTemporaryTask(this), 60L, 60L);
getServer().getScheduler().runTaskTimerAsynchronously(this, new CacheHousekeepingTask(this), 2400L, 2400L);
// register permissions
registerPermissions(getConfiguration().isCommandsAllowOp() ? PermissionDefault.OP : PermissionDefault.FALSE);
if (!getConfiguration().isOpsEnabled()) {
getServer().getScheduler().runTask(this, () -> getServer().getOperators().forEach(o -> o.setOp(false)));
}
// replace the temporary executor when the Bukkit one starts
getServer().getScheduler().runTaskAsynchronously(this, () -> {
asyncExecutor = bukkitAsyncExecutor;
});
// Load any online users (in the case of a reload)
for (Player player : getServer().getOnlinePlayers()) {
doAsync(() -> {
listener.onAsyncLogin(player.getUniqueId(), player.getName());
User user = getUserManager().get(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) {
doSync(() -> {
try {
LPPermissible lpPermissible = new LPPermissible(player, user, this);
Injector.inject(player, lpPermissible);
} catch (Throwable t) {
t.printStackTrace();
}
});
}
});
}
started = true;
getLog().info("Successfully loaded.");
}
@Override
public void onDisable() {
started = false;
defaultsProvider.close();
permissionCache.setShutdown(true);
debugHandler.setShutdown(true);
for (Player player : getServer().getOnlinePlayers()) {
Injector.unInject(player, false);
if (getConfiguration().isAutoOp()) {
player.setOp(false);
}
final User user = getUserManager().get(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) {
user.unregisterData();
getUserManager().unload(user);
}
}
getLog().info("Closing datastore...");
storage.shutdown();
if (redisMessaging != null) {
getLog().info("Closing redis...");
redisMessaging.shutdown();
}
getLog().info("Unregistering API...");
ApiHandler.unregisterProvider();
getServer().getServicesManager().unregisterAll(this);
if (vaultHook != null) {
vaultHook.unhook(this);
}
// Bukkit will do this again when #onDisable completes, but we do it early to prevent NPEs elsewhere.
getServer().getScheduler().cancelTasks(this);
HandlerList.unregisterAll(this);
// Null everything
ignoringLogs = null;
syncExecutor = null;
asyncExecutor = null;
vaultHook = null;
configuration = null;
userManager = null;
groupManager = null;
trackManager = null;
storage = null;
redisMessaging = null;
uuidCache = null;
listener = null;
apiProvider = null;
log = null;
importer = null;
defaultsProvider = null;
childPermissionProvider = null;
localeManager = null;
cachedStateManager = null;
contextManager = null;
worldCalculator = null;
calculatorFactory = null;
updateTaskBuffer = null;
debugHandler = null;
senderFactory = null;
permissionCache = null;
}
public void tryVaultHook(boolean force) {
if (vaultHook != null) {
return;
}
getLog().info("Attempting to hook with Vault...");
try {
if (force || getServer().getPluginManager().isPluginEnabled("Vault")) {
vaultHook = new VaultHook();
vaultHook.hook(this);
getLog().info("Registered Vault permission & chat hook.");
} else {
getLog().info("Vault not found.");
}
} catch (Exception e) {
vaultHook = null;
getLog().severe("Error occurred whilst hooking into Vault.");
e.printStackTrace();
}
}
@Override
public void onUserRefresh(User user) {
LPPermissible lpp = Injector.getPermissible(uuidCache.getExternalUUID(user.getUuid()));
if (lpp != null) {
lpp.updateSubscriptions();
}
}
@Override
public void doAsync(Runnable r) {
asyncExecutor.execute(r);
}
@Override
public void doSync(Runnable r) {
syncExecutor.execute(r);
}
@Override
public void doAsyncRepeating(Runnable r, long interval) {
getServer().getScheduler().runTaskTimerAsynchronously(this, r, interval, interval);
}
@Override
public String getVersion() {
return getDescription().getVersion();
}
@Override
public PlatformType getType() {
return PlatformType.BUKKIT;
}
@Override
public String getServerName() {
return getServer().getName();
}
@Override
public String getServerVersion() {
return getServer().getVersion();
}
@Override
public File getMainDir() {
return getDataFolder();
}
@Override
public InputStream getResourceStream(String path) {
return getResource(path);
}
@Override
public Player getPlayer(User user) {
return getServer().getPlayer(uuidCache.getExternalUUID(user.getUuid()));
}
@Override
public Contexts getContextForUser(User user) {
Player player = getPlayer(user);
if (player == null) {
return null;
}
return new Contexts(
getContextManager().getApplicableContext(player),
getConfiguration().isIncludingGlobalPerms(),
getConfiguration().isIncludingGlobalWorldPerms(),
true,
getConfiguration().isApplyingGlobalGroups(),
getConfiguration().isApplyingGlobalWorldGroups(),
player.isOp()
);
}
@Override
public int getPlayerCount() {
return getServer().getOnlinePlayers().size();
}
@Override
public List<String> getPlayerList() {
return getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
}
@Override
public Set<UUID> getOnlinePlayers() {
return getServer().getOnlinePlayers().stream().map(Player::getUniqueId).collect(Collectors.toSet());
}
@Override
public boolean isOnline(UUID external) {
return getServer().getPlayer(external) != null;
}
@Override
public List<Sender> getSenders() {
return getServer().getOnlinePlayers().stream()
.map(p -> getSenderFactory().wrap(p))
.collect(Collectors.toList());
}
@Override
public Sender getConsoleSender() {
return getSenderFactory().wrap(getServer().getConsoleSender());
}
@Override
public Set<Contexts> getPreProcessContexts(boolean op) {
Set<ContextSet> c = new HashSet<>();
c.add(ContextSet.empty());
c.add(ContextSet.singleton("server", getConfiguration().getServer()));
// Pre process all worlds
c.addAll(getServer().getWorlds().stream()
.map(World::getName)
.map(s -> {
MutableContextSet set = MutableContextSet.create();
set.add("server", getConfiguration().getServer());
set.add("world", s);
return set.makeImmutable();
})
.collect(Collectors.toList())
);
// Pre process the separate Vault server, if any
if (!getConfiguration().getServer().equals(getConfiguration().getVaultServer())) {
c.add(ContextSet.singleton("server", getConfiguration().getVaultServer()));
c.addAll(getServer().getWorlds().stream()
.map(World::getName)
.map(s -> {
MutableContextSet set = MutableContextSet.create();
set.add("server", getConfiguration().getVaultServer());
set.add("world", s);
return set.makeImmutable();
})
.collect(Collectors.toList())
);
}
Set<Contexts> contexts = new HashSet<>();
// Convert to full Contexts
contexts.addAll(c.stream()
.map(set -> new Contexts(
set,
getConfiguration().isIncludingGlobalPerms(),
getConfiguration().isIncludingGlobalWorldPerms(),
true,
getConfiguration().isApplyingGlobalGroups(),
getConfiguration().isApplyingGlobalWorldGroups(),
op
))
.collect(Collectors.toSet())
);
// Check for and include varying Vault config options
boolean vaultDiff = getConfiguration().isVaultIncludingGlobal() != getConfiguration().isIncludingGlobalPerms() ||
!getConfiguration().isIncludingGlobalWorldPerms() ||
!getConfiguration().isApplyingGlobalGroups() ||
!getConfiguration().isApplyingGlobalWorldGroups();
if (vaultDiff) {
contexts.addAll(c.stream()
.map(map -> new Contexts(map, getConfiguration().isVaultIncludingGlobal(), true, true, true, true, op))
.collect(Collectors.toSet())
);
}
return contexts;
}
@Override
public LinkedHashMap<String, Object> getExtraInfo() {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("Vault Enabled", vaultHook != null);
map.put("Vault Server", configuration.getVaultServer());
map.put("Bukkit Defaults count", defaultsProvider.size());
map.put("Bukkit Child Permissions count", childPermissionProvider.getPermissions().size());
map.put("World Cache size", worldCalculator.getWorldCache().size());
map.put("Vault Including Global", configuration.isVaultIncludingGlobal());
map.put("Vault Ignoring World", configuration.isVaultIgnoreWorld());
map.put("Vault Primary Group Overrides", configuration.isVaultPrimaryGroupOverrides());
map.put("Vault Debug", configuration.isVaultDebug());
map.put("OPs Enabled", configuration.isOpsEnabled());
map.put("Auto OP", configuration.isAutoOp());
map.put("Commands Allow OPs", configuration.isCommandsAllowOp());
return map;
}
@Override
public Object getPlugin(String name) {
return getServer().getPluginManager().getPlugin(name);
}
@Override
public Object getService(Class clazz) {
return getServer().getServicesManager().load(clazz);
}
@SuppressWarnings("deprecation")
@Override
public UUID getUUID(String playerName) {
try {
return getServer().getOfflinePlayer(playerName).getUniqueId();
} catch (Exception e) {
return null;
}
}
@Override
public boolean isPluginLoaded(String name) {
return getServer().getPluginManager().isPluginEnabled(name);
}
private void registerPermissions(PermissionDefault def) {
PluginManager pm = getServer().getPluginManager();
for (Permission p : Permission.values()) {
for (String node : p.getNodes()) {
pm.addPermission(new org.bukkit.permissions.Permission(node, def));
}
}
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.common.LuckPermsPlugin;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor
public class WorldCalculator extends ContextCalculator<Player> implements Listener {
private static final String WORLD_KEY = "world";
private final LuckPermsPlugin plugin;
@Getter
private final Map<UUID, String> worldCache = new ConcurrentHashMap<>();
@Override
public MutableContextSet giveApplicableContext(Player subject, MutableContextSet accumulator) {
String world = getWorld(subject);
if (world != null) {
accumulator.add(Maps.immutableEntry(WORLD_KEY, world));
}
return accumulator;
}
@Override
public boolean isContextApplicable(Player subject, Map.Entry<String, String> context) {
if (!context.getKey().equals(WORLD_KEY)) {
return false;
}
String world = getWorld(subject);
return world != null && world.equals(context.getValue());
}
private String getWorld(Player player) {
UUID internal = plugin.getUuidCache().getUUID(player.getUniqueId());
String world = worldCache.get(internal);
if (world == null) {
return null;
}
return plugin.getConfiguration().getWorldRewrites().getOrDefault(world, world);
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerJoin(PlayerLoginEvent e) {
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
worldCache.put(internal, e.getPlayer().getWorld().getName());
pushUpdate(
e.getPlayer(),
Maps.immutableEntry(WORLD_KEY, null),
Maps.immutableEntry(WORLD_KEY, e.getPlayer().getWorld().getName())
);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerChangedWorld(PlayerChangedWorldEvent e) {
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
worldCache.put(internal, e.getPlayer().getWorld().getName());
pushUpdate(
e.getPlayer(),
Maps.immutableEntry(WORLD_KEY, e.getFrom().getName()),
Maps.immutableEntry(WORLD_KEY, e.getPlayer().getWorld().getName())
);
}
}
@@ -20,41 +20,39 @@
* SOFTWARE.
*/
package me.lucko.luckperms.users;
package me.lucko.luckperms.bukkit.calculators;
import me.lucko.luckperms.LPBungeePlugin;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.UUID;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.calculators.PermissionProcessor;
public class BungeeUserManager extends UserManager {
private final LPBungeePlugin plugin;
import org.bukkit.permissions.PermissionAttachmentInfo;
public BungeeUserManager(LPBungeePlugin plugin) {
super(plugin);
this.plugin = plugin;
}
import java.util.Map;
import java.util.function.Supplier;
@AllArgsConstructor
public class AttachmentProcessor implements PermissionProcessor {
@Getter
private final Supplier<Map<String, PermissionAttachmentInfo>> map;
@Override
public void cleanup(User user) {
if (plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())) == null) {
unload(user);
public Tristate hasPermission(String permission) {
Map<String, PermissionAttachmentInfo> m = map.get();
if (m == null) {
return Tristate.UNDEFINED;
}
PermissionAttachmentInfo pai = m.get(permission);
return pai == null ? Tristate.UNDEFINED : Tristate.fromBoolean(pai.getValue());
}
@Override
public User make(UUID uuid) {
return new BungeeUser(uuid, plugin);
public void updateBacking(Map<String, Boolean> map) {
// Do nothing, this doesn't use the backing
}
@Override
public User make(UUID uuid, String username) {
return new BungeeUser(uuid, username, plugin);
}
@Override
public void updateAllUsers() {
plugin.getProxy().getPlayers().stream()
.map(p -> plugin.getUuidCache().getUUID(p.getUniqueId()))
.forEach(u -> plugin.getDatastore().loadUser(u, "null"));
}
}
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.calculators;
import me.lucko.luckperms.api.context.ContextListener;
import me.lucko.luckperms.bukkit.inject.Injector;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.Optional;
public class AutoOPListener implements ContextListener<Player> {
@Override
public void onContextChange(Player subject, Map.Entry<String, String> before, Map.Entry<String, String> current) throws Exception {
try {
LPPermissible permissible = Injector.getPermissible(subject.getUniqueId());
if (permissible == null) {
return;
}
Map<String, Boolean> backing = permissible.getUser().getUserData().getPermissionData(permissible.calculateContexts()).getImmutableBacking();
boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false);
subject.setOp(op);
} catch (Exception ignored) {}
}
}
@@ -20,42 +20,36 @@
* SOFTWARE.
*/
package me.lucko.luckperms.users;
package me.lucko.luckperms.bukkit.calculators;
import me.lucko.luckperms.LPSpongePlugin;
import lombok.RequiredArgsConstructor;
import java.util.UUID;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.model.ChildPermissionProvider;
import me.lucko.luckperms.common.calculators.PermissionProcessor;
public class SpongeUserManager extends UserManager {
private final LPSpongePlugin plugin;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public SpongeUserManager(LPSpongePlugin plugin) {
super(plugin);
this.plugin = plugin;
@RequiredArgsConstructor
public class ChildProcessor implements PermissionProcessor {
private final ChildPermissionProvider provider;
private Map<String, Boolean> childPermissions = new ConcurrentHashMap<>();
@Override
public Tristate hasPermission(String permission) {
Boolean b = childPermissions.get(permission);
return b == null ? Tristate.UNDEFINED : Tristate.fromBoolean(b);
}
@Override
public void cleanup(User user) {
if (!plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())).isPresent()) {
unload(user);
plugin.getService().getUserSubjects().unload(user.getUuid());
public void updateBacking(Map<String, Boolean> map) {
childPermissions.clear();
for (Map.Entry<String, Boolean> e : map.entrySet()) {
Map<String, Boolean> children = provider.getPermissions().get(e);
if (children != null) {
childPermissions.putAll(children);
}
}
}
@Override
public User make(UUID uuid) {
return new SpongeUser(uuid, plugin);
}
@Override
public User make(UUID uuid, String username) {
return new SpongeUser(uuid, username, plugin);
}
@Override
public void updateAllUsers() {
plugin.getGame().getServer().getOnlinePlayers().stream()
.map(p -> plugin.getUuidCache().getUUID(p.getUniqueId()))
.forEach(u -> plugin.getDatastore().loadUser(u, "null"));
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.calculators;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.model.DefaultsProvider;
import me.lucko.luckperms.common.calculators.PermissionProcessor;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import java.util.Map;
@AllArgsConstructor
public class DefaultsProcessor implements PermissionProcessor {
private final boolean isOp;
private final DefaultsProvider defaultsProvider;
@Override
public Tristate hasPermission(String permission) {
Tristate t = defaultsProvider.hasDefault(permission, isOp);
if (t != Tristate.UNDEFINED) {
return t;
}
Permission defPerm = Bukkit.getServer().getPluginManager().getPermission(permission);
return defPerm == null ? Tristate.UNDEFINED : Tristate.fromBoolean(defPerm.getDefault().getValue(isOp));
}
@Override
public void updateBacking(Map<String, Boolean> map) {
// Do nothing, this doesn't use the backing
}
}
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.compat;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class BukkitJsonMessageHandler {
private static boolean setup = false;
private static boolean triedAndFailed = false;
private static Method GET_HANDLE_METHOD;
private static Field PLAYER_CONNECTION_FIELD;
private static Method SEND_PACKET_METHOD;
private static Constructor<?> PACKET_CHAT_CONSTRUCTOR;
private static Method SERIALIZE_METHOD;
private static void setup(Object player) throws Exception {
Class<?> craftPlayerClass = player.getClass();
GET_HANDLE_METHOD = craftPlayerClass.getDeclaredMethod("getHandle");
Object handleObject = GET_HANDLE_METHOD.invoke(player);
Class<?> handleClass = handleObject.getClass();
PLAYER_CONNECTION_FIELD = handleClass.getDeclaredField("playerConnection");
Object playerConnectionObject = PLAYER_CONNECTION_FIELD.get(handleObject);
Method[] playerConnectionMethods = playerConnectionObject.getClass().getDeclaredMethods();
for (Method m : playerConnectionMethods) {
if (m.getName().equals("sendPacket")) {
SEND_PACKET_METHOD = m;
break;
}
}
Class<?> packetChatClass = Class.forName(getVersionedClassName("PacketPlayOutChat"));
Constructor[] packetConstructors = packetChatClass.getDeclaredConstructors();
for (Constructor c : packetConstructors) {
Class<?>[] parameters = c.getParameterTypes();
if (parameters.length == 1 && parameters[0].getName().endsWith("IChatBaseComponent")) {
PACKET_CHAT_CONSTRUCTOR = c;
break;
}
}
Class<?> baseComponentClass = Class.forName(getVersionedClassName("IChatBaseComponent"));
Class<?> chatSerializerClass = baseComponentClass.getClasses()[0];
SERIALIZE_METHOD = chatSerializerClass.getDeclaredMethod("a", String.class);
}
private static String getVersionedClassName(String className) {
Class server = Bukkit.getServer().getClass();
if (!server.getSimpleName().equals("CraftServer")) {
throw new RuntimeException("Couldn't reflect into server " + server);
}
String version;
if (server.getName().equals("org.bukkit.craftbukkit.CraftServer")) {
// Non versioned class
version = ".";
} else {
version = server.getName().substring("org.bukkit.craftbukkit".length());
version = version.substring(0, version.length() - "CraftServer".length());
}
return "net.minecraft.server" + version + className;
}
private static synchronized boolean trySetup(Object player) {
if (setup) return true;
if (triedAndFailed) return false;
try {
setup(player);
setup = true;
return true;
} catch (Exception e) {
triedAndFailed = true;
return false;
}
}
public boolean sendJsonMessage(Player player, String json) {
if (!trySetup(player)) {
return false;
}
try {
Object connection = PLAYER_CONNECTION_FIELD.get(GET_HANDLE_METHOD.invoke(player));
SEND_PACKET_METHOD.invoke(connection, PACKET_CHAT_CONSTRUCTOR.newInstance(SERIALIZE_METHOD.invoke(null, json)));
return true;
} catch (Exception e) {
return false;
}
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.compat;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import io.github.mkremins.fanciful.FancyMessage;
public class MessageHandler {
private final BukkitJsonMessageHandler bukkitHandler;
private final SpigotJsonMessageHandler spigotHandler;
public MessageHandler() {
bukkitHandler = new BukkitJsonMessageHandler();
spigotHandler = isSpigot() ? new SpigotJsonMessageHandler() : null;
}
public void sendJsonMessage(CommandSender sender, FancyMessage message) {
if (sender instanceof Player) {
Player player = (Player) sender;
String json = message.toJSONString();
// Try Bukkit.
if (bukkitHandler.sendJsonMessage(player, json)) {
return;
}
// Try Spigot.
if (spigotHandler != null && spigotHandler.sendJsonMessage(player, json)) {
return;
}
}
// Fallback to Bukkit
sender.sendMessage(message.toOldMessageFormat());
}
private static boolean isSpigot() {
try {
Class.forName("net.md_5.bungee.chat.ComponentSerializer");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.compat;
import net.md_5.bungee.chat.ComponentSerializer;
import org.bukkit.entity.Player;
public class SpigotJsonMessageHandler {
public boolean sendJsonMessage(Player player, String json) {
try {
player.spigot().sendMessage(ComponentSerializer.parse(json));
return true;
} catch (Exception e) {
return false;
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.inject;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import java.util.Collections;
import java.util.Set;
public class DummyPermissibleBase extends PermissibleBase {
public DummyPermissibleBase() {
super(null);
}
@Override
public boolean isOp() {
return false;
}
@Override
public void setOp(boolean value) {
}
@Override
public boolean isPermissionSet(String name) {
return false;
}
@Override
public boolean isPermissionSet(Permission perm) {
return false;
}
@Override
public boolean hasPermission(String inName) {
return false;
}
@Override
public boolean hasPermission(Permission perm) {
return false;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin) {
return null;
}
@Override
public void removeAttachment(PermissionAttachment attachment) {
}
@Override
public void recalculatePermissions() {
}
@Override
public synchronized void clearPermissions() {
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
return null;
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return Collections.emptySet();
}
}
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.inject;
import lombok.experimental.UtilityClass;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.PermissionAttachment;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Injects a {@link LPPermissible} into a {@link Player}
*/
@UtilityClass
public class Injector {
private static final Map<UUID, LPPermissible> INJECTED_PERMISSIBLES = new ConcurrentHashMap<>();
private static Field HUMAN_ENTITY_FIELD;
private static Field PERMISSIBLEBASE_ATTACHMENTS;
static {
try {
HUMAN_ENTITY_FIELD = Class.forName(getVersionedClassName("entity.CraftHumanEntity")).getDeclaredField("perm");
HUMAN_ENTITY_FIELD.setAccessible(true);
PERMISSIBLEBASE_ATTACHMENTS = PermissibleBase.class.getDeclaredField("attachments");
PERMISSIBLEBASE_ATTACHMENTS.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean inject(Player player, LPPermissible lpPermissible) {
try {
PermissibleBase existing = (PermissibleBase) HUMAN_ENTITY_FIELD.get(player);
if (existing instanceof LPPermissible) {
// uh oh
throw new IllegalStateException();
}
// Move attachments over from the old permissible.
List<PermissionAttachment> attachments = (List<PermissionAttachment>) PERMISSIBLEBASE_ATTACHMENTS.get(existing);
lpPermissible.addAttachments(attachments);
attachments.clear();
existing.clearPermissions();
lpPermissible.recalculatePermissions();
lpPermissible.setOldPermissible(existing);
lpPermissible.updateSubscriptionsAsync();
HUMAN_ENTITY_FIELD.set(player, lpPermissible);
INJECTED_PERMISSIBLES.put(player.getUniqueId(), lpPermissible);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean unInject(Player player, boolean dummy) {
try {
PermissibleBase permissible = (PermissibleBase) HUMAN_ENTITY_FIELD.get(player);
if (permissible instanceof LPPermissible) {
permissible.clearPermissions();
((LPPermissible) permissible).unsubscribeFromAllAsync();
if (dummy) {
HUMAN_ENTITY_FIELD.set(player, new DummyPermissibleBase());
} else {
LPPermissible lpp = ((LPPermissible) permissible);
List<PermissionAttachment> attachments = lpp.getAttachments();
PermissibleBase newPb = lpp.getOldPermissible();
if (newPb == null) {
newPb = new PermissibleBase(player);
}
List<PermissionAttachment> newAttachments = (List<PermissionAttachment>) PERMISSIBLEBASE_ATTACHMENTS.get(newPb);
newAttachments.addAll(attachments);
attachments.clear();
HUMAN_ENTITY_FIELD.set(player, newPb);
}
}
INJECTED_PERMISSIBLES.remove(player.getUniqueId());
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static LPPermissible getPermissible(UUID uuid) {
return INJECTED_PERMISSIBLES.get(uuid);
}
private static String getVersionedClassName(String className) {
Class server = Bukkit.getServer().getClass();
if (!server.getSimpleName().equals("CraftServer")) {
throw new RuntimeException("Couldn't inject into server " + server);
}
String version;
if (server.getName().equals("org.bukkit.craftbukkit.CraftServer")) {
// Non versioned class
version = ".";
} else {
version = server.getName().substring("org.bukkit.craftbukkit".length());
version = version.substring(0, version.length() - "CraftServer".length());
}
return "org.bukkit.craftbukkit" + version + className;
}
}
@@ -20,26 +20,38 @@
* SOFTWARE.
*/
package me.lucko.luckperms.commands.migration.subcommands;
package me.lucko.luckperms.bukkit.migration;
import de.bananaco.bpermissions.api.Calculable;
import de.bananaco.bpermissions.api.CalculableType;
import de.bananaco.bpermissions.api.Group;
import de.bananaco.bpermissions.api.Permission;
import de.bananaco.bpermissions.api.World;
import de.bananaco.bpermissions.api.WorldManager;
import de.bananaco.bpermissions.api.*;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.api.MetaUtils;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
import me.lucko.luckperms.common.commands.SubCommand;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.constants.Constants;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.data.LogEntry;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import static me.lucko.luckperms.constants.Permission.MIGRATION;
import static me.lucko.luckperms.common.constants.Permission.MIGRATION;
public class MigrationBPermissions extends SubCommand<Object> {
private static Field uConfigField;
@@ -81,12 +93,90 @@ public class MigrationBPermissions extends SubCommand<Object> {
}
}
private static void migrateHolder(LuckPermsPlugin plugin, World world, Calculable c, PermissionHolder holder) {
// Migrate the groups permissions in this world
for (Permission p : c.getPermissions()) {
try {
holder.setPermission(p.name(), p.isTrue(), "global", world.getName());
LogEntry.build()
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("set " + p.name() + " " + p.isTrue() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
// Include any child permissions
for (Map.Entry<String, Boolean> child : p.getChildren().entrySet()) {
try {
holder.setPermission(child.getKey(), child.getValue(), "global", world.getName());
LogEntry.build()
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("set " + child.getKey() + " " + child.getValue() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
// Migrate any inherited groups
for (Group parent : c.getGroups()) {
try {
holder.setPermission("group." + parent.getName(), true, "global", world.getName());
LogEntry.build()
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("setinherit " + parent.getName() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
// Migrate existing meta
for (Map.Entry<String, String> meta : c.getMeta().entrySet()) {
if (meta.getKey().equalsIgnoreCase("prefix") || meta.getKey().equalsIgnoreCase("suffix")) {
String chatMeta = MetaUtils.escapeCharacters(meta.getValue());
try {
holder.setPermission(meta.getKey().toLowerCase() + "." + c.getPriority() + "." + chatMeta, true);
LogEntry.build()
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("set " + meta.getKey().toLowerCase() + "." + c.getPriority() + "." + chatMeta + " true")
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
continue;
}
try {
holder.setPermission("meta." + meta.getKey() + "." + meta.getValue(), true, "global", world.getName());
LogEntry.build()
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("set meta." + meta.getKey() + "." + meta.getValue() + " true global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
public MigrationBPermissions() {
super("bpermissions", "Migration from bPermissions", "/%s migration bpermissions", MIGRATION, Predicate.alwaysFalse());
super("bpermissions", "Migration from bPermissions", MIGRATION, Predicates.alwaysFalse(), null);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
final Logger log = plugin.getLog();
WorldManager worldManager = WorldManager.getInstance();
@@ -109,7 +199,7 @@ public class MigrationBPermissions extends SubCommand<Object> {
log.info("bPermissions Migration: Starting world migration.");
for (World world : worldManager.getAllWorlds()) {
log.info("bPermissions Migration: Migrating world: " + world.getName());
// Migrate all groups
log.info("bPermissions Migration: Starting group migration in world " + world.getName() + ".");
int groupCount = 0;
@@ -121,19 +211,19 @@ public class MigrationBPermissions extends SubCommand<Object> {
}
// Make a LuckPerms group for the one being migrated.
plugin.getDatastore().createAndLoadGroup(groupName);
me.lucko.luckperms.groups.Group lpGroup = plugin.getGroupManager().get(groupName);
plugin.getStorage().createAndLoadGroup(groupName).join();
me.lucko.luckperms.common.core.model.Group lpGroup = plugin.getGroupManager().getIfLoaded(groupName);
try {
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpGroup).action("create")
.build().submit(plugin);
} catch (Exception ex) {
ex.printStackTrace();
}
migrateHolder(plugin, world, group, lpGroup);
plugin.getDatastore().saveGroup(lpGroup);
plugin.getStorage().saveGroup(lpGroup);
}
log.info("bPermissions Migration: Migrated " + groupCount + " groups in world " + world.getName() + ".");
@@ -157,12 +247,12 @@ public class MigrationBPermissions extends SubCommand<Object> {
}
// Make a LuckPerms user for the one being migrated.
plugin.getDatastore().loadUser(uuid, "null");
me.lucko.luckperms.users.User lpUser = plugin.getUserManager().get(uuid);
plugin.getStorage().loadUser(uuid, "null").join();
User lpUser = plugin.getUserManager().get(uuid);
migrateHolder(plugin, world, user, lpUser);
plugin.getDatastore().saveUser(lpUser);
plugin.getStorage().saveUser(lpUser);
plugin.getUserManager().cleanup(lpUser);
}
@@ -172,82 +262,4 @@ public class MigrationBPermissions extends SubCommand<Object> {
log.info("bPermissions Migration: Success! Completed without any errors.");
return CommandResult.SUCCESS;
}
private static void migrateHolder(LuckPermsPlugin plugin, World world, Calculable c, PermissionHolder holder) {
// Migrate the groups permissions in this world
for (Permission p : c.getPermissions()) {
try {
holder.setPermission(p.name(), p.isTrue(), "global", world.getName());
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("set " + p.name() + " " + p.isTrue() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
// Include any child permissions
for (Map.Entry<String, Boolean> child : p.getChildren().entrySet()) {
try {
holder.setPermission(child.getKey(), child.getValue(), "global", world.getName());
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("set " + child.getKey() + " " + child.getValue() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
// Migrate any inherited groups
for (Group parent : c.getGroups()) {
try {
holder.setPermission("group." + parent.getName(), true, "global", world.getName());
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("setinherit " + parent.getName() + " global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
// Migrate existing meta
for (Map.Entry<String, String> meta : c.getMeta().entrySet()) {
if (meta.getKey().equalsIgnoreCase("prefix") || meta.getKey().equalsIgnoreCase("suffix")) {
String chatMeta = ArgumentChecker.escapeCharacters(meta.getValue());
try {
holder.setPermission(meta.getKey().toLowerCase() + "." + c.getPriority() + "." + chatMeta, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("set " + meta.getKey().toLowerCase() + "." + c.getPriority() + "." + chatMeta + " true")
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
continue;
}
try {
holder.setPermission("meta." + meta.getKey() + "." + meta.getValue(), true, "global", world.getName());
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.acted(holder).action("set meta." + meta.getKey() + "." + meta.getValue() + " true global " + world.getName())
.build().submit(plugin);
} catch (Exception ex) {
if (!(ex instanceof ObjectAlreadyHasException)) {
ex.printStackTrace();
}
}
}
}
}
@@ -20,18 +20,23 @@
* SOFTWARE.
*/
package me.lucko.luckperms.commands.migration.subcommands;
package me.lucko.luckperms.bukkit.migration;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
import me.lucko.luckperms.common.commands.SubCommand;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.constants.Constants;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.data.LogEntry;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import org.anjocaido.groupmanager.GlobalGroups;
import org.anjocaido.groupmanager.GroupManager;
import org.anjocaido.groupmanager.data.Group;
@@ -39,18 +44,22 @@ import org.anjocaido.groupmanager.data.User;
import org.anjocaido.groupmanager.dataholder.WorldDataHolder;
import org.anjocaido.groupmanager.dataholder.worlds.WorldsHolder;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
public class MigrationGroupManager extends SubCommand<Object> {
public MigrationGroupManager() {
super("groupmanager", "Migration from GroupManager", "/%s migration groupmanager [world names]",
Permission.MIGRATION, Predicate.is(0));
super("groupmanager", "Migration from GroupManager", Permission.MIGRATION, Predicates.is(0),
Arg.list(Arg.create("world names...", false, "a list of worlds to migrate permissions from"))
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
final Logger log = plugin.getLog();
if (!plugin.isPluginLoaded("GroupManager")) {
log.severe("GroupManager Migration: Error -> GroupManager is not loaded.");
@@ -65,21 +74,14 @@ public class MigrationGroupManager extends SubCommand<Object> {
// Migrate Global Groups
log.info("GroupManager Migration: Starting Global Group migration.");
GlobalGroups gg;
try {
gg = (GlobalGroups) GroupManager.class.getMethod("getGlobalGroups").invoke(gm);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
return CommandResult.FAILURE;
}
GlobalGroups gg = GroupManager.getGlobalGroups();
for (Group g : gg.getGroupList()) {
plugin.getDatastore().createAndLoadGroup(g.getName().toLowerCase());
me.lucko.luckperms.groups.Group group = plugin.getGroupManager().get(g.getName().toLowerCase());
plugin.getStorage().createAndLoadGroup(g.getName().toLowerCase()).join();
me.lucko.luckperms.common.core.model.Group group = plugin.getGroupManager().getIfLoaded(g.getName().toLowerCase());
try {
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(group).action("create")
.build().submit(plugin);
} catch (Exception ex) {
@@ -99,7 +101,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
try {
group.setPermission(node, value);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(group).action("set " + node + " " + value)
.build().submit(plugin);
} catch (Exception ex) {
@@ -113,7 +115,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
try {
group.setPermission("group." + s.toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(group).action("setinherit " + s.toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
@@ -123,33 +125,21 @@ public class MigrationGroupManager extends SubCommand<Object> {
}
}
plugin.getDatastore().saveGroup(group);
plugin.getStorage().saveGroup(group);
}
Map<UUID, Map<Map.Entry<String, String>, Boolean>> users = new HashMap<>();
Map<UUID, String> primaryGroups = new HashMap<>();
Map<String, Map<Map.Entry<String, String>, Boolean>> groups = new HashMap<>();
WorldsHolder wh;
try {
wh = (WorldsHolder) GroupManager.class.getMethod("getWorldsHolder").invoke(gm);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
return CommandResult.FAILURE;
}
WorldsHolder wh = gm.getWorldsHolder();
// Collect data for all users and groups.
log.info("GroupManager Migration: Starting user and group migration.");
for (String world : worlds) {
world = world.toLowerCase();
WorldDataHolder wdh;
try {
wdh = (WorldDataHolder) WorldsHolder.class.getMethod("getWorldData", String.class).invoke(wh, world);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
return CommandResult.FAILURE;
}
WorldDataHolder wdh = wh.getWorldData(world);
for (Group g : wdh.getGroupList()) {
groups.putIfAbsent(g.getName().toLowerCase(), new HashMap<>());
@@ -176,7 +166,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
UUID uuid;
try {
uuid = UUID.fromString(user.getUUID());
} catch (IllegalArgumentException e){
} catch (IllegalArgumentException e) {
continue;
}
@@ -195,7 +185,13 @@ public class MigrationGroupManager extends SubCommand<Object> {
users.get(uuid).put(new AbstractMap.SimpleEntry<>(world, node), value);
}
users.get(uuid).put(new AbstractMap.SimpleEntry<>(world, "group." + user.getGroupName().toLowerCase()), true);
String finalWorld = world;
users.get(uuid).putAll(user.subGroupListStringCopy().stream()
.map(n -> "group." + n)
.map(n -> new AbstractMap.SimpleEntry<>(finalWorld, n))
.collect(Collectors.toMap(n -> n, n -> true))
);
primaryGroups.put(uuid, user.getGroupName());
}
}
@@ -204,11 +200,11 @@ public class MigrationGroupManager extends SubCommand<Object> {
log.info("GroupManager Migration: Found a total of " + users.size() + " users and " + groups.size() + " groups.");
for (Map.Entry<String, Map<Map.Entry<String, String>, Boolean>> e : groups.entrySet()) {
plugin.getDatastore().createAndLoadGroup(e.getKey());
me.lucko.luckperms.groups.Group group = plugin.getGroupManager().get(e.getKey());
plugin.getStorage().createAndLoadGroup(e.getKey()).join();
me.lucko.luckperms.common.core.model.Group group = plugin.getGroupManager().getIfLoaded(e.getKey());
try {
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(group).action("create")
.build().submit(plugin);
} catch (Exception ex) {
@@ -221,17 +217,17 @@ public class MigrationGroupManager extends SubCommand<Object> {
// n.key.value = node
// n.value = true/false
try {
group.setPermission("global-" + n.getKey().getKey() + "/" + n.getKey().getValue(), n.getValue());
group.setPermission(NodeFactory.fromSerialisedNode("global-" + n.getKey().getKey() + "/" + n.getKey().getValue(), n.getValue()));
if (n.getKey().getValue().startsWith("group.")) {
String groupName = n.getKey().getValue().substring(6);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(group).action("setinherit " + groupName + " global " + n.getKey().getKey())
.build().submit(plugin);
} else {
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(group).action("set " + n.getKey().getValue() + " " + n.getValue() + " global " + n.getKey().getKey())
.build().submit(plugin);
}
@@ -243,29 +239,29 @@ public class MigrationGroupManager extends SubCommand<Object> {
}
}
plugin.getDatastore().saveGroup(group);
plugin.getStorage().saveGroup(group);
}
for (Map.Entry<UUID, Map<Map.Entry<String, String>, Boolean>> e : users.entrySet()) {
plugin.getDatastore().loadUser(e.getKey(), "null");
me.lucko.luckperms.users.User user = plugin.getUserManager().get(e.getKey());
plugin.getStorage().loadUser(e.getKey(), "null").join();
me.lucko.luckperms.common.core.model.User user = plugin.getUserManager().get(e.getKey());
for (Map.Entry<Map.Entry<String, String>, Boolean> n : e.getValue().entrySet()) {
// n.key.key = world
// n.key.value = node
// n.value = true/false
try {
user.setPermission("global-" + n.getKey().getKey() + "/" + n.getKey().getValue(), n.getValue());
user.setPermission(NodeFactory.fromSerialisedNode("global-" + n.getKey().getKey() + "/" + n.getKey().getValue(), n.getValue()));
if (n.getKey().getValue().startsWith("group.")) {
String group = n.getKey().getValue().substring(6);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(user).action("addgroup " + group + " global " + n.getKey().getKey())
.build().submit(plugin);
} else {
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(user).action("set " + n.getKey().getValue() + " " + n.getValue() + " global " + n.getKey().getKey())
.build().submit(plugin);
}
@@ -277,7 +273,20 @@ public class MigrationGroupManager extends SubCommand<Object> {
}
}
plugin.getDatastore().saveUser(user);
String primaryGroup = primaryGroups.get(e.getKey());
if (primaryGroup != null) {
try {
user.setPermission("group." + primaryGroup, true);
} catch (ObjectAlreadyHasException ignored) {
}
user.setPrimaryGroup(primaryGroup);
try {
user.unsetPermission("group.default");
} catch (ObjectLacksException ignored) {
}
}
plugin.getStorage().saveUser(user);
plugin.getUserManager().cleanup(user);
}
@@ -20,22 +20,25 @@
* SOFTWARE.
*/
package me.lucko.luckperms.commands.migration.subcommands;
package me.lucko.luckperms.bukkit.migration;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.MetaUtils;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
import me.lucko.luckperms.common.commands.SubCommand;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.constants.Constants;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.data.LogEntry;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.ArgumentChecker;
import ru.tehkode.permissions.NativeInterface;
import ru.tehkode.permissions.PermissionGroup;
import ru.tehkode.permissions.PermissionManager;
@@ -43,20 +46,20 @@ import ru.tehkode.permissions.PermissionUser;
import ru.tehkode.permissions.bukkit.PermissionsEx;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class MigrationPermissionsEx extends SubCommand<Object> {
public MigrationPermissionsEx() {
super("permissionsex", "Migration from PermissionsEx", "/%s migration permissionsex [world names]",
Permission.MIGRATION, Predicate.alwaysFalse());
super("permissionsex", "Migration from PermissionsEx", Permission.MIGRATION, Predicates.alwaysFalse(),
Arg.list(Arg.create("world names...", false, "a list of worlds to migrate permissions from"))
);
}
@SuppressWarnings("deprecation")
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
final Logger log = plugin.getLog();
if (!plugin.isPluginLoaded("PermissionsEx")) {
log.severe("PermissionsEx Migration: Error -> PermissionsEx is not loaded.");
@@ -74,13 +77,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
.collect(Collectors.toList());
PermissionsEx pex = (PermissionsEx) plugin.getPlugin("PermissionsEx");
PermissionManager manager; // The compiler complains if you call the method directly, as Bukkit is not in this module.
try {
manager = (PermissionManager) PermissionsEx.class.getMethod("getPermissionsManager").invoke(pex);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
return CommandResult.FAILURE;
}
PermissionManager manager = pex.getPermissionsManager();
NativeInterface ni;
try {
@@ -100,15 +97,15 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
for (PermissionGroup group : manager.getGroupList()) {
int groupWeight = group.getWeight() * -1;
groupCount ++;
groupCount++;
maxGroupWeight = Math.max(maxGroupWeight, groupWeight);
final String name = group.getName().toLowerCase();
plugin.getDatastore().createAndLoadGroup(name);
Group lpGroup = plugin.getGroupManager().get(name);
plugin.getStorage().createAndLoadGroup(name).join();
Group lpGroup = plugin.getGroupManager().getIfLoaded(name);
try {
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpGroup).action("create")
.build().submit(plugin);
} catch (Exception ex) {
@@ -126,7 +123,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
lpGroup.setPermission(node, value);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpGroup).action("set " + node + " " + value)
.build().submit(plugin);
} catch (Exception ex) {
@@ -151,7 +148,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
lpGroup.setPermission(node, value, "global", world);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpGroup).action("set " + node + " " + value + " global " + world)
.build().submit(plugin);
} catch (Exception ex) {
@@ -167,7 +164,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
lpGroup.setPermission("group." + g.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpGroup).action("setinherit " + g.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
@@ -183,7 +180,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
lpGroup.setPermission("group." + g.getName().toLowerCase(), true, "global", world);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpGroup).action("setinherit " + g.getName().toLowerCase() + " global " + world)
.build().submit(plugin);
} catch (Exception ex) {
@@ -199,11 +196,11 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
String suffix = group.getOwnSuffix();
if (prefix != null && !prefix.equals("")) {
prefix = ArgumentChecker.escapeCharacters(prefix);
prefix = MetaUtils.escapeCharacters(prefix);
try {
lpGroup.setPermission("prefix." + groupWeight + "." + prefix, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpGroup).action("set prefix." + groupWeight + "." + prefix + " true")
.build().submit(plugin);
} catch (Exception ex) {
@@ -214,11 +211,11 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
}
if (suffix != null && !suffix.equals("")) {
suffix = ArgumentChecker.escapeCharacters(suffix);
suffix = MetaUtils.escapeCharacters(suffix);
try {
lpGroup.setPermission("suffix." + groupWeight + "." + suffix, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpGroup).action("set suffix." + groupWeight + "." + suffix + " true")
.build().submit(plugin);
} catch (Exception ex) {
@@ -228,7 +225,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
}
}
plugin.getDatastore().saveGroup(lpGroup);
plugin.getStorage().saveGroup(lpGroup);
}
@@ -256,7 +253,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
}
userCount++;
plugin.getDatastore().loadUser(u, "null");
plugin.getStorage().loadUser(u, user.getName()).join();
User lpUser = plugin.getUserManager().get(u);
try {
@@ -270,7 +267,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
lpUser.setPermission(node, value);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpUser).action("set " + node + " " + value)
.build().submit(plugin);
} catch (Exception ex) {
@@ -295,7 +292,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
lpUser.setPermission(node, value, "global", world);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpUser).action("set " + node + " " + value + " global " + world)
.build().submit(plugin);
} catch (Exception ex) {
@@ -311,7 +308,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
lpUser.setPermission("group." + s.toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpUser).action("addgroup " + s.toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
@@ -327,7 +324,7 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
try {
lpUser.setPermission("group." + s.toLowerCase(), true, "global", world);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpUser).action("addgroup " + s.toLowerCase() + " global " + world)
.build().submit(plugin);
} catch (Exception ex) {
@@ -343,11 +340,11 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
String suffix = user.getOwnSuffix();
if (prefix != null && !prefix.equals("")) {
prefix = ArgumentChecker.escapeCharacters(prefix);
prefix = MetaUtils.escapeCharacters(prefix);
try {
lpUser.setPermission("prefix." + maxGroupWeight + "." + prefix, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpUser).action("set prefix." + maxGroupWeight + "." + prefix + " true")
.build().submit(plugin);
} catch (Exception ex) {
@@ -358,11 +355,11 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
}
if (suffix != null && !suffix.equals("")) {
suffix = ArgumentChecker.escapeCharacters(suffix);
suffix = MetaUtils.escapeCharacters(suffix);
try {
lpUser.setPermission("suffix." + maxGroupWeight + "." + suffix, true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(lpUser).action("set suffix." + maxGroupWeight + "." + suffix + " true")
.build().submit(plugin);
} catch (Exception ex) {
@@ -384,12 +381,13 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
if (primary != null) {
try {
lpUser.setPermission("group." + primary.toLowerCase(), true);
} catch (ObjectAlreadyHasException ignored) {}
} catch (ObjectAlreadyHasException ignored) {
}
lpUser.setPrimaryGroup(primary);
}
plugin.getUserManager().cleanup(lpUser);
plugin.getDatastore().saveUser(lpUser);
plugin.getStorage().saveUser(lpUser);
}
log.info("PermissionsEx Migration: Migrated " + userCount + " users.");
@@ -20,25 +20,34 @@
* SOFTWARE.
*/
package me.lucko.luckperms.commands.migration.subcommands;
package me.lucko.luckperms.bukkit.migration;
import com.github.cheesesoftware.PowerfulPermsAPI.*;
import lombok.Cleanup;
import com.github.cheesesoftware.PowerfulPermsAPI.CachedGroup;
import com.github.cheesesoftware.PowerfulPermsAPI.Group;
import com.github.cheesesoftware.PowerfulPermsAPI.Permission;
import com.github.cheesesoftware.PowerfulPermsAPI.PermissionManager;
import com.github.cheesesoftware.PowerfulPermsAPI.PowerfulPermsPlugin;
import com.github.cheesesoftware.PowerfulPermsAPI.ResultRunnable;
import com.google.common.util.concurrent.ListenableFuture;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandResult;
import me.lucko.luckperms.commands.Predicate;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.commands.SubCommand;
import me.lucko.luckperms.commands.migration.subcommands.utils.LPResultRunnable;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.bukkit.migration.utils.LPResultRunnable;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.commands.Arg;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
import me.lucko.luckperms.common.commands.SubCommand;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.constants.Constants;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.data.LogEntry;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.users.User;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -46,11 +55,17 @@ import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import static me.lucko.luckperms.constants.Permission.MIGRATION;
import static me.lucko.luckperms.common.constants.Permission.MIGRATION;
@SuppressWarnings("unchecked")
public class MigrationPowerfulPerms extends SubCommand<Object> {
@@ -75,25 +90,29 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
Class.forName("com.github.cheesesoftware.PowerfulPermsAPI.ResponseRunnable");
legacy = true;
} catch (ClassNotFoundException ignored) {}
} catch (ClassNotFoundException ignored) {
}
if (legacy) {
try {
getPlayerPermissionsMethod = PermissionManager.class.getMethod("getPlayerOwnPermissions", UUID.class, ResultRunnable.class);
getPlayerPermissionsMethod.setAccessible(true);
} catch (NoSuchMethodException ignored) {}
} catch (NoSuchMethodException ignored) {
}
} else {
try {
getPlayerPermissionsMethod = PermissionManager.class.getMethod("getPlayerOwnPermissions", UUID.class);
getPlayerPermissionsMethod.setAccessible(true);
} catch (NoSuchMethodException ignored) {}
} catch (NoSuchMethodException ignored) {
}
}
try {
getGroupMethod = CachedGroup.class.getMethod("getGroup");
getGroupMethod.setAccessible(true);
superLegacy = true;
} catch (NoSuchMethodException ignored) {}
} catch (NoSuchMethodException ignored) {
}
if (!legacy) {
try {
@@ -117,22 +136,6 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
}
public MigrationPowerfulPerms() {
super("powerfulperms", "Migration from PowerfulPerms",
"/%s migration powerfulperms <address> <database> <username> <password> <db table>", MIGRATION, Predicate.not(5));
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
try {
return run(plugin, args);
} catch (Throwable t) {
t.printStackTrace();
return CommandResult.FAILURE;
}
}
private static void getPlayerPermissions(PermissionManager manager, UUID uuid, Callback<List<Permission>> callback) {
if (legacy) {
try {
@@ -169,6 +172,28 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
}
public MigrationPowerfulPerms() {
super("powerfulperms", "Migration from PowerfulPerms", MIGRATION, Predicates.not(5),
Arg.list(
Arg.create("address", true, "the address of the PP database"),
Arg.create("database", true, "the name of the PP database"),
Arg.create("username", true, "the username to log into the DB"),
Arg.create("password", true, "the password to log into the DB"),
Arg.create("db table", true, "the name of the PP table where player data is stored")
)
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
try {
return run(plugin, args);
} catch (Throwable t) {
t.printStackTrace();
return CommandResult.FAILURE;
}
}
private CommandResult run(LuckPermsPlugin plugin, List<String> args) {
final Logger log = plugin.getLog();
if (!plugin.isPluginLoaded("PowerfulPerms")) {
@@ -242,11 +267,11 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
log.info("PowerfulPerms Migration: Starting group migration.");
Map<Integer, Group> groups = pm.getGroups(); // All versions
for (Group g : groups.values()) {
plugin.getDatastore().createAndLoadGroup(g.getName().toLowerCase());
final me.lucko.luckperms.groups.Group group = plugin.getGroupManager().get(g.getName().toLowerCase());
plugin.getStorage().createAndLoadGroup(g.getName().toLowerCase()).join();
final me.lucko.luckperms.common.core.model.Group group = plugin.getGroupManager().getIfLoaded(g.getName().toLowerCase());
try {
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(group).action("create")
.build().submit(plugin);
} catch (Exception ex) {
@@ -261,7 +286,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
group.setPermission("group." + parent.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(group).action("setinherit " + parent.getName().toLowerCase()) // All versions
.build().submit(plugin);
} catch (Exception ex) {
@@ -271,7 +296,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
}
}
plugin.getDatastore().saveGroup(group);
plugin.getStorage().saveGroup(group);
}
log.info("PowerfulPerms Migration: Group migration complete.");
@@ -284,7 +309,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
progress.put(uuid, new CountDownLatch(2));
// Create a LuckPerms user for the UUID
plugin.getDatastore().loadUser(uuid, "null");
plugin.getStorage().loadUser(uuid, "null").join();
User user = plugin.getUserManager().get(uuid);
// Get a list of Permissions held by the user from the PP API.
@@ -295,7 +320,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getStorage().saveUser(user);
plugin.getUserManager().cleanup(user);
}
}
@@ -328,7 +353,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
user.setPermission("group." + g.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(user).action("addgroup " + g.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
@@ -340,7 +365,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
user.setPermission("group." + g.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(user).action("addgroup " + g.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
@@ -361,7 +386,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L)
.build().submit(plugin);
} catch (Exception ex) {
@@ -373,7 +398,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server, g.getExpirationDate().getTime() / 1000L);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(user).action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
@@ -388,7 +413,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
user.setPermission("group." + group.getName().toLowerCase(), true);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(user).action("addgroup " + group.getName().toLowerCase())
.build().submit(plugin);
} catch (Exception ex) {
@@ -400,7 +425,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
user.setPermission("group." + group.getName().toLowerCase(), true, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(user).action("addgroup " + group.getName().toLowerCase() + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
@@ -418,7 +443,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
synchronized (progress) {
progress.get(uuid).countDown();
if (progress.get(uuid).getCount() == 0) {
plugin.getDatastore().saveUser(user);
plugin.getStorage().saveUser(user);
plugin.getUserManager().cleanup(user);
}
}
@@ -524,7 +549,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
holder.setPermission(node, value, server, world);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("set " + node + " " + value + " " + server + " " + world)
.build().submit(plugin);
} catch (Exception ex) {
@@ -536,7 +561,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
holder.setPermission(node, value, server, world, expireAt);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("settemp " + node + " " + value + " " + expireAt + " " + server + " " + world)
.build().submit(plugin);
} catch (Exception ex) {
@@ -551,7 +576,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
holder.setPermission(node, value, server);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("set " + node + " " + value + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
@@ -563,7 +588,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
holder.setPermission(node, value, server, expireAt);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("settemp " + node + " " + value + " " + expireAt + " " + server)
.build().submit(plugin);
} catch (Exception ex) {
@@ -577,7 +602,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
holder.setPermission(node, value);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("set " + node + " " + value)
.build().submit(plugin);
} catch (Exception ex) {
@@ -589,7 +614,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
try {
holder.setPermission(node, value, expireAt);
LogEntry.build()
.actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName())
.actor(Constants.CONSOLE_UUID).actorName(Constants.CONSOLE_NAME)
.acted(holder).action("settemp " + node + " " + value + " " + expireAt)
.build().submit(plugin);
} catch (Exception ex) {
@@ -0,0 +1,169 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.migration;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
import me.lucko.luckperms.common.commands.SubCommand;
import me.lucko.luckperms.common.commands.sender.Sender;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.Track;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import org.tyrannyofheaven.bukkit.zPermissions.ZPermissionsService;
import org.tyrannyofheaven.bukkit.zPermissions.dao.PermissionService;
import org.tyrannyofheaven.bukkit.zPermissions.model.EntityMetadata;
import org.tyrannyofheaven.bukkit.zPermissions.model.Entry;
import org.tyrannyofheaven.bukkit.zPermissions.model.Inheritance;
import org.tyrannyofheaven.bukkit.zPermissions.model.Membership;
import org.tyrannyofheaven.bukkit.zPermissions.model.PermissionEntity;
import java.lang.reflect.Field;
import java.util.List;
import java.util.UUID;
public class MigrationZPermissions extends SubCommand<Object> {
public MigrationZPermissions() {
super("zpermissions", "Migration from zPermissions", Permission.MIGRATION, Predicates.alwaysFalse(), null);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
final Logger log = plugin.getLog();
if (!plugin.isPluginLoaded("zPermissions")) {
log.severe("zPermissions Migration: Error -> zPermissions is not loaded.");
return CommandResult.STATE_ERROR;
}
ZPermissionsService service = (ZPermissionsService) plugin.getService(ZPermissionsService.class);
if (service == null) {
log.severe("zPermissions Migration: Error -> zPermissions is not loaded.");
return CommandResult.STATE_ERROR;
}
PermissionService internalService;
try {
Field psField = service.getClass().getDeclaredField("permissionService");
psField.setAccessible(true);
internalService = (PermissionService) psField.get(service);
} catch (Exception e) {
e.printStackTrace();
return CommandResult.FAILURE;
}
// Migrate all groups
log.info("zPermissions Migration: Starting group migration.");
for (String g : service.getAllGroups()) {
plugin.getStorage().createAndLoadGroup(g.toLowerCase()).join();
Group group = plugin.getGroupManager().getIfLoaded(g.toLowerCase());
PermissionEntity entity = internalService.getEntity(g, null, true);
migrateEntity(group, entity, null);
plugin.getStorage().saveGroup(group);
}
// Migrate all tracks
log.info("zPermissions Migration: Starting track migration.");
for (String t : service.getAllTracks()) {
plugin.getStorage().createAndLoadTrack(t.toLowerCase()).join();
Track track = plugin.getTrackManager().getIfLoaded(t.toLowerCase());
track.setGroups(service.getTrackGroups(t));
plugin.getStorage().saveTrack(track);
}
// Migrate all users.
log.info("zPermissions Migration: Starting user migration.");
for (UUID u : service.getAllPlayersUUID()) {
plugin.getStorage().loadUser(u, "null").join();
User user = plugin.getUserManager().get(u);
PermissionEntity entity = internalService.getEntity(null, u, false);
migrateEntity(user, entity, internalService.getGroups(u));
user.setPrimaryGroup(service.getPlayerPrimaryGroup(u));
if (!entity.isGroup()) {
user.setName(entity.getDisplayName());
}
plugin.getUserManager().cleanup(user);
plugin.getStorage().saveUser(user);
}
log.info("zPermissions Migration: Success! Completed without any errors.");
return CommandResult.SUCCESS;
}
private void migrateEntity(PermissionHolder group, PermissionEntity entity, List<Membership> memberships) {
for (Entry e : entity.getPermissions()) {
if (e.getWorld() != null) {
try {
group.setPermission(e.getPermission(), true, "global", e.getWorld().getName());
} catch (ObjectAlreadyHasException ignored) {
}
} else {
try {
group.setPermission(e.getPermission(), true); // TODO handle negated.
} catch (ObjectAlreadyHasException ignored) {
}
}
}
if (entity.isGroup()) {
for (Inheritance inheritance : entity.getInheritancesAsChild()) {
try {
if (!inheritance.getParent().getName().equals(group.getObjectName())) {
group.setPermission("group." + inheritance.getParent().getName(), true);
}
} catch (ObjectAlreadyHasException ignored) {
}
}
} else {
// entity.getMemberships() doesn't work (always returns 0 records)
for (Membership membership : memberships) {
try {
group.setPermission("group." + membership.getGroup().getDisplayName(), true);
} catch (ObjectAlreadyHasException ignored) {
}
}
}
for (EntityMetadata metadata : entity.getMetadata()) {
try {
group.setPermission(NodeFactory.makeMetaNode(metadata.getName(), metadata.getStringValue()).build());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@@ -20,12 +20,13 @@
* SOFTWARE.
*/
package me.lucko.luckperms.commands.migration.subcommands.utils;
package me.lucko.luckperms.bukkit.migration.utils;
import com.github.cheesesoftware.PowerfulPermsAPI.ResultRunnable;
/**
* Overrides the default ResultRunnable, callbacks will always run in the same thread. (an async one, hopefully.)
*
* @param <T> type
*/
public abstract class LPResultRunnable<T> extends ResultRunnable<T> {
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.model;
import lombok.Getter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ChildPermissionProvider {
private static void resolveChildren(Map<String, Boolean> accumulator, Map<String, Boolean> children, boolean invert) {
for (Map.Entry<String, Boolean> e : children.entrySet()) {
if (accumulator.containsKey(e.getKey())) {
continue; // Prevent infinite loops
}
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
boolean value = e.getValue() ^ invert;
String lName = e.getKey().toLowerCase();
accumulator.put(lName, value);
if (perm != null) {
resolveChildren(accumulator, perm.getChildren(), !value);
}
}
}
@Getter
private ImmutableMap<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> permissions = ImmutableMap.of();
public void setup() {
Map<Map.Entry<String, Boolean>, ImmutableMap<String, Boolean>> permissions = new HashMap<>();
for (Permission permission : Bukkit.getServer().getPluginManager().getPermissions()) {
// handle true
Map<String, Boolean> children = new HashMap<>();
resolveChildren(children, Collections.singletonMap(permission.getName(), true), false);
permissions.put(Maps.immutableEntry(permission.getName().toLowerCase(), true), ImmutableMap.copyOf(children));
// handle false
Map<String, Boolean> nChildren = new HashMap<>();
resolveChildren(nChildren, Collections.singletonMap(permission.getName(), false), false);
permissions.put(Maps.immutableEntry(permission.getName().toLowerCase(), false), ImmutableMap.copyOf(nChildren));
}
this.permissions = ImmutableMap.copyOf(permissions);
}
}
@@ -0,0 +1,201 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.api.Tristate;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class DefaultsProvider {
private static void unregisterDefaults(Map<String, Boolean> map, DummyPermissible p) {
Set<String> perms = map.keySet();
for (String name : perms) {
Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, p);
}
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(false, p);
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(true, p);
}
private static void calculateDefaults(Map<String, Boolean> map, DummyPermissible p, boolean op) {
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(op);
Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(op, p);
for (Permission perm : defaults) {
String name = perm.getName().toLowerCase();
map.put(name, true);
Bukkit.getServer().getPluginManager().subscribeToPermission(name, p);
calculateChildPermissions(map, p, perm.getChildren(), false);
}
}
private static void calculateChildPermissions(Map<String, Boolean> map, DummyPermissible p, Map<String, Boolean> children, boolean invert) {
for (Map.Entry<String, Boolean> e : children.entrySet()) {
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
boolean value = e.getValue() ^ invert;
String lName = e.getKey().toLowerCase();
map.put(lName, value);
Bukkit.getServer().getPluginManager().subscribeToPermission(e.getKey(), p);
if (perm != null) {
calculateChildPermissions(map, p, perm.getChildren(), !value);
}
}
}
@Getter
private Map<String, Boolean> opDefaults = ImmutableMap.of();
private final DummyPermissible opDummy = new DummyPermissible(this::refreshOp);
@Getter
private Map<String, Boolean> nonOpDefaults = ImmutableMap.of();
private final DummyPermissible nonOpDummy = new DummyPermissible(this::refreshNonOp);
public void refresh() {
refreshOp();
refreshNonOp();
}
private void refreshOp() {
unregisterDefaults(opDefaults, opDummy);
Map<String, Boolean> builder = new HashMap<>();
calculateDefaults(builder, opDummy, true);
opDefaults = ImmutableMap.copyOf(builder);
}
private void refreshNonOp() {
unregisterDefaults(nonOpDefaults, nonOpDummy);
Map<String, Boolean> builder = new HashMap<>();
calculateDefaults(builder, nonOpDummy, false);
nonOpDefaults = ImmutableMap.copyOf(builder);
}
public void close() {
unregisterDefaults(opDefaults, opDummy);
unregisterDefaults(nonOpDefaults, nonOpDummy);
}
public Tristate hasDefault(String permission, boolean isOp) {
Map<String, Boolean> map = isOp ? opDefaults : nonOpDefaults;
Boolean b = map.get(permission);
return b == null ? Tristate.UNDEFINED : Tristate.fromBoolean(b);
}
public int size() {
return opDefaults.size() + nonOpDefaults.size();
}
@AllArgsConstructor
private static class DummyPermissible implements Permissible {
private final Runnable onRefresh;
@Override
public void recalculatePermissions() {
onRefresh.run();
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return Collections.emptySet();
}
@Override
public boolean isPermissionSet(String name) {
return false;
}
@Override
public boolean isPermissionSet(Permission perm) {
return false;
}
@Override
public boolean hasPermission(String name) {
return false;
}
@Override
public boolean hasPermission(Permission perm) {
return false;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
return null;
}
@Override
public void removeAttachment(PermissionAttachment attachment) {
}
@Override
public boolean isOp() {
return false;
}
@Override
public void setOp(boolean value) {
}
}
}
@@ -20,23 +20,33 @@
* SOFTWARE.
*/
package me.lucko.luckperms.inject;
package me.lucko.luckperms.bukkit.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.LuckPermsPlugin;
import lombok.Setter;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.utils.PermissionCalculator;
import me.lucko.luckperms.utils.PermissionProcessor;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.core.model.User;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.*;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.permissions.PermissionRemovedExecutor;
import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -46,40 +56,81 @@ import java.util.stream.Collectors;
public class LPPermissible extends PermissibleBase {
@Getter
private final CommandSender parent;
private final PermissionCalculator calculator;
private final User user;
@Getter
private final Map<String, Boolean> luckPermsPermissions = new ConcurrentHashMap<>();
private final List<PermissionAttachment> attachments = new LinkedList<>();
private final Map<String, PermissionAttachmentInfo> attachmentPermissions = new HashMap<>();
private final Player parent;
public LPPermissible(@NonNull CommandSender sender, LuckPermsPlugin plugin) {
super(sender);
this.parent = sender;
@Getter
private final LPBukkitPlugin plugin;
List<PermissionProcessor> processors = new ArrayList<>(5);
processors.add(new PermissionCalculator.MapProcessor(luckPermsPermissions));
processors.add(new AttachmentProcessor(attachmentPermissions));
if (plugin.getConfiguration().getApplyWildcards()) {
processors.add(new PermissionCalculator.WildcardProcessor(luckPermsPermissions));
}
if (plugin.getConfiguration().getApplyRegex()) {
processors.add(new PermissionCalculator.RegexProcessor(luckPermsPermissions));
}
processors.add(new BukkitDefaultsProcessor(parent::isOp));
@Getter
private final SubscriptionManager subscriptions;
calculator = new PermissionCalculator(plugin, parent.getName(), plugin.getConfiguration().getDebugPermissionChecks(), processors);
@Getter
@Setter
private PermissibleBase oldPermissible = null;
// Attachment stuff.
@Getter
private final Map<String, PermissionAttachmentInfo> attachmentPermissions = new ConcurrentHashMap<>();
@Getter
private final List<PermissionAttachment> attachments = Collections.synchronizedList(new LinkedList<>());
public LPPermissible(@NonNull Player parent, User user, LPBukkitPlugin plugin) {
super(parent);
this.user = user;
this.parent = parent;
this.plugin = plugin;
this.subscriptions = new SubscriptionManager(this);
recalculatePermissions();
}
public void invalidateCache() {
calculator.invalidateCache();
public void updateSubscriptionsAsync() {
plugin.doAsync(this::updateSubscriptions);
}
@Override
public boolean isOp() {
return parent.isOp();
public void updateSubscriptions() {
Set<String> ent = new HashSet<>(user.getUserData().getPermissionData(calculateContexts()).getImmutableBacking().keySet());
if (parent.isOp()) {
ent.addAll(plugin.getDefaultsProvider().getOpDefaults().keySet());
} else {
ent.addAll(plugin.getDefaultsProvider().getNonOpDefaults().keySet());
}
subscriptions.subscribe(ent);
}
public void unsubscribeFromAllAsync() {
plugin.doAsync(this::unsubscribeFromAll);
}
public void unsubscribeFromAll() {
subscriptions.subscribe(Collections.emptySet());
}
public void addAttachments(List<PermissionAttachment> attachments) {
for (PermissionAttachment attachment : attachments) {
this.attachments.add(attachment);
}
}
public Contexts calculateContexts() {
return new Contexts(
plugin.getContextManager().getApplicableContext(parent),
plugin.getConfiguration().isIncludingGlobalPerms(),
plugin.getConfiguration().isIncludingGlobalWorldPerms(),
true,
plugin.getConfiguration().isApplyingGlobalGroups(),
plugin.getConfiguration().isApplyingGlobalWorldGroups(),
parent.isOp()
);
}
private boolean hasData() {
return user.getUserData() != null;
}
@Override
@@ -89,7 +140,7 @@ public class LPPermissible extends PermissibleBase {
@Override
public boolean isPermissionSet(@NonNull String name) {
return luckPermsPermissions.containsKey(name.toLowerCase()) || attachmentPermissions.containsKey(name.toLowerCase());
return hasData() && user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(name) != Tristate.UNDEFINED;
}
@Override
@@ -99,9 +150,11 @@ public class LPPermissible extends PermissibleBase {
@Override
public boolean hasPermission(@NonNull String name) {
Tristate ts = calculator.getPermissionValue(name);
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
if (hasData()) {
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(name);
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
}
return Permission.DEFAULT_PERMISSION.getValue(isOp());
@@ -109,14 +162,32 @@ public class LPPermissible extends PermissibleBase {
@Override
public boolean hasPermission(@NonNull Permission perm) {
Tristate ts = calculator.getPermissionValue(perm.getName());
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
if (hasData()) {
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(perm.getName());
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
}
return perm.getDefault().getValue(isOp());
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
perms.addAll(attachmentPermissions.values());
if (hasData()) {
perms.addAll(
user.getUserData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet().stream()
.map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue()))
.collect(Collectors.toList())
);
}
return perms;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value) {
if (!plugin.isEnabled()) {
@@ -166,7 +237,7 @@ public class LPPermissible extends PermissibleBase {
}
PermissionAttachment result = addAttachment(plugin);
if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new RemoveAttachmentRunnable(result), ticks) == -1) {
if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, result::remove, ticks) == -1) {
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1");
result.remove();
return null;
@@ -198,22 +269,14 @@ public class LPPermissible extends PermissibleBase {
}
attachmentPermissions.clear();
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(isOp());
Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(isOp(), parent);
for (Permission perm : defaults) {
String name = perm.getName().toLowerCase();
attachmentPermissions.put(name, new PermissionAttachmentInfo(parent, name, null, true));
Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent);
calculateChildPermissions(perm.getChildren(), false, null);
}
for (PermissionAttachment attachment : attachments) {
calculateChildPermissions(attachment.getPermissions(), false, attachment);
}
invalidateCache();
if (hasData()) {
user.getUserData().invalidatePermissionCalculators();
}
}
@Override
@@ -244,60 +307,4 @@ public class LPPermissible extends PermissibleBase {
}
}
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
perms.addAll(attachmentPermissions.values());
perms.addAll(luckPermsPermissions.entrySet().stream()
.map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue()))
.collect(Collectors.toList()));
return perms;
}
private class RemoveAttachmentRunnable implements Runnable {
private PermissionAttachment attachment;
private RemoveAttachmentRunnable(PermissionAttachment attachment) {
this.attachment = attachment;
}
public void run() {
attachment.remove();
}
}
@AllArgsConstructor
private static class AttachmentProcessor implements PermissionProcessor {
@Getter
private final Map<String, PermissionAttachmentInfo> map;
@Override
public Tristate hasPermission(String permission) {
if (map.containsKey(permission)) {
return Tristate.fromBoolean(map.get(permission).getValue());
}
return Tristate.UNDEFINED;
}
}
@AllArgsConstructor
private static class BukkitDefaultsProcessor implements PermissionProcessor {
private final Supplier<Boolean> isOp;
@Override
public Tristate hasPermission(String permission) {
Permission defPerm = Bukkit.getServer().getPluginManager().getPermission(permission);
if (defPerm != null) {
return Tristate.fromBoolean(defPerm.getDefault().getValue(isOp.get()));
} else {
return Tristate.UNDEFINED;
}
}
}
}
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.model;
import lombok.RequiredArgsConstructor;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@RequiredArgsConstructor
public class SubscriptionManager {
private final LPPermissible permissible;
private Set<String> currentSubscriptions = ImmutableSet.of();
public synchronized void subscribe(Set<String> perms) {
Set<String> newPerms = ImmutableSet.copyOf(perms);
Map.Entry<Set<String>, Set<String>> changes = compareSets(newPerms, currentSubscriptions);
Set<String> toAdd = changes.getKey();
Set<String> toRemove = changes.getValue();
permissible.getPlugin().doSync(() -> {
for (String s : toAdd) {
permissible.getPlugin().getServer().getPluginManager().subscribeToPermission(s, permissible.getParent());
}
for (String s : toRemove) {
permissible.getPlugin().getServer().getPluginManager().unsubscribeFromPermission(s, permissible.getParent());
}
});
this.currentSubscriptions = newPerms;
}
/**
* Compares two sets
* @param local the local set
* @param remote the remote set
* @return the entries to add to remote, and the entries to remove from remote
*/
private static Map.Entry<Set<String>, Set<String>> compareSets(Set<String> local, Set<String> remote) {
// entries in local but not remote need to be added
// entries in remote but not local need to be removed
Set<String> toAdd = new HashSet<>(local);
toAdd.removeAll(remote);
Set<String> toRemove = new HashSet<>(remote);
toRemove.removeAll(local);
return Maps.immutableEntry(toAdd, toRemove);
}
}
@@ -0,0 +1,395 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.vault;
import lombok.NonNull;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.core.NodeBuilder;
import me.lucko.luckperms.common.core.NodeFactory;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.utils.ExtractedContexts;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import net.milkbowl.vault.chat.Chat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static me.lucko.luckperms.api.MetaUtils.escapeCharacters;
import static me.lucko.luckperms.api.MetaUtils.unescapeCharacters;
/**
* Provides the Vault Chat service through the use of normal permission nodes.
*
* Prefixes / Suffixes:
* Normal inheritance rules apply.
* Permission Nodes = prefix.priority.value OR suffix.priority.value
* If a user/group has / inherits multiple prefixes and suffixes, the one with the highest priority is the one that
* will apply.
*
* Meta:
* Normal inheritance rules DO NOT apply.
* Permission Nodes = meta.node.value
*
* Note that special characters used within LuckPerms are escaped:
* See {@link me.lucko.luckperms.api.MetaUtils#unescapeCharacters(String)}
*/
public class VaultChatHook extends Chat {
private final VaultPermissionHook perms;
VaultChatHook(VaultPermissionHook perms) {
super(perms);
this.perms = perms;
}
public String getName() {
return perms.getName();
}
public boolean isEnabled() {
return perms.isEnabled();
}
private void setMeta(PermissionHolder holder, String world, String node, String value) {
String finalWorld = perms.isIgnoreWorld() ? null : world;
if (holder == null) return;
if (node.equals("")) return;
perms.log("Setting meta: '" + node + "' for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer());
perms.getScheduler().execute(() -> {
List<Node> toRemove = holder.getNodes().stream()
.filter(n -> n.isMeta() && n.getMeta().getKey().equals(node))
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
holder.unsetPermission(n);
} catch (ObjectLacksException ignored) {
}
});
Node.Builder metaNode = NodeFactory.makeMetaNode(node, value).setValue(true);
if (!perms.getServer().equalsIgnoreCase("global")) {
metaNode.setServer(perms.getServer());
}
if (finalWorld != null && !finalWorld.equals("")) {
metaNode.setServer(perms.getServer()).setWorld(finalWorld);
}
try {
holder.setPermission(metaNode.build());
} catch (ObjectAlreadyHasException ignored) {
}
perms.save(holder);
});
}
private void setChatMeta(boolean prefix, PermissionHolder holder, String value, String world) {
String finalWorld = perms.isIgnoreWorld() ? null : world;
if (holder == null) return;
if (value.equals("")) return;
perms.log("Setting " + (prefix ? "prefix" : "suffix") + " for " + holder.getObjectName() + " on world " + world + ", server " + perms.getServer());
perms.getScheduler().execute(() -> {
Node.Builder node = new NodeBuilder((prefix ? "prefix" : "suffix") + ".1000." + escapeCharacters(value));
node.setValue(true);
if (!perms.getServer().equalsIgnoreCase("global")) {
node.setServer(perms.getServer());
}
if (finalWorld != null && !finalWorld.equals("")) {
node.setServer(perms.getServer()).setWorld(finalWorld);
}
try {
holder.setPermission(node.build());
} catch (ObjectAlreadyHasException ignored) {
}
perms.save(holder);
});
}
private String getUserMeta(User user, String world, String node, String defaultValue) {
if (user == null) return defaultValue;
world = perms.isIgnoreWorld() ? null : world;
node = escapeCharacters(node);
perms.log("Getting meta: '" + node + "' for user " + user.getName() + " on world " + world + ", server " + perms.getServer());
if (user.getUserData() == null) {
return defaultValue;
}
return unescapeCharacters(user.getUserData().getMetaData(perms.createContextForWorld(world)).getMeta().getOrDefault(node, defaultValue));
}
private String getUserChatMeta(boolean prefix, User user, String world) {
if (user == null) return "";
world = perms.isIgnoreWorld() ? null : world;
perms.log("Getting " + (prefix ? "prefix" : "suffix") + " for user " + user.getName() + " on world " + world + ", server " + perms.getServer());
if (user.getUserData() == null) {
return "";
}
MetaData data = user.getUserData().getMetaData(perms.createContextForWorld(world));
String v = prefix ? data.getPrefix() : data.getSuffix();
return v == null ? "" : unescapeCharacters(v);
}
private String getGroupMeta(Group group, String world, String node, String defaultValue) {
world = perms.isIgnoreWorld() ? null : world;
if (group == null) return defaultValue;
if (node.equals("")) return defaultValue;
node = escapeCharacters(node);
perms.log("Getting meta: '" + node + "' for group " + group.getName() + " on world " + world + ", server " + perms.getServer());
for (Node n : group.getPermissions(true)) {
if (!n.getValue()) {
continue;
}
if (!n.isMeta()) {
continue;
}
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
return unescapeCharacters(meta.getValue());
}
}
return defaultValue;
}
private String getGroupChatMeta(boolean prefix, Group group, String world) {
world = perms.isIgnoreWorld() ? null : world;
if (group == null) return "";
perms.log("Getting " + (prefix ? "prefix" : "suffix") + " for group " + group + " on world " + world + ", server " + perms.getServer());
int priority = Integer.MIN_VALUE;
String meta = null;
Map<String, String> context = new HashMap<>();
context.put("server", perms.getServer());
if (world != null) {
context.put("world", world);
}
ExtractedContexts ec = ExtractedContexts.generate(new Contexts(ContextSet.fromMap(context), perms.isIncludeGlobal(), true, true, true, true, false));
for (Node n : group.getAllNodes(null, ec)) {
if (!n.getValue()) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue;
}
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
}
}
return meta == null ? "" : unescapeCharacters(meta);
}
public String getPlayerPrefix(String world, @NonNull String player) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
return getUserChatMeta(true, user, world);
}
public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
setChatMeta(true, user, prefix, world);
}
public String getPlayerSuffix(String world, @NonNull String player) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
return getUserChatMeta(false, user, world);
}
public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
setChatMeta(false, user, suffix, world);
}
public String getGroupPrefix(String world, @NonNull String group) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
return getGroupChatMeta(true, g, world);
}
public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
setChatMeta(true, g, prefix, world);
}
public String getGroupSuffix(String world, @NonNull String group) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
return getGroupChatMeta(false, g, world);
}
public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
setChatMeta(false, g, suffix, world);
}
public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
try {
return Integer.parseInt(getUserMeta(user, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
setMeta(user, world, node, String.valueOf(value));
}
public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
try {
return Integer.parseInt(getGroupMeta(g, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
setMeta(g, world, node, String.valueOf(value));
}
public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
try {
return Double.parseDouble(getUserMeta(user, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
setMeta(user, world, node, String.valueOf(value));
}
public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
try {
return Double.parseDouble(getGroupMeta(g, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
setMeta(g, world, node, String.valueOf(value));
}
public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
String s = getUserMeta(user, world, node, String.valueOf(defaultValue));
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
return defaultValue;
}
return Boolean.parseBoolean(s);
}
public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
setMeta(user, world, node, String.valueOf(value));
}
public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
String s = getGroupMeta(g, world, node, String.valueOf(defaultValue));
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
return defaultValue;
}
return Boolean.parseBoolean(s);
}
public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
setMeta(g, world, node, String.valueOf(value));
}
public String getPlayerInfoString(String world, @NonNull String player, @NonNull String node, String defaultValue) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
return getUserMeta(user, world, node, defaultValue);
}
public void setPlayerInfoString(String world, @NonNull String player, @NonNull String node, String value) {
final User user = perms.getPlugin().getUserManager().getByUsername(player);
setMeta(user, world, node, value);
}
public String getGroupInfoString(String world, @NonNull String group, @NonNull String node, String defaultValue) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
return getGroupMeta(g, world, node, defaultValue);
}
public void setGroupInfoString(String world, @NonNull String group, @NonNull String node, String value) {
final Group g = perms.getPlugin().getGroupManager().getIfLoaded(group);
setMeta(g, world, node, value);
}
}
@@ -20,42 +20,60 @@
* SOFTWARE.
*/
package me.lucko.luckperms.api.vault;
package me.lucko.luckperms.bukkit.vault;
import lombok.Getter;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.LPBukkitPlugin;
import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.ServicesManager;
/**
* Handles hooking with the Vault API
*/
@Getter
public class VaultHook {
private VaultChatHook chatHook = null;
private VaultPermissionHook permissionHook = null;
private static VaultChatHook chatHook = null;
private static VaultPermissionHook permissionHook = null;
public static void hook(LPBukkitPlugin plugin) {
public void hook(LPBukkitPlugin plugin) {
try {
if (permissionHook == null) {
permissionHook = new VaultPermissionHook();
permissionHook = new VaultPermissionHook(plugin);
}
permissionHook.setPlugin(plugin);
permissionHook.setServer(plugin.getConfiguration().getVaultServer());
permissionHook.setIncludeGlobal(plugin.getConfiguration().getVaultIncludeGlobal());
if (chatHook == null) {
chatHook = new VaultChatHook(permissionHook);
}
chatHook.setPlugin(plugin);
final ServicesManager sm = plugin.getServer().getServicesManager();
sm.unregisterAll(plugin);
sm.register(Permission.class, permissionHook, plugin, ServicePriority.High);
sm.register(Chat.class, chatHook, plugin, ServicePriority.Low);
sm.register(Chat.class, chatHook, plugin, ServicePriority.High);
} catch (Exception e) {
e.printStackTrace();
}
}
public void unhook(LPBukkitPlugin plugin) {
final ServicesManager sm = plugin.getServer().getServicesManager();
if (permissionHook != null) {
sm.unregister(Permission.class, permissionHook);
permissionHook.getScheduler().cancelTask();
permissionHook = null;
}
if (chatHook != null) {
sm.unregister(Chat.class, chatHook);
chatHook = null;
}
}
public boolean isHooked() {
return permissionHook != null && chatHook != null;
}
}
@@ -0,0 +1,421 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bukkit.vault;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.PermissionData;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.core.model.Group;
import me.lucko.luckperms.common.core.model.PermissionHolder;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import net.milkbowl.vault.permission.Permission;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
/**
* The LuckPerms Vault Permission implementation
* Most lookups are cached.
*/
@Getter
public class VaultPermissionHook extends Permission {
private LPBukkitPlugin plugin;
private VaultScheduler scheduler;
private final String name = "LuckPerms";
private String server = "global";
private boolean includeGlobal = true;
private boolean ignoreWorld = false;
private boolean pgo = false;
private boolean pgoCheckInherited = false;
private boolean pgoCheckExists = true;
private boolean pgoCheckMemberOf = true;
private Function<String, String> WORLD_CORRECTION_FUNCTION = s -> ignoreWorld ? null : s;
public VaultPermissionHook(LPBukkitPlugin plugin) {
this.plugin = plugin;
this.scheduler = new VaultScheduler(plugin);
super.plugin = plugin;
// Config options
this.server = plugin.getConfiguration().getVaultServer();
this.includeGlobal = plugin.getConfiguration().isVaultIncludingGlobal();
this.ignoreWorld = plugin.getConfiguration().isVaultIgnoreWorld();
this.pgo = plugin.getConfiguration().isVaultPrimaryGroupOverrides();
this.pgoCheckInherited = plugin.getConfiguration().isVaultPrimaryGroupOverridesCheckInherited();
this.pgoCheckExists = plugin.getConfiguration().isVaultPrimaryGroupOverridesCheckExists();
this.pgoCheckMemberOf = plugin.getConfiguration().isVaultPrimaryGroupOverridesCheckMemberOf();
}
public void log(String s) {
if (plugin.getConfiguration().isVaultDebug()) {
plugin.getLog().info("[VAULT] " + s);
}
}
@Override
public boolean isEnabled() {
return plugin.isEnabled();
}
@Override
public boolean hasSuperPermsCompat() {
return true;
}
/**
* Generic method to add a permission to a holder
*
* @param world the world to add in
* @param holder the holder to add the permission to
* @param permission the permission to add
*/
private CompletableFuture<Void> add(String world, PermissionHolder holder, String permission) {
return CompletableFuture.runAsync(() -> {
try {
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
holder.setPermission(permission, true, server, world);
} else {
holder.setPermission(permission, true, server);
}
save(holder);
} catch (ObjectAlreadyHasException ignored) {}
}, scheduler);
}
/**
* Generic method to remove a permission from a holder
*
* @param world the world to remove in
* @param holder the holder to remove the permission from
* @param permission the permission to remove
*/
private CompletableFuture<Void> remove(String world, PermissionHolder holder, String permission) {
return CompletableFuture.runAsync(() -> {
try {
if (world != null && !world.equals("") && !world.equalsIgnoreCase("global")) {
holder.unsetPermission(permission, server, world);
} else {
holder.unsetPermission(permission, server);
}
save(holder);
} catch (ObjectLacksException ignored) {}
}, scheduler);
}
/**
* Utility method to asynchronously save a user or group
*
* @param holder the holder instance
*/
public void save(PermissionHolder holder) {
if (holder instanceof User) {
User u = (User) holder;
plugin.getStorage().saveUser(u).thenRunAsync(() -> u.getRefreshBuffer().request(), plugin.getAsyncExecutor());
}
if (holder instanceof Group) {
Group g = (Group) holder;
plugin.getStorage().saveGroup(g).thenRunAsync(() -> plugin.getUpdateTaskBuffer().request(), plugin.getAsyncExecutor());
}
}
public Contexts createContextForWorld(String world) {
Map<String, String> context = new HashMap<>();
if (world != null && !world.equals("")) {
context.put("world", world);
}
context.put("server", server);
return new Contexts(ContextSet.fromMap(context), isIncludeGlobal(), true, true, true, true, false);
}
@Override
public boolean playerHas(String world, @NonNull String player, @NonNull String permission) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Checking if player " + player + " has permission: " + permission + " on world " + world + ", server " + server);
User user = plugin.getUserManager().getByUsername(player);
if (user == null) return false;
if (user.getUserData() == null) {
return false;
}
// Effectively fallback to the standard Bukkit #hasPermission check.
return user.getUserData().getPermissionData(createContextForWorld(world)).getPermissionValue(permission).asBoolean();
}
@Override
public boolean playerAdd(String world, @NonNull String player, @NonNull String permission) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Adding permission to player " + player + ": '" + permission + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().getByUsername(player);
if (user == null) return false;
add(world, user, permission);
return true;
}
@Override
public boolean playerRemove(String world, @NonNull String player, @NonNull String permission) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Removing permission from player " + player + ": '" + permission + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().getByUsername(player);
if (user == null) return false;
remove(world, user, permission);
return true;
}
@Override
public boolean groupHas(String world, @NonNull String groupName, @NonNull String permission) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Checking if group " + groupName + " has permission: " + permission + " on world " + world + ", server " + server);
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
if (group == null) return false;
// This is a nasty call. Groups aren't cached. :(
Map<String, Boolean> permissions = group.exportNodes(createContextForWorld(world), true);
return permissions.containsKey(permission.toLowerCase()) && permissions.get(permission.toLowerCase());
}
@Override
public boolean groupAdd(String world, @NonNull String groupName, @NonNull String permission) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Adding permission to group " + groupName + ": '" + permission + "' on world " + world + ", server " + server);
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
if (group == null) return false;
add(world, group, permission);
return true;
}
@Override
public boolean groupRemove(String world, @NonNull String groupName, @NonNull String permission) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Removing permission from group " + groupName + ": '" + permission + "' on world " + world + ", server " + server);
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
if (group == null) return false;
remove(world, group, permission);
return true;
}
@Override
public boolean playerInGroup(String world, @NonNull String player, @NonNull String group) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Checking if player " + player + " is in group: " + group + " on world " + world + ", server " + server);
final User user = plugin.getUserManager().getByUsername(player);
if (user == null) return false;
String w = world; // screw effectively final
return user.getNodes().stream()
.filter(Node::isGroupNode)
.filter(n -> n.shouldApplyOnServer(server, isIncludeGlobal(), false))
.filter(n -> n.shouldApplyOnWorld(w, true, false))
.map(Node::getGroupName)
.anyMatch(s -> s.equalsIgnoreCase(group));
}
@Override
public boolean playerAddGroup(String world, @NonNull String player, @NonNull String groupName) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Adding player " + player + " to group: '" + groupName + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().getByUsername(player);
if (user == null) return false;
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
if (group == null) return false;
String w = world;
scheduler.execute(() -> {
try {
if (w != null && !w.equals("") && !w.equalsIgnoreCase("global")) {
user.setInheritGroup(group, server, w);
} else {
user.setInheritGroup(group, server);
}
save(user);
} catch (ObjectAlreadyHasException ignored) {}
});
return true;
}
@Override
public boolean playerRemoveGroup(String world, @NonNull String player, @NonNull String groupName) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Removing player " + player + " from group: '" + groupName + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().getByUsername(player);
if (user == null) return false;
final Group group = plugin.getGroupManager().getIfLoaded(groupName);
if (group == null) return false;
String w = world;
scheduler.execute(() -> {
try {
if (w != null && !w.equals("") && !w.equalsIgnoreCase("global")) {
user.unsetInheritGroup(group, server, w);
} else {
user.unsetInheritGroup(group, server);
}
save(user);
} catch (ObjectLacksException ignored) {}
});
return true;
}
@Override
public String[] getPlayerGroups(String world, @NonNull String player) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Getting groups of player: " + player + ", on world " + world + ", server " + server);
User user = plugin.getUserManager().getByUsername(player);
if (user == null) return new String[0];
String w = world; // screw effectively final
return user.getNodes().stream()
.filter(Node::isGroupNode)
.filter(n -> n.shouldApplyOnServer(server, isIncludeGlobal(), false))
.filter(n -> n.shouldApplyOnWorld(w, true, false))
.map(Node::getGroupName)
.toArray(String[]::new);
}
@Override
public String getPrimaryGroup(String world, @NonNull String player) {
world = WORLD_CORRECTION_FUNCTION.apply(world);
log("Getting primary group of player: " + player);
final User user = plugin.getUserManager().getByUsername(player);
if (user == null) {
return null;
}
if (!pgo) {
String g = user.getPrimaryGroup();
return plugin.getConfiguration().getGroupNameRewrites().getOrDefault(g, g);
}
if (pgoCheckInherited) {
PermissionData data = user.getUserData().getPermissionData(createContextForWorld(world));
for (Map.Entry<String, Boolean> e : data.getImmutableBacking().entrySet()) {
if (!e.getValue()) {
continue;
}
if (!e.getKey().toLowerCase().startsWith("vault.primarygroup.")) {
continue;
}
String group = e.getKey().substring("vault.primarygroup.".length());
if (pgoCheckExists) {
if (!plugin.getGroupManager().isLoaded(group)) {
continue;
}
}
if (pgoCheckMemberOf) {
if (data.getPermissionValue("group." + group) != Tristate.TRUE) {
continue;
}
}
return group;
}
} else {
for (LocalizedNode node : user.getPermissions(true)) {
if (!node.getValue()) {
continue;
}
if (!node.getPermission().toLowerCase().startsWith("vault.primarygroup.")) {
continue;
}
if (!node.shouldApplyOnServer(server, isIncludeGlobal(), false)) {
continue;
}
if (!node.shouldApplyOnWorld(world, true, false)) {
continue;
}
String group = node.getPermission().substring("vault.primarygroup.".length());
if (pgoCheckExists) {
if (!plugin.getGroupManager().isLoaded(group)) {
continue;
}
}
if (pgoCheckMemberOf) {
if (!user.getLocalGroups(server, world, isIncludeGlobal()).contains(group.toLowerCase())) {
continue;
}
}
return group;
}
}
// Fallback
String g = user.getPrimaryGroup();
return plugin.getConfiguration().getGroupNameRewrites().getOrDefault(g, g);
}
@Override
public String[] getGroups() {
return plugin.getGroupManager().getAll().keySet().toArray(new String[0]);
}
@Override
public boolean hasGroupSupport() {
return true;
}
}
@@ -20,53 +20,55 @@
* SOFTWARE.
*/
package me.lucko.luckperms.commands;
package me.lucko.luckperms.bukkit.vault;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import org.bukkit.scheduler.BukkitTask;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Executes commands consecutively
* Sequential executor for Vault modifications
*/
@RequiredArgsConstructor
public class ConsecutiveExecutor implements Runnable {
private final CommandManager manager;
public class VaultScheduler implements Runnable, Executor {
private final List<QueuedCommand> commands = new ArrayList<>();
private BukkitTask task = null;
private final List<Runnable> tasks = new ArrayList<>();
public void queueCommand(QueuedCommand command) {
synchronized (commands) {
commands.add(command);
public VaultScheduler(LPBukkitPlugin plugin) {
task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this, 1L, 1L);
}
@Override
public void execute(Runnable r) {
synchronized (tasks) {
tasks.add(r);
}
}
@Override
public void run() {
final List<QueuedCommand> toExecute = new ArrayList<>();
synchronized (commands) {
toExecute.addAll(commands);
commands.clear();
}
if (toExecute.isEmpty()) {
return;
}
for (QueuedCommand command : toExecute) {
manager.onCommand(command.getSender(), "perms", command.getArgs());
List<Runnable> toRun;
synchronized (tasks) {
if (tasks.isEmpty()) {
return;
}
toRun = new ArrayList<>();
toRun.addAll(tasks);
tasks.clear();
}
toRun.forEach(Runnable::run);
}
@Getter
@AllArgsConstructor
public static class QueuedCommand {
private final Sender sender;
private final List<String> args;
public void cancelTask() {
if (task != null) {
task.cancel();
task = null;
}
}
}
@@ -1,108 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.inject;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.PermissibleBase;
import java.lang.reflect.Field;
/**
* Injects a {@link LPPermissible} into a {@link Player}
*/
@UtilityClass
public class Injector {
private static Field HUMAN_ENTITY_FIELD;
static {
try {
HUMAN_ENTITY_FIELD = Class.forName(getInternalClassName("entity.CraftHumanEntity")).getDeclaredField("perm");
HUMAN_ENTITY_FIELD.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean inject(CommandSender sender, PermissibleBase permissible) {
try {
Field f = getPermField(sender);
f.set(sender, permissible);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean unInject(CommandSender sender) {
try {
Permissible permissible = getPermissible(sender);
if (permissible instanceof LPPermissible) {
getPermField(sender).set(sender, new PermissibleBase(sender));
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static Permissible getPermissible(CommandSender sender) {
try {
Field f = getPermField(sender);
return (Permissible) f.get(sender);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Field getPermField(CommandSender sender) {
if (sender instanceof Player) {
return HUMAN_ENTITY_FIELD;
}
throw new RuntimeException("Couldn't get perm field for sender " + sender.getClass().getName());
}
private static String getInternalClassName(String className) {
Class server = Bukkit.getServer().getClass();
if (!server.getSimpleName().equals("CraftServer")) {
throw new RuntimeException("Couldn't inject into server " + server);
}
String version;
if (server.getName().equals("org.bukkit.craftbukkit.CraftServer")) {
// Non versioned class
version = ".";
} else {
version = server.getName().substring("org.bukkit.craftbukkit".length());
version = version.substring(0, version.length() - "CraftServer".length());
}
return "org.bukkit.craftbukkit" + version + className;
}
}
@@ -1,122 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.users;
import lombok.Getter;
import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.api.implementation.internal.UserLink;
import me.lucko.luckperms.inject.LPPermissible;
import me.lucko.luckperms.utils.Contexts;
import org.bukkit.permissions.Permissible;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
public class BukkitUser extends User {
private final LPBukkitPlugin plugin;
@Getter
@Setter
private LPPermissible lpPermissible = null;
BukkitUser(UUID uuid, LPBukkitPlugin plugin) {
super(uuid, plugin);
this.plugin = plugin;
}
BukkitUser(UUID uuid, String username, LPBukkitPlugin plugin) {
super(uuid, username, plugin);
this.plugin = plugin;
}
@SuppressWarnings("unchecked")
@Override
public synchronized void refreshPermissions() {
if (lpPermissible == null) {
return;
}
// Calculate the permissions that should be applied. This is done async, who cares about how long it takes or how often it's done.
Map<String, Boolean> toApply = exportNodes(
new Contexts(
getPlugin().getConfiguration().getServer(),
plugin.getUserManager().getWorldCache().get(getUuid()),
null,
getPlugin().getConfiguration().getIncludeGlobalPerms(),
getPlugin().getConfiguration().getIncludeGlobalWorldPerms(),
true,
getPlugin().getConfiguration().getApplyGlobalGroups(),
getPlugin().getConfiguration().getApplyGlobalWorldGroups()
),
Collections.emptyList()
);
try {
Map<String, Boolean> existing = lpPermissible.getLuckPermsPermissions();
boolean different = false;
if (toApply.size() != existing.size()) {
different = true;
} else {
for (Map.Entry<String, Boolean> e : existing.entrySet()) {
if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) {
continue;
}
different = true;
break;
}
}
if (!different) return;
existing.clear();
lpPermissible.invalidateCache();
existing.putAll(toApply);
if (plugin.getConfiguration().getAutoOp()) {
boolean op = false;
for (Map.Entry<String, Boolean> e : toApply.entrySet()) {
if (e.getKey().equalsIgnoreCase("luckperms.autoop") && e.getValue()) {
op = true;
break;
}
}
final boolean finalOp = op;
if (lpPermissible.isOp() != op) {
final Permissible parent = lpPermissible.getParent();
plugin.doSync(() -> parent.setOp(finalOp));
}
}
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -1,92 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.users;
import lombok.Getter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.inject.Injector;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class BukkitUserManager extends UserManager {
private final LPBukkitPlugin plugin;
@Getter
private final Map<UUID, String> worldCache = new ConcurrentHashMap<>();
public BukkitUserManager(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
public void preUnload(User user) {
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (player != null) {
if (u.getLpPermissible() != null) {
Injector.unInject(player);
u.setLpPermissible(null);
}
if (plugin.getConfiguration().getAutoOp()) {
player.setOp(false);
}
}
}
}
@Override
public void cleanup(User user) {
if (plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())) == null) {
unload(user);
}
}
@Override
public User make(UUID uuid) {
return new BukkitUser(uuid, plugin);
}
@Override
public User make(UUID uuid, String username) {
return new BukkitUser(uuid, username, plugin);
}
@Override
public void updateAllUsers() {
// Sometimes called async, so we need to get the players on the Bukkit thread.
plugin.doSync(() -> {
Set<UUID> players = plugin.getServer().getOnlinePlayers().stream()
.map(p -> plugin.getUuidCache().getUUID(p.getUniqueId()))
.collect(Collectors.toSet());
plugin.doAsync(() -> players.forEach(u -> plugin.getDatastore().loadUser(u, "null")));
});
}
}
+179 -13
View File
@@ -5,6 +5,10 @@
# +------------------------------------------------------------------------+ #
##############################################################################
# +------------------------------------------------------------------------+ #
# | General | #
# +------------------------------------------------------------------------+ #
# The name of the server, used for server specific permissions. Set to 'global' to disable.
server: global
@@ -24,21 +28,31 @@ apply-global-world-groups: true
# If this server is in offline or online mode.
# This setting allows a player to have the same UUID across a network of offline mode/mixed servers.
#
# You should generally reflect the setting in server.properties here. Except when...
#
# 1. You have Spigot servers connected to a BungeeCord proxy, with online-mode set to false, but 'bungeecord' set to true in the spigot.yml
# AND 'ip-forward' set to true in the BungeeCord config.yml
# In this case, set online-mode in LuckPerms to true, despite the server being in offline mode.
#
# 2. You are only running one server instance using LuckPerms, (not a network)
# In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache)
#
# 3. If your proxy is running in offline mode, and you are using PaperSpigot (https://ci.destroystokyo.com/job/PaperSpigot/),
# you should set "bungee-online-mode" to false in the paper.yml, and set "online-mode" to true in all LuckPerms configs.
# This approach is thoroughly recommended for offline mode networks.
online-mode: true
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# +------------------------------------------------------------------------+ #
# | Permission Calculation | #
# +------------------------------------------------------------------------+ #
# If the plugin should apply wildcard permissions.
# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered permissions matching
# the wildcard.
@@ -53,11 +67,17 @@ apply-regex: true
# If set to true, LuckPerms will detect and expand shorthand node patterns.
apply-shorthand: true
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# Define special group weights for this server.
# Default is just 0.
group-weight:
# admin: 10
# If LuckPerms should print to console every time a plugin checks if a player has a permission
debug-permission-checks: false
# +------------------------------------------------------------------------+ #
# | OP (Server Operator) Settings | #
# +------------------------------------------------------------------------+ #
# If the vanilla OP system is enabled. If set to false, all users will be de-opped, and the op/deop commands will be disabled.
enable-ops: true
@@ -67,12 +87,23 @@ enable-ops: true
# Additionally, setting this to true will force the "enable-ops" option above to false. All users will be de-opped unless
# they have the permission node, and the op/deop commands will be disabled.
#
# It is important to note that this setting is only checked when a player first joins the server, and when they switch
# worlds. Therefore, simply removing this permission from a user will not automatically de-op them. A player needs to
# relog to have the change take effect.
#
# It is recommended that you use this option instead of assigning a single '*' permission.
auto-op: false
# If opped players should be allowed to use LuckPerms commands. Set to false to only allow users who have the permissions access to the commands
commands-allow-op: true
# +------------------------------------------------------------------------+ #
# | Vault | #
# +------------------------------------------------------------------------+ #
# The name of the server used within Vault operations. If you don't want Vault operations to be server specific, set this
# to "global".
vault-server: global
@@ -80,14 +111,48 @@ vault-server: global
# If global permissions should be considered when retrieving meta or player groups
vault-include-global: true
# If Vault operations should ignore any world arguments if supplied.
vault-ignore-world: false
# This block controls the Primary Group override feature
# See the wiki for more information.
vault-primary-groups-overrides:
# If the feature is enabled
enabled: false
# If the check should query the user's inherited permissions.
# (a value of false only checks the permissions they explicitly have)
check-inherited-permissions: false
# If LuckPerms should check if the group exists
check-group-exists: true
# If LuckPerms should check if the user is actually a member of the group
check-user-member-of: true
# If LuckPerms should print debugging info to console when a plugin uses a Vault function
vault-debug: false
# Mirrors world names. Whenever LuckPerms checks what world a user is in, if the world name is in this list, the value assigned
# will be sent forward for permission calculation instead.
world-rewrite:
# world_nether: world
# world_the_end: world
# Rewrites group names. The underlying name of the group does not change, just the output in commands / placeholders / Vault.
group-name-rewrite:
# default: Member
# +------------------------------------------------------------------------+ #
# | Storage | #
# +------------------------------------------------------------------------+ #
# Which storage method the plugin should use.
# Currently supported: mysql, sqlite, h2, json, yaml, mongodb
# Fill out connection info below if you're using MySQL or MongoDB
# Currently supported: mysql, postgresql, sqlite, h2, json, yaml, mongodb
# Fill out connection info below if you're using MySQL, PostgreSQL or MongoDB
storage-method: h2
# This block enables support for split datastores.
# Only touch this if you're sure that you know what you're doing.
# I (the author) do not endorse nor recommend the use of this feature.
split-storage:
enabled: false
methods:
@@ -98,11 +163,112 @@ split-storage:
log: h2
data:
address: localhost:3306
# Uses standard DB engine port by default
# MySQL: 3306, PostgreSQL: 5432, MongoDB: 27017
# Specify as "host:port" if differs
address: localhost
database: minecraft
username: root
password: ''
pool-size: 10 # The size of the MySQL connection pool.
# The prefix for all LuckPerms tables. Change this is you want to use different tables for different servers.
# This should *not* be set to "lp_" if you have previously ran LuckPerms v2.16.81 or earlier with this database.
table_prefix: 'luckperms_'
# Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing.
# e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources.
sync-minutes: 3
# Settings for Redis.
#
# If enabled and configured, LuckPerms will use the Redis PubSub system to inform other
# connected servers of changes. Use the command "/luckperms networksync" to push changes.
# Data is NOT stored on redis. It is only used as a messaging platform.
#
# If you decide to enable this feature, you should set "sync-minutes" to -1, as there is no need for LuckPerms
# to poll the database for changes.
#
# Port 6379 is used by default; set address to "host:port" if differs
redis:
enabled: false
address: localhost
password: ''
# +------------------------------------------------------------------------+ #
# | Default Assignments | #
# +------------------------------------------------------------------------+ #
# This section allows you to define defaults to give users whenever they connect to the server.
# The default assignments are highly configurable and conditional.
#
# There is one default assignment built into LuckPerms, which will add all users to the "default" group if they
# are not a member of any other group. This setting cannot be disabled. However, you can use this section to add more of
# your own.
#
# IMPORTANT:
# In order to save storage space, LuckPerms does not store users who have no permissions defined, and are only a member
# of the default group. Adding default assignments to this section will negate this effect. It is HIGHLY RECCOMENDED
# that instead of assigning defaults here, you add permissions to the "default" group, or set the "default" group to inherit
# other groups, and then use the group-name-rewrite rule above.
#
# It is also important to note that these rules are considered every time a player logs into the server, and are applied
# directly to the user's data. Simply removing a rule here will not reserse the effect of that rule on any users who have
# already had it applied to them.
#
# The "has" and "lacks" conditions below support standard boolean logic, using the 'and' & 'or' characters used in Java.
# e.g. "(some.other.permission | some.permission.other) & some.thing.else" == a user has 'some.other.permission', or
# 'some.permission.other', and they also have 'some.thing.else'
#
# Groups are represented by the permission node: group.<group name>
# Per server and per world nodes are represented by: "server-world/permission" or "server/permission"
#
# Within conditions, permission nodes MUST be escaped using "<" and ">". See the example below.
#
# Explaination of the examples below: (they're just to demonstrate the features & use cases)
#
# rule1:
# If a user is either in the vip or vip+ group, and they have the "titles.titlecollector" permission set to true, and the
# "some.random.permission" set to false... if they're not in the group "prison_titlepack" on the "prison" server, then
# give add them to the "prison_titlepack" group on the "prison" server, and remove "some.random.permission".
#
# rule2:
# If the user isn't in any of the following groups on the skyblock server: sb_level1, sb_level2, sb_level3, then add
# them to sb_level1 on the skyblock server.
#
# rule3:
# If the user is a member of the default group, remove them from default, add them to member, and set their primary group
# to member.
#
# WARNING: Unlike internal commands, this system does not ensure that a group exists before adding a user to it.
# It also does not unsure that a user is a member of a group before making that group their primary group.
#
# Before you use "give: group.<name>" or "set-primary-group", make sure that the group exists, and that the user is
# a member of the group.
default-assignments:
# rule1:
# if:
# has-true: (<group.vip> | <group.vip+>) & <titles.tilecollector>
# has-false: <some.random.permission>
# lacks: <prison/group.prison_titlepack>
# give:
# - prison/group.prison_titlepack
# take:
# - some.random.permission
# rule2:
# if:
# lacks: <skyblock/group.sb_level1> & <skyblock/group.sb_level2> & <skyblock/group.sb_level3>
# give:
# - skyblock/group.sb_level1
# rule3:
# if:
# has-true: <group.default>
# take:
# - group.default
# give:
# - group.member
# set-primary-group: member
+10 -4
View File
@@ -4,13 +4,19 @@ description: A permissions plugin
author: Luck
website: https://github.com/lucko/LuckPerms
main: me.lucko.luckperms.LPBukkitPlugin
softdepend: [Vault, PermissionsEx, GroupManager, PowerfulPerms, zPermissions, bPermissions] # For migration
main: me.lucko.luckperms.bukkit.LPBukkitPlugin
load: STARTUP
# This means that all plugins that (soft-)depend on Vault, depend on LuckPerms too.
# It in turn fixes issues where plugins using Vault cache the provided instance when their plugin enables, or
# when they check for the presence of a service provider, before LuckPerms has enabled.
loadbefore: [Vault]
commands:
luckperms:
description: Manage permissions
aliases: [perms, permissions, lp, p, perm]
aliases: [perms, permissions, lp, perm]
# Permissions are registered programmatically instead of here.
# Permissions are registered programmatically instead of here.
# See: the last method in me.lucko.luckperms.bukkit.LPBukkitPlugin
+20 -26
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.9-SNAPSHOT</version>
<version>2.17-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -14,7 +14,7 @@
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPermsBungee-${release.version}.${git.closest.tag.commit.count}</finalName>
<finalName>LuckPerms-Bungee-${release.version}.${git.closest.tag.commit.count}</finalName>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<resources>
<resource>
@@ -46,28 +46,8 @@
<minimizeJar>false</minimizeJar>
<relocations>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>me.lucko.luckperms.lib.slf4j</shadedPattern>
</relocation>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>com.mongodb</pattern>
<shadedPattern>me.lucko.luckperms.lib.mongodb</shadedPattern>
</relocation>
<relocation>
<pattern>org.bson</pattern>
<shadedPattern>me.lucko.luckperms.lib.bson</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
</relocation>
<relocation>
<pattern>org.sqlite</pattern>
<shadedPattern>me.lucko.luckperms.lib.sqlite</shadedPattern>
<pattern>io.github.mkremins.fanciful</pattern>
<shadedPattern>me.lucko.luckperms.lib.fanciful</shadedPattern>
</relocation>
</relocations>
</configuration>
@@ -114,6 +94,13 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- slf4j library -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.9</version>
<scope>provided</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
@@ -121,6 +108,13 @@
<version>1.16.10</version>
<scope>provided</scope>
</dependency>
<!-- **************Migration Plugins************** -->
<dependency>
<groupId>net.alpenblock</groupId>
<artifactId>BungeePerms</artifactId>
<version>3.0-dev-52</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -1,61 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms;
import lombok.Getter;
import me.lucko.luckperms.utils.PermissionCalculator;
import me.lucko.luckperms.utils.PermissionProcessor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BungeePlayerCache {
private final PermissionCalculator calculator;
@Getter
private final Map<String, Boolean> permissions = new ConcurrentHashMap<>();
public BungeePlayerCache(LuckPermsPlugin plugin, String name) {
List<PermissionProcessor> processors = new ArrayList<>(3);
processors.add(new PermissionCalculator.MapProcessor(permissions));
if (plugin.getConfiguration().getApplyWildcards()) {
processors.add(new PermissionCalculator.WildcardProcessor(permissions));
}
if (plugin.getConfiguration().getApplyRegex()) {
processors.add(new PermissionCalculator.RegexProcessor(permissions));
}
calculator = new PermissionCalculator(plugin, name, plugin.getConfiguration().getDebugPermissionChecks(), processors);
}
public void invalidateCache() {
calculator.invalidateCache();
}
public boolean getPermissionValue(String permission) {
return calculator.getPermissionValue(permission).asBoolean();
}
}
@@ -1,218 +0,0 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms;
import lombok.Getter;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.data.Importer;
import me.lucko.luckperms.groups.GroupManager;
import me.lucko.luckperms.runnables.ExpireTemporaryTask;
import me.lucko.luckperms.runnables.UpdateTask;
import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.storage.StorageFactory;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.BungeeUserManager;
import me.lucko.luckperms.users.UserManager;
import me.lucko.luckperms.utils.LogFactory;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Getter
public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
private final Map<UUID, BungeePlayerCache> playerCache = new ConcurrentHashMap<>();
private final Set<UUID> ignoringLogs = ConcurrentHashMap.newKeySet();
private LPConfiguration configuration;
private UserManager userManager;
private GroupManager groupManager;
private TrackManager trackManager;
private Datastore datastore;
private UuidCache uuidCache;
private ApiProvider apiProvider;
private Logger log;
private Importer importer;
private ConsecutiveExecutor consecutiveExecutor;
@Override
public void onEnable() {
log = LogFactory.wrap(getLogger());
getLog().info("Loading configuration...");
configuration = new BungeeConfig(this);
// register events
getProxy().getPluginManager().registerListener(this, new BungeeListener(this));
// register commands
getLog().info("Registering commands...");
CommandManager commandManager = new CommandManager(this);
getProxy().getPluginManager().registerCommand(this, new BungeeCommand(commandManager));
// disable the default Bungee /perms command so it gets handled by the Bukkit plugin
getProxy().getDisabledCommands().add("perms");
datastore = StorageFactory.getDatastore(this, "h2");
getLog().info("Loading internal permission managers...");
uuidCache = new UuidCache(getConfiguration().getOnlineMode());
userManager = new BungeeUserManager(this);
groupManager = new GroupManager(this);
trackManager = new TrackManager();
importer = new Importer(commandManager);
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
int mins = getConfiguration().getSyncTime();
if (mins > 0) {
getProxy().getScheduler().schedule(this, new UpdateTask(this), mins, mins, TimeUnit.MINUTES);
}
// 20 times per second (once per "tick")
getProxy().getScheduler().schedule(this, BungeeSenderFactory.get(), 50L, 50L, TimeUnit.MILLISECONDS);
getProxy().getScheduler().schedule(this, new ExpireTemporaryTask(this), 3L, 3L, TimeUnit.SECONDS);
getProxy().getScheduler().schedule(this, consecutiveExecutor, 1L, 1L, TimeUnit.SECONDS);
getLog().info("Registering API...");
apiProvider = new ApiProvider(this);
LuckPerms.registerProvider(apiProvider);
// Run update task to refresh any online users
getLog().info("Scheduling Update Task to refresh any online users.");
try {
new UpdateTask(this).run();
} catch (Exception e) {
e.printStackTrace();
}
getLog().info("Successfully loaded.");
}
@Override
public void onDisable() {
getLog().info("Closing datastore...");
datastore.shutdown();
getLog().info("Unregistering API...");
LuckPerms.unregisterProvider();
}
@Override
public String getVersion() {
return getDescription().getVersion();
}
@Override
public PlatformType getType() {
return PlatformType.BUNGEE;
}
@Override
public File getMainDir() {
return getDataFolder();
}
@Override
public Message getPlayerStatus(UUID uuid) {
return getProxy().getPlayer(getUuidCache().getExternalUUID(uuid)) != null ? Message.PLAYER_ONLINE : Message.PLAYER_OFFLINE;
}
@Override
public int getPlayerCount() {
return getProxy().getOnlineCount();
}
@Override
public List<String> getPlayerList() {
return getProxy().getPlayers().stream().map(ProxiedPlayer::getName).collect(Collectors.toList());
}
@Override
public List<Sender> getNotifyListeners() {
return getProxy().getPlayers().stream()
.map(p -> BungeeSenderFactory.get().wrap(p, Collections.singleton(Permission.LOG_NOTIFY)))
.filter(Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
public Sender getConsoleSender() {
return BungeeSenderFactory.get().wrap(getProxy().getConsole());
}
@Override
public List<String> getPossiblePermissions() {
// No such thing on Bungee. Wildcards are processed in the listener instead.
return Collections.emptyList();
}
@Override
public Object getPlugin(String name) {
return getProxy().getPluginManager().getPlugin(name);
}
@Override
public Object getService(Class clazz) {
return null;
}
@Override
public UUID getUUID(String playerName) {
return null; // Not needed on Bungee
}
@Override
public boolean isPluginLoaded(String name) {
return getProxy().getPluginManager().getPlugins().stream()
.filter(p -> p.getDescription().getName().equalsIgnoreCase(name))
.findAny()
.isPresent();
}
@Override
public void runUpdateTask() {
doAsync(new UpdateTask(this));
}
@Override
public void doAsync(Runnable r) {
getProxy().getScheduler().runAsync(this, r);
}
@Override
public void doSync(Runnable r) {
r.run();
}
}
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bungee;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.MutableContextSet;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ServerSwitchEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.util.Map;
public class BackendServerCalculator extends ContextCalculator<ProxiedPlayer> implements Listener {
private static final String WORLD_KEY = "world";
private static String getServer(ProxiedPlayer player) {
return player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName());
}
@Override
public MutableContextSet giveApplicableContext(ProxiedPlayer subject, MutableContextSet accumulator) {
String server = getServer(subject);
if (server != null) {
accumulator.add(Maps.immutableEntry(WORLD_KEY, server));
}
return accumulator;
}
@Override
public boolean isContextApplicable(ProxiedPlayer subject, Map.Entry<String, String> context) {
if (!context.getKey().equals(WORLD_KEY)) {
return false;
}
String server = getServer(subject);
return server != null && server.equals(context.getValue());
}
@EventHandler
public void onPlayerServerSwitch(ServerSwitchEvent e) {
pushUpdate(e.getPlayer(), Maps.immutableEntry("null", "null"), Maps.immutableEntry(WORLD_KEY, getServer(e.getPlayer())));
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.bungee;
import lombok.AllArgsConstructor;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.calculators.PermissionProcessor;
import me.lucko.luckperms.common.calculators.processors.MapProcessor;
import me.lucko.luckperms.common.calculators.processors.RegexProcessor;
import me.lucko.luckperms.common.calculators.processors.WildcardProcessor;
import me.lucko.luckperms.common.core.model.User;
@AllArgsConstructor
public class BungeeCalculatorFactory extends AbstractCalculatorFactory {
private final LPBungeePlugin plugin;
@Override
public PermissionCalculator build(Contexts contexts, User user) {
ImmutableList.Builder<PermissionProcessor> processors = ImmutableList.builder();
processors.add(new MapProcessor());
if (plugin.getConfiguration().isApplyingWildcards()) {
processors.add(new WildcardProcessor());
}
if (plugin.getConfiguration().isApplyingRegex()) {
processors.add(new RegexProcessor());
}
return registerCalculator(new PermissionCalculator(plugin, user.getName(), processors.build()));
}
}
@@ -20,14 +20,15 @@
* SOFTWARE.
*/
package me.lucko.luckperms;
package me.lucko.luckperms.bungee;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.common.commands.CommandManager;
import me.lucko.luckperms.common.commands.utils.Util;
import me.lucko.luckperms.common.constants.Patterns;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
@@ -35,25 +36,26 @@ import net.md_5.bungee.api.plugin.TabExecutor;
import java.util.Arrays;
class BungeeCommand extends Command implements TabExecutor {
private final LPBungeePlugin plugin;
private final CommandManager manager;
BungeeCommand(CommandManager manager) {
BungeeCommand(LPBungeePlugin plugin, CommandManager manager) {
super("luckpermsbungee", null, "bperms", "lpb", "bpermissions", "bp", "bperm");
this.plugin = plugin;
this.manager = manager;
}
@Override
public void execute(CommandSender sender, String[] args) {
manager.onCommand(
BungeeSenderFactory.get().wrap(sender),
plugin.getSenderFactory().wrap(sender),
"bperms",
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args))),
Callback.empty()
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args)))
);
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
return manager.onTabComplete(BungeeSenderFactory.get().wrap(sender), Arrays.asList(args));
return manager.onTabComplete(plugin.getSenderFactory().wrap(sender), Arrays.asList(args));
}
}
@@ -20,9 +20,10 @@
* SOFTWARE.
*/
package me.lucko.luckperms;
package me.lucko.luckperms.bungee;
import me.lucko.luckperms.common.config.AbstractConfiguration;
import me.lucko.luckperms.core.LPConfiguration;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
@@ -31,8 +32,13 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
class BungeeConfig extends LPConfiguration<LPBungeePlugin> {
class BungeeConfig extends AbstractConfiguration<LPBungeePlugin> {
private Configuration configuration;
BungeeConfig(LPBungeePlugin plugin) {
@@ -62,11 +68,6 @@ class BungeeConfig extends LPConfiguration<LPBungeePlugin> {
}
}
@Override
protected void set(String path, Object value) {
configuration.set(path, value);
}
@Override
protected String getString(String path, String def) {
return configuration.getString(path, def);
@@ -81,4 +82,34 @@ class BungeeConfig extends LPConfiguration<LPBungeePlugin> {
protected boolean getBoolean(String path, boolean def) {
return configuration.getBoolean(path, def);
}
@Override
protected List<String> getList(String path, List<String> def) {
return Optional.ofNullable(configuration.getStringList(path)).orElse(def);
}
@Override
protected List<String> getObjectList(String path, List<String> def) {
Configuration section = configuration.getSection(path);
if (section == null) {
return def;
}
return Optional.ofNullable(section.getKeys().stream().collect(Collectors.toList())).orElse(def);
}
@Override
protected Map<String, String> getMap(String path, Map<String, String> def) {
Map<String, String> map = new HashMap<>();
Configuration section = configuration.getSection(path);
if (section == null) {
return def;
}
for (String key : section.getKeys()) {
map.put(key, section.getString(key));
}
return map;
}
}
@@ -20,18 +20,26 @@
* SOFTWARE.
*/
package me.lucko.luckperms;
package me.lucko.luckperms.bungee;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.event.events.UserFirstLoginEvent;
import me.lucko.luckperms.common.constants.Message;
import me.lucko.luckperms.common.core.UuidCache;
import me.lucko.luckperms.common.core.model.User;
import me.lucko.luckperms.common.defaults.Rule;
import me.lucko.luckperms.common.utils.AbstractListener;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.AbstractListener;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.*;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PermissionCheckEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -55,15 +63,30 @@ public class BungeeListener extends AbstractListener implements Listener {
final ProxiedPlayer player = ((ProxiedPlayer) e.getSender());
BungeePlayerCache playerCache = plugin.getPlayerCache().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (playerCache == null) {
User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) {
return;
}
e.setHasPermission(playerCache.getPermissionValue(e.getPermission()));
if (user.getUserData() == null) {
plugin.getLog().warn("Player " + player.getName() + " does not have any user data setup.");
return;
}
Contexts contexts = new Contexts(
plugin.getContextManager().getApplicableContext(player),
plugin.getConfiguration().isIncludingGlobalPerms(),
plugin.getConfiguration().isIncludingGlobalWorldPerms(),
true,
plugin.getConfiguration().isApplyingGlobalGroups(),
plugin.getConfiguration().isApplyingGlobalWorldGroups(),
false
);
e.setHasPermission(user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission()).asBoolean());
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(LoginEvent e) {
/* Delay the login here, as we want to cache UUID data before the player is connected to a backend bukkit server.
This means that a player will have the same UUID across the network, even if parts of the network are running in
@@ -75,22 +98,48 @@ public class BungeeListener extends AbstractListener implements Listener {
final PendingConnection c = e.getConnection();
if (!cache.isOnlineMode()) {
UUID uuid = plugin.getDatastore().getUUID(c.getName());
UUID uuid = plugin.getStorage().getUUID(c.getName()).join();
if (uuid != null) {
cache.addToCache(c.getUniqueId(), uuid);
} else {
// No previous data for this player
plugin.getApiProvider().fireEventAsync(new UserFirstLoginEvent(c.getUniqueId(), c.getName()));
cache.addToCache(c.getUniqueId(), c.getUniqueId());
plugin.getDatastore().saveUUIDData(c.getName(), c.getUniqueId());
plugin.getStorage().force().saveUUIDData(c.getName(), c.getUniqueId()).join();
}
} else {
String name = plugin.getStorage().getName(c.getUniqueId()).join();
if (name == null) {
plugin.getApiProvider().fireEventAsync(new UserFirstLoginEvent(c.getUniqueId(), c.getName()));
}
// Online mode, no cache needed. This is just for name -> uuid lookup.
plugin.getDatastore().saveUUIDData(c.getName(), c.getUniqueId());
plugin.getStorage().force().saveUUIDData(c.getName(), c.getUniqueId()).join();
}
// We have to make a new user on this thread whilst the connection is being held, or we get concurrency issues as the Bukkit server
// and the BungeeCord server try to make a new user at the same time.
plugin.getDatastore().loadUser(cache.getUUID(c.getUniqueId()), c.getName());
plugin.getStorage().force().loadUser(cache.getUUID(c.getUniqueId()), c.getName()).join();
User user = plugin.getUserManager().get(cache.getUUID(c.getUniqueId()));
if (user == null) {
plugin.getLog().warn("Failed to load user: " + c.getName());
} else {
// Setup defaults for the user
boolean save = false;
for (Rule rule : plugin.getConfiguration().getDefaultAssignments()) {
if (rule.apply(user)) {
save = true;
}
}
// If they were given a default, persist the new assignments back to the storage.
if (save) {
plugin.getStorage().force().saveUser(user).join();
}
user.setupData(false); // Pretty nasty calculation call. Sets up the caching system so data is ready when the user joins.
}
final long time = System.currentTimeMillis() - startTime;
if (time >= 1000) {
plugin.getLog().warn("Processing login for " + c.getName() + " took " + time + "ms.");
@@ -102,25 +151,15 @@ public class BungeeListener extends AbstractListener implements Listener {
@EventHandler
public void onPlayerPostLogin(PostLoginEvent e) {
final ProxiedPlayer player = e.getPlayer();
final UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
final User user = plugin.getUserManager().get(internal);
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
if (user == null) {
plugin.getProxy().getScheduler().schedule(plugin, () -> player.sendMessage(WARN_MESSAGE), 3, TimeUnit.SECONDS);
} else {
plugin.getPlayerCache().put(internal, new BungeePlayerCache(plugin, e.getPlayer().getName()));
user.refreshPermissions();
}
}
@EventHandler
public void onPlayerQuit(PlayerDisconnectEvent e) {
plugin.getPlayerCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
onLeave(e.getPlayer().getUniqueId());
}
@EventHandler
public void onPlayerServerSwitch(ServerSwitchEvent e) {
refreshPlayer(e.getPlayer().getUniqueId());
}
}
@@ -20,26 +20,24 @@
* SOFTWARE.
*/
package me.lucko.luckperms;
package me.lucko.luckperms.bungee;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.commands.sender.SenderFactory;
import me.lucko.luckperms.common.constants.Constants;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import me.lucko.luckperms.commands.SenderFactory;
import me.lucko.luckperms.constants.Constants;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.chat.ComponentSerializer;
import io.github.mkremins.fanciful.FancyMessage;
import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BungeeSenderFactory extends SenderFactory<CommandSender> {
private static BungeeSenderFactory instance = null;
public static synchronized BungeeSenderFactory get() {
if (instance == null) {
instance = new BungeeSenderFactory();
}
return instance;
public BungeeSenderFactory(LuckPermsPlugin plugin) {
super(plugin);
}
@Override
@@ -47,7 +45,7 @@ public class BungeeSenderFactory extends SenderFactory<CommandSender> {
if (sender instanceof ProxiedPlayer) {
return sender.getName();
}
return Constants.getConsoleName();
return Constants.CONSOLE_NAME;
}
@Override
@@ -55,7 +53,7 @@ public class BungeeSenderFactory extends SenderFactory<CommandSender> {
if (sender instanceof ProxiedPlayer) {
return ((ProxiedPlayer) sender).getUniqueId();
}
return Constants.getConsoleUUID();
return Constants.CONSOLE_UUID;
}
@Override
@@ -63,6 +61,15 @@ public class BungeeSenderFactory extends SenderFactory<CommandSender> {
sender.sendMessage(new TextComponent(s));
}
@Override
protected void sendMessage(CommandSender sender, FancyMessage message) {
try {
sender.sendMessage(ComponentSerializer.parse(message.toJSONString()));
} catch (Exception e) {
sendMessage(sender, message.toOldMessageFormat());
}
}
@Override
protected boolean hasPermission(CommandSender sender, String node) {
return sender.hasPermission(node);
Loaded 100 of 462 files, more files were not shown because too many files have changed in this diff. Show more