Compare commits

...
325 Commits
Author SHA1 Message Date
Luck 175a21c0e4 API 4.0 - this is a breaking change 2017-11-07 22:05:49 +00:00
Luck a2801bff7c Inject a fake map into the super PermissionAttachment instance to fix compat with plugins using reflection to change attachment data (#528) 2017-11-07 17:05:16 +00:00
Luck f1a50f433f Throw exception earlier if an invalid permission is added/removed via Vault (#526) 2017-11-07 15:22:31 +00:00
Luck 76bfde4d77 Don't prevent modification of group.<group name> nodes with permission subcommands 2017-11-04 19:12:46 +00:00
Luck d393a4c9ca Alias /lp player --> /lp user 2017-11-03 16:26:54 +00:00
Luck 9ea6b0b8bb Use "None" instead of "global" when no server name is specified 2017-11-02 19:32:13 +00:00
Luck c20b91d5ea Increase actor_name field length to account for changes to logging 2017-11-02 19:28:49 +00:00
Luck fad8a38bdf Use a string version of the full static context, as opposed to the server name 2017-11-02 19:25:35 +00:00
Luck 2440a38e82 add RedisBungee context calculator for the BungeeCord platform 2017-11-02 19:01:53 +00:00
Luck b7541c43c8 Add Sender#getNameWithLocation 2017-11-02 18:51:59 +00:00
Luck 0f10bb0bc9 Deprecate some unused methods in ImmutableNode, refactor #shouldApplyWithContext 2017-11-02 18:22:42 +00:00
Luck 357717886e Refactor the log creation process 2017-10-30 21:06:45 +00:00
Luck 94a4051d29 Add extra catch for null primary group in powerfulperms migration 2017-10-30 20:03:59 +00:00
Luck 0e581cbcf3 Increase actions actor_name column length (#525) 2017-10-30 20:03:03 +00:00
Luck 230d9725e8 cleanup & fix build 2017-10-29 20:37:29 +00:00
Luck dee41b315f Inject our own subscription map into the plugin manager instead of registering permissions individually for each player 2017-10-29 15:16:48 +00:00
Luck 72e6c75433 Cleanup / tidy up a number of classes 2017-10-29 12:58:49 +00:00
Luck ebe12f838c Fix user exports 2017-10-28 00:15:34 +01:00
Luck 7279fdc25f Fix changes not being saved for flatfile bulkupdate operations 2017-10-27 23:17:57 +01:00
Luck af5c859385 Fix temporary group display format in parent info command (#518) 2017-10-24 13:17:27 +01:00
Luck e8896fbdf7 Fix NPE on startup (#520) 2017-10-24 13:05:06 +01:00
Luck 54695b8046 Form more descriptive console & import usernames 2017-10-23 23:37:55 +01:00
Luck c573c755d2 Add option to cancel failed logins on BungeeCord variant 2017-10-23 16:59:07 +01:00
Luck b299301b69 Cleanup 2017-10-22 21:02:48 +01:00
Luck a3a7198777 More storage refactoring 2017-10-22 20:09:53 +01:00
Luck ca8cc91f37 Add group setdisplayname command (#493) 2017-10-22 16:58:53 +01:00
Luck 26c813c07b Mode storage refactoring - write group nodes into their own section in flatfile types (#502) 2017-10-22 14:22:52 +01:00
Luck f109cb684a Apply PermissionAttachment permissions at a higher priority than normal nodes using the transient system (#515) 2017-10-22 10:24:04 +01:00
Luck 1baefaade4 Try to find the most appropriate primary group before just adding a user to default - closes #506 2017-10-22 09:16:04 +01:00
Luck 5ac0f93214 Fix migrating null / non-existent usernames - reverts parts of 6bfeec6, fixes #514 2017-10-22 09:07:08 +01:00
Luck ecfbed00b1 Add config option to allow invalid usernames (#516) 2017-10-22 09:00:10 +01:00
Luck 6e429d6c78 Throw exception if directories cannot be created 2017-10-22 08:48:00 +01:00
Luck fa4bb0447a Rewrite flatfile storage (YAML & JSON) to use configurate, add HOCON storage method 2017-10-21 23:20:45 +01:00
Luck aa0be40124 Cleanup verbose handler & only send sponge OP command notification if the sender has permission 2017-10-16 18:55:17 +01:00
Luck 04d5310c04 Ensure users are a member of their primary group (#506) 2017-10-16 18:53:02 +01:00
Luck 74069af309 Throw an exception if we're unable to create the lib dir 2017-10-16 15:28:02 +01:00
Luck 6bfeec6d16 Fix updating primary groups for players who've never joined the server with SQL storage types (#500) 2017-10-16 14:43:58 +01:00
Luck 0f4c057395 Add highest_inherited and lowest_inherited meta stack elements 2017-10-15 15:13:23 +01:00
Luck 2fd74f3b7e fix MetaStackElement equality checks 2017-10-15 14:51:42 +01:00
Luck fb171a57c2 Ensure stored primary groups are always lowercase'd 2017-10-15 14:32:57 +01:00
Luck b26fc69e73 Remove usage of the now-redundant ExtractedContexts class, other misc cleanup 2017-10-15 14:23:51 +01:00
Luck 28961b1cfa Primary groups should come first, not last, when ordering groups for inheritance (#500) 2017-10-15 11:22:38 +01:00
Luck 597f2f7b1b Make parents-by-weight the default primary group calculation method 2017-10-14 22:54:54 +01:00
Luck 00cd8bb3f3 Prioritise primary groups when two inherited groups have the same weight (#500) 2017-10-14 22:50:50 +01:00
Luck 5732e60510 Remove cleanupUsers functionality
It was pretty useless (and wasn't implemented on SQL/Mongo storage types.) Data is naturally cleaned up over time as users are loaded/saved.
2017-10-12 20:38:08 +01:00
Luck 7b271e21e3 Update readme 2017-10-12 20:25:51 +01:00
Luck 1a5f9683c2 Fix build (whoops) 2017-10-12 20:21:49 +01:00
Luck 3b74d55442 Remove sponge comparison chart from readme 2017-10-12 20:19:53 +01:00
Luck 8920396360 Remove context pre-processing (mostly)
This approach isn't very effective when contexts are frequently changing, and it is hard to guess in advance which contexts are going to be in-use.

The Sponge version has proven that this whole system isn't really necessary.

Contexts for 'allow all' and 'global' are still pre-processed, however this should be significantly less work for the server. (even if it is being done async)
2017-10-12 20:17:52 +01:00
Luck 9c505e4402 Move Track methods away from checked exceptions, refactor ContextManager & primary group holders 2017-10-12 19:43:42 +01:00
Luck de4c6b384c Export using the /lp alias 2017-10-12 18:47:35 +01:00
Luck 6dec1ba8af Add hu_HU locale - thanks @montlikadani 2017-10-03 19:04:47 +01:00
Luck 9e6788de2f Refactor context manager 2017-10-03 19:02:14 +01:00
Luck 1bd7e5c9c4 Refactor sponge cache invalidation 2017-09-29 23:21:12 +01:00
Luck ec75fdddb5 Make verbose output more readable when a check is made against a lot of contexts 2017-09-27 20:11:17 +01:00
Luck f98aafb97c Fix world content on BungeeCord not being lowercase'd 2017-09-24 23:43:39 +01:00
Luck f4d2a0daf9 Bump dependency versions 2017-09-23 23:36:13 +01:00
Luck de8fb8de90 Only load slf4j if it's not already present on the server 2017-09-23 21:15:45 +01:00
Luck 44d137b8f9 Add project Code of Conduct 2017-09-22 16:09:50 +01:00
Luck 777c972bdc Cache SubjectReference instances, general cleanup 2017-09-21 21:59:27 +01:00
Luck f9efa15781 Re-add 'list' as an alias of 'permission info' - reverts #461, closes #473 2017-09-20 20:31:24 +01:00
Luck 1d1cd679dd Fix tab completion inconsistencies caused by argument rewrites - closes #481 2017-09-20 20:27:46 +01:00
Luck a9ed4686dd Avoid loading slf4j when running on Sponge 2017-09-19 22:13:02 +01:00
Luck 5b742eb5b2 Only schedule a user cleanup when LuckPermsApi#cleanupUser is called 2017-09-18 20:50:50 +01:00
Luck a3d78f4d0e Always load dependencies into the plugin classloader, and not it's parent - fixes #479 2017-09-17 19:30:43 +01:00
Luck 214929e5e5 Fix group expiry time not being exported (#474) 2017-09-14 19:10:18 +01:00
Luck 67602b8d85 Fix startup deadlock 2017-09-13 21:24:35 +01:00
Luck 2412edaa3a Cleanup subscription updates & UserManager#loadAllUsers 2017-09-13 20:18:48 +01:00
EuropeJing b1584aa2c3 Update zh-CN locale (#466) 2017-09-12 08:47:37 +01:00
Luck a14a416193 Make some ingame messages more consistent with the LP theme, general cleanup 2017-09-11 22:21:57 +01:00
Luck f3251e9407 Fix missing depend 2017-09-10 21:45:47 +01:00
Luck 096885d91f Add tracing to /lp verbose, API updates/cleanup, add login process event, and utilise string interning for faster context/node comparisons 2017-09-10 21:25:00 +01:00
Frank Zhang 368700156c Added uranium server support to DependencyManager (#464) 2017-09-08 14:25:49 +01:00
Luck 81cf50520b Fix NodeFactory#nodeAsCommand when unsetting meta nodes, fix meta unset command 2017-09-06 21:56:41 +01:00
Luck 649748c2d3 Bump caffeine and hikari versions (#462) 2017-09-05 23:56:45 +01:00
Luck bf7a708440 Send some extra data to the editor data object 2017-09-05 14:54:22 +01:00
Luck 8faa4d137b Fix some issues with the recent import/export changes 2017-09-04 23:20:21 +01:00
Luck 479bd91b74 Fix build 2017-09-04 22:32:02 +01:00
Luck 6bc7f1fca2 Refactor Importer/Exporter to use multiple threads (#317) 2017-09-04 22:24:07 +01:00
Luck dadd8e2c50 Don't publish/broadcast log entries from the import process - towards #317 2017-09-04 20:47:45 +01:00
Luck 03f342a21c Refactor command execution to use Locks per target instead of (effectively) one for all commands - towards #317 2017-09-04 20:42:21 +01:00
Luck 7fe5397e21 Fix incorrect usage of CompletableFuture#thenCombineAsync 2017-09-04 17:56:01 +01:00
Luck b6e1fd179e Add type argument to 'meta clear' command (#457) 2017-09-04 17:25:59 +01:00
Luck 754c7d8e3b Add config option to prevent primary group removal (#405) 2017-09-04 17:03:17 +01:00
Luck 925d3ccd1b Remove "list" alias for holder info (#461) 2017-09-04 16:55:10 +01:00
Luck d60d0ac9c5 Reduce buffer times, refactor MessagingService init 2017-09-04 16:49:43 +01:00
Luck e1b51dd6af Catch Throwable instead of Exception when setting up BukkitJsonMessageHandler 2017-09-04 13:03:09 +01:00
Luck fbe9d9f128 Run user cleanup task async and not on Storage init 2017-09-03 16:16:21 +01:00
Luck 509826949d Add login debug option 2017-08-27 13:54:26 +01:00
Luck 39aaa8a292 Update for latest Text version 2017-08-22 22:51:49 +01:00
Luck 069c76c53a Fix time test for node expiry times 2017-08-22 16:30:46 +01:00
Luck f45c0caa45 Refactor metastacks & primary group calculation 2017-08-21 15:45:29 +02:00
EuropeJing 41b0e10709 Update zh-CN locale (#450) 2017-08-21 10:38:30 +02:00
EuropeJing 8e8da20c13 Update zh-CN locale (#449) 2017-08-20 19:43:58 +02:00
Luck 269aa39378 Fix comparator and ensure @Nonnull annotated methods never return null in LogEntry 2017-08-20 14:51:46 +02:00
Luck ae8be97db7 Dispatch log entries via the messaging service 2017-08-20 13:32:52 +02:00
Luck f0ad40825b Refactor and cleanup Log classes 2017-08-20 01:35:03 +02:00
Luck 36d292747a Format times in /lp log into a shorter form 2017-08-19 11:38:08 +02:00
Luck b65d2262bf Remove "... data was saved to storage" messages 2017-08-19 11:08:28 +02:00
Luck a7ab0c6b36 Send a more helpful message when the non legacy version of LP is installed on Bukkit 1.7 (#437) 2017-08-18 13:58:41 +02:00
Luck c02fcb6508 Refactor weight lookups 2017-08-18 11:14:03 +02:00
Luck 07c38de44b Allow permission info command to be filtered by context (#442) 2017-08-18 10:44:54 +02:00
Luck 188d62c99b Fix issue with casting UUIDs in MongoDB backing (#443) 2017-08-18 10:35:56 +02:00
Luck 76b203ad10 Fix text dependencies being shaded 2017-08-17 10:12:08 +02:00
Luck 909844aef5 Update KyoriPowered/text dependency 2017-08-16 10:56:35 +02:00
Luck 046202719a Correctly export meta/prefix/suffix nodes to command strings 2017-08-14 18:58:12 +02:00
Luck ed15e2838d Fix checkinherits command not returning the inheritance data 2017-08-14 09:38:08 +02:00
Luck 82466c2e5d Properly implement Contexts#allowAll - bump API to 3.3 2017-08-13 22:21:04 +02:00
Luck c598daf350 Fix negating group permissions to cancel inheritance 2017-08-13 19:40:59 +02:00
Luck a40421fa1f Optimizations to the core PermissionHolder class
- Remove usage of Java 8 streams from frequently called methods
- Use LinkedLists where appropriate to improve Iterator#remove speed
- Cache immutable multimap conversion
- Rename some methods to make the intended use/behaviour clearer
2017-08-13 19:24:00 +02:00
Luck d98b464ce9 Cleanup verbose & treeview packages. Return known permissions from the PermissionVault as Sponge PermissionDescriptions 2017-08-13 12:07:05 +02:00
Luck 1d5e3205ac Refactoring & misc cleanup 2017-08-12 19:34:14 +02:00
Luck 07eb17f5d5 Fix Sponge PermissionDescription lookups (#425) 2017-08-08 10:37:54 +02:00
Luck 3951725852 Fix 'meta settemp' command using duration as the server context (#421) 2017-08-05 23:46:19 +02:00
Luck 77956d69ad Fix NPE with Vault #getPlayerGroups 2017-08-05 17:30:40 +02:00
Luck 5784458d03 Make Sponge's #getFriendlyIdentifier return usernames of Users and display names of Groups (#412) 2017-08-03 18:39:39 +01:00
Luck 3d2115ff3c Remove unnecessary extra cache in ContextManager 2017-08-02 21:48:22 +01:00
Luck 8618a67e2e Fix sponge config loading - closes #407 2017-07-31 22:26:59 +01:00
Luck 234b9cafd7 Give default group if needed after "parent remove" (#405) 2017-07-30 21:08:05 +01:00
Luck 4ecd899bac Fix IndexOutOfBoundsException with 'permission settemp' command (#403) 2017-07-30 15:23:42 +01:00
Luck ce2c943397 Inject bukkit permissible at lowest priority instead of low 2017-07-27 12:58:31 +01:00
Luck 51898abb71 Don't load contexts file twice 2017-07-26 10:31:14 +01:00
Luck e2f800dd63 Throw an NPE if a null delegate sender is passed to SenderFactory#wrap 2017-07-26 10:10:25 +01:00
Luck d9e5bc9345 fix error with null subject in ContextManager removal listener - closes #392 2017-07-24 19:42:35 +01:00
Luck e42cc101cc Cleanup & document a number of Bukkit impl classes 2017-07-24 14:32:10 +01:00
Luck 845367e847 Flush storage buffer more frequently 2017-07-22 20:42:53 +01:00
Luck 90afd3c3ed Cleanup handling of empty permissions when migrating data (closes #387) 2017-07-22 20:04:55 +01:00
Luck b4aeb04d1b Fix possible NPEs when calling API storage events - fixes #384 2017-07-22 00:06:37 +01:00
Luck 65b0d45738 Fix IllegalStateException in meta info command - closes #385 2017-07-22 00:00:58 +01:00
Luck 3c44388883 Update /lp info output 2017-07-19 22:15:12 +01:00
Luck 400f3a9156 Add option to disable colored logging (#382) 2017-07-18 17:58:40 +01:00
creator3 ad6e837aa5 Use single quotes in escapeStringForSql (#379) 2017-07-16 23:53:15 +02:00
Luck cadd7545c5 Use saner class naming 2017-07-16 21:16:08 +01:00
Luck b41dea9ee1 Implement support for futures & other changes in the Sponge Permissions API 2017-07-16 14:12:36 +01:00
Luck 7a6c0ab154 Improve /lp listgroups output - closes #368 2017-07-10 10:09:29 +01:00
Luck 1bb1f5ebb3 Fail silently when the Sponge Server is not available - closes #367 2017-07-10 09:18:58 +01:00
Luck fce5b8430d Use GA MySQL version 2017-07-09 18:05:08 +01:00
Luck 0befa6a491 Fix generating unset commands for temporary nodes 2017-07-07 19:39:31 +01:00
Luck b12ce943f7 Make 'meta info' and 'parent info' clickable (#311) 2017-07-07 19:28:26 +01:00
Luck d23b29aeb1 Update dependency versions 2017-07-07 14:48:42 +01:00
Luck 42d48c8da2 Implement argument based permissions for LP commands - closes #52, #170, #174 2017-07-07 12:43:13 +01:00
Luck 9e83a5e4d9 Add RedisBungee messaging service option for BungeeCord 2017-07-03 16:24:00 +01:00
Luck d2bf940105 Add option to use the servers uuid cache/lookup facility (#354) 2017-07-03 16:07:33 +01:00
Luck 7d4aca68cc Fix jsr being shaded & tweak javadoc generation to link to guava docs correctly 2017-07-02 22:04:37 +01:00
Luck d7094909c0 Change verbose output slightly 2017-07-02 21:29:48 +01:00
Luck 7083d55a9b Fix vault lookups with a specified world 2017-07-02 10:49:07 +01:00
Luck 770db2fee0 Export groups in order of weight, then alphabetically 2017-06-30 19:43:45 +01:00
Luck b8e65c4f26 Fix bukkit defaults still being applied to subscriptions when disabled 2017-06-30 19:32:12 +01:00
Luck 5cd5186092 Don't cancel re-allowed connections unless the cancel was made by LP 2017-06-26 21:39:59 +01:00
Luck fa0b66c2dc Fix lp tree max recursion property 2017-06-26 16:31:30 +01:00
Luck 1b199fd2c7 Fix NPE when loading locale file - closes #338 2017-06-24 17:26:52 +01:00
Luck 2ffbeeef8e Add jsr305 annotations to the API 2017-06-24 17:25:13 +01:00
Chris N 26ce8b8ab7 YAML/JSON batch permission defining (#337) 2017-06-22 10:41:29 +01:00
Luck 8e224c4a27 Don't force cancel Sponge ClientConnection.Join if the cancellation state changes, shuffle event orders slightly - fixes #335 2017-06-20 21:37:38 +01:00
Luck cf5318b92b Make command defintion/usage messages translatable 2017-06-20 20:20:41 +01:00
Luck e50fa17f7c Store all accumulated meta values in MetaCache 2017-06-19 21:48:52 +01:00
Luck 8766e3b408 Add jar signing to build scripts 2017-06-19 20:09:18 +01:00
Luck c4c7f06d83 Fix BungeePerms migration - closes #333 2017-06-19 16:43:00 +01:00
Luck 413e304845 Fix NPE with null context in FileWatcher 2017-06-14 17:32:02 +01:00
Luck 046daf23d0 Deploy source & javadoc jar with the API 2017-06-13 20:17:41 +01:00
Luck c57947949c Don't send messages to command blocks async 2017-06-13 17:30:01 +01:00
Luck 0acefde805 Tidy up configuration file layouts 2017-06-12 21:41:23 +01:00
Luck a9e751b4b8 Replace fanciful with KyoriPowered/text 2017-06-11 14:46:56 +01:00
Luck 3df1ae8ded Fix argument validation for parent settrack command - closes #321 2017-06-11 10:27:26 +01:00
Luck b4a2da0ea8 Add some missing tab completions, remove /lp log export command 2017-06-09 22:19:07 +01:00
Luck 5c5d1e8d0a Fixes some issues with username storage and handling
- Fix the yaml/json UUID cache corrupting username data when performing uuid --> username checks
- Fix handling of usernames when users are loaded

Closes #301
2017-06-09 18:20:12 +01:00
Luck ff0c988a1d Don't store attributes for permissions with only 'value: true' in YAML/JSON storage files 2017-06-07 21:04:14 +01:00
Luck b9fc8f4b5f Add MongoDB collection prefix option 2017-06-05 16:09:02 +01:00
Luck 2ecaa39cb6 Only resolve inheritances for groups which exist already 2017-06-04 16:41:20 +01:00
Luck 7b8654339c Maybe fix usernames not being updated in storage after name changes (#301) 2017-06-03 16:40:16 +01:00
dzegarek 306e252c31 Add pl_PL locale (#306) 2017-06-03 14:47:34 +01:00
Luck 87c3bd6293 Update README.md 2017-05-26 20:48:41 +01:00
Luck 307beebb58 Cleanup Bukkit PEX migration, fix user parents being transferred incorrectly 2017-05-25 22:24:55 +01:00
Luck 4109863aa9 Fix -s flag (#132) 2017-05-25 21:35:47 +01:00
Luck 0febdbe3a4 Update copyright headers in the API module 2017-05-21 09:47:08 +01:00
Luck 139dd5302b Cleanup login handling & reduce the amount of unnecessary logging output on startup 2017-05-18 22:30:01 +01:00
Luck cc907b6530 Don't catch exceptions thrown inside i/o (#291) 2017-05-18 21:16:31 +01:00
Luck ca1dacf2b6 Invalidate active context cache when a player changes world - closes #288 2017-05-16 21:52:46 +01:00
Luck abc1c164cf Expose MetaStacks in the API, general cleanup, release 3.2 2017-05-16 21:41:20 +01:00
Luck dc801464ef Refactor meta stacking to be mapped in MetaCache - towards lucko/LuckPermsPlaceholders#1 2017-05-16 19:13:08 +01:00
Luck 99c6fe20c2 Catch exceptions thrown by context calculators 2017-05-16 13:50:28 +01:00
Luck 9d780ae24a Fix misleading Vault debug message 2017-05-15 21:26:43 +01:00
Luck a44dec0402 Fix typo 2017-05-15 12:07:59 +01:00
Luck a081152baa Fix web editor url 2017-05-15 12:05:54 +01:00
Luck 9b50d48f46 fix compile 2017-05-14 22:41:25 +01:00
Luck 5121fc6b1f Cleanup 2017-05-14 22:15:25 +01:00
Luck d4ac261e85 Cleanup config keys, make editor URL configurable 2017-05-14 21:36:02 +01:00
Luck 2e9561371d Implement initial web editor support 2017-05-13 20:36:05 +01:00
Luck 942fd3c4ce Fix vault lookups with static context 2017-05-11 20:04:11 +01:00
Luck 8cf32752e9 Fix meta info only showing values in global context 2017-05-11 19:46:28 +01:00
Luck b240ca9939 Add parent cleartrack command, cleanup Messages enum 2017-05-09 16:26:55 +01:00
Luck e25d99129c Implement parent settrack command - closes #112 2017-05-09 14:06:16 +01:00
Luck 1fee47e087 Implement default contexts - closes #241 2017-05-09 13:36:43 +01:00
Luck e835b31277 Apply world rewrites on-top of the real world name, and apply recursively (#255) 2017-05-09 13:07:39 +01:00
Luck 6817945e12 Bump sponge API version, cleanup 2017-05-09 12:55:45 +01:00
Luck 030fb1262e Fix being able to negate permissions/parents/meta in specific contexts 2017-05-08 22:27:46 +01:00
Luck fae49096f3 Fix retrieving meta keys from Vault which contain delimited characters - closes #270 2017-05-08 21:43:20 +01:00
EuropeJing afd491ecea Update zh-CN locale (#281) 2017-05-08 20:17:51 +01:00
EuropeJing 09bc100ad9 Add zh-CN locale (#279) 2017-05-08 18:11:29 +01:00
Luck 037f18ac56 Fix zPerms parent group migration for users, and fix migration of temporary memberships 2017-05-08 17:45:17 +01:00
Luck a5746dfa21 Support sponge selector resolution - closes #275 2017-05-07 22:52:45 +01:00
Luck cedc5108fc Refactor Patterns to PatternCache 2017-05-07 13:56:42 +01:00
Luck 65af3d0f54 Add data constraints - closes #256 2017-05-07 13:44:29 +01:00
Luck 6c4fa65f91 Fix migrating weight with zPerms 2017-05-06 23:35:58 +01:00
Luck 4731ad8061 Fix migrating false zPerms nodes 2017-05-06 21:06:22 +01:00
Luck 7dc41e15d3 Fix users being cleaned up too early and then logging back into the server - closes #271 2017-05-06 18:24:44 +01:00
Luck 8326539d40 Refactor LP PermissionService to implement a modified API & add proxied classes 2017-05-03 20:14:31 +01:00
Luck 163e6e0f2a Always load user data caches alongside user instances 2017-05-02 16:55:08 +01:00
Luck f56bb251e9 Cleanup some stuff 2017-05-02 16:36:46 +01:00
Luck 152ca99276 Fix issue with bulk removing suffixes 2017-04-25 16:51:21 +01:00
Luck 7c398d2c67 Split up chat messages sent to console containing new lines 2017-04-23 20:51:24 +01:00
Luck a526c942ca Add config options to allow for finer control over how permissions are calculated, and set include-global to true on BungeeCord by default 2017-04-23 20:30:58 +01:00
Luck af8fd15929 improve speed of NodeTools#removeSamePermission 2017-04-23 11:51:56 +01:00
Luck 7bfd5d4bc8 implement atomic group insertion 2017-04-22 22:53:29 +01:00
Luck 4382d5c56d show uuid in log messages if username is null 2017-04-21 21:28:50 +01:00
Luck f43b9c96de cleanup login handling & add CountdownLatch to ensure the plugin has started before logins are handled 2017-04-21 19:10:25 +01:00
Luck c64f72e394 Fix issue with loading usernames on MongoDB - closes #257 2017-04-21 16:58:07 +01:00
Luck 3d54f832c5 cleanup duplicated method 2017-04-20 19:26:47 +01:00
Luck 867ba24c6c support glowstone (maybe) 2017-04-20 19:15:37 +01:00
Luck 9715c5394c Fix NPE caused by AllParentsByWeight primary group selector - closes #252 2017-04-18 17:11:07 +01:00
Luck 67b5c72520 Cleanup handling of unknown/null usernames for users 2017-04-17 19:31:52 +01:00
Articuno 43450f6cc7 Update RU locale (#250) 2017-04-17 10:12:37 +01:00
Luck a3737d4f35 fix typo 2017-04-16 11:08:54 +01:00
Luck 3314e94a9a Add not_on_track meta stack criteria 2017-04-16 11:06:05 +01:00
Luck a27436b086 Fix getting primary groups for offline users 2017-04-15 20:32:03 +01:00
Luck fe5554ffb1 fix build 2017-04-15 19:53:12 +01:00
Luck f305f8d822 Determine dynamic primary groups using current context 2017-04-15 19:08:04 +01:00
Luck 1f54bc41cc Cleanup poms, update copyright header 2017-04-15 19:01:52 +01:00
Luck c82d028429 fix yaml/json schema migration 2017-04-12 21:33:59 +01:00
Luck be2a8b7b07 Fix incorrect chat meta command usage 2017-04-12 17:59:51 +01:00
Luck 261fc82016 Remove getuuid command
why did that ever exist anyways
2017-04-12 17:25:22 +01:00
Luck 37b940f3a5 Convert promote / demote commands to use new context system, update en locale with latest changes 2017-04-12 17:11:01 +01:00
Luck 0504e904a2 Only perform flatfile schema migration if the new dir doesn't exist 2017-04-12 14:28:39 +01:00
Luck 71bfdd9bfd log the file name when exceptions are thrown during i/o 2017-04-12 14:09:51 +01:00
Articuno c7305f6b06 Update ru_RU locale (#238) 2017-04-11 10:47:43 +01:00
Luck 4fb07ff181 API changes for 3.1 2017-04-10 22:26:20 +01:00
Luck 4631dcf857 Ensure expired permissions are removed when users/groups are loaded for commands 2017-04-10 20:57:59 +01:00
Luck f6f9840eb7 Re-implement bulk updates 2017-04-10 19:33:23 +01:00
Luck 4de8165c95 fix issue with /reloading and permission registration 2017-04-10 18:17:09 +01:00
Luck 242672f49d Remove old bulk edit commands 2017-04-10 14:56:46 +01:00
creator3 c32c975a71 Fix accumulation of temporary perms with a world or server context (#235) 2017-04-08 20:49:10 +01:00
Luck 82e759d708 Make sure world names are lowercase'd 2017-04-08 17:00:36 +01:00
Luck 9de44d2605 Don't attempt to migrate empty permissions 2017-04-07 21:03:27 +01:00
Luck 132d0cf578 Fix export command switchprimarygroup output, and log player uuid if their username isn't included 2017-04-07 19:59:53 +01:00
Luck 029dc9f8d9 Make the standard MySQL driver the default again 2017-04-06 11:30:17 +01:00
Luck 5567b1dad8 Use the mariadb driver by default 2017-04-05 21:23:18 +01:00
Luck ea00ec64af Refactor NodeModel into a more useful class, remove duplicate context serialization methods 2017-04-04 19:19:31 +01:00
Luck 7259e6be0a Fixes for the verbose uploader 2017-04-04 17:17:57 +01:00
Luck 2e40557b39 Add '/lp group <group> listmembers' command - closes #203 2017-04-04 16:07:41 +01:00
Luck e68fc7c558 Improve performance of resolve methods in PermissionHolder, other cleanup 2017-04-04 15:22:25 +01:00
Luck 055dfb000d Only allow one server and one world in context sets parsed from command args 2017-04-04 13:47:50 +01:00
Luck b997ab6e3f Properly sort storage files - closes #227 2017-04-03 10:57:29 +01:00
Luck 25f31d0bb8 Cleanup sponge delegate classes 2017-04-03 10:27:31 +01:00
Luck 4cfd8a7e2c Fix broken command parameter & update default locale file 2017-04-03 09:53:26 +01:00
Luck 0ec19a8fee Implement accumulation of static contexts from a file 2017-04-03 01:42:49 +01:00
Luck 2749563f5d Replace guava caches with caffeine 2017-04-03 00:53:04 +01:00
Luck e7e2e3f7e0 Allow context pairs to be specified in commands, fix misuse of exceptions in the permission holder class 2017-04-02 18:44:05 +01:00
Luck 0a33c0aac5 Fix compatibility with older gson versions - closes #222 2017-04-01 07:26:05 +01:00
Articuno 24e09fd2d9 Add ru_RU.yml (#224) 2017-03-30 18:45:50 +01:00
Luck 4d0499a61e Avoid parsing UUID in sponge world calculator 2017-03-30 18:40:36 +01:00
Luck ae82807139 Add "use-vault-server" config option, depreciate vault primary group override feature 2017-03-27 17:35:28 +01:00
Luck 65147e1935 Remove missing permission nodes before adding new ones when saving users/groups with SQL storage 2017-03-27 16:54:59 +01:00
Luck 8e557d122b Fix some concurrency issues with login handling 2017-03-26 18:40:09 +01:00
Luck 486b19aa90 Cleanup some misc stuff 2017-03-26 16:10:49 +01:00
Luck 14cb34ac95 Force some config options to be read as lower case 2017-03-25 23:23:20 +00:00
Luck abfc23b04d Provide tab completion for rewritten arguments & add some more rules 2017-03-25 20:27:33 +00:00
Luck ebb24aefa1 Fix parent set command also adding default 2017-03-25 20:06:30 +00:00
Luck 2573f46f40 Fix removing temporary permissions/parents/meta in global context 2017-03-25 19:58:56 +00:00
Luck 5dcb8cd73a Output group/track lists in alphabetical order 2017-03-25 19:48:17 +00:00
Luck e4bfe09882 Don't bother loading test classes (#217) 2017-03-25 11:38:02 +00:00
Luck eecbfe04db Add es_ES locale 2017-03-25 11:13:45 +00:00
Luck 671e2adc32 Cleanup remaining (un)setPermission methods 2017-03-25 11:04:25 +00:00
Luck 1137e476dd Rewrite inheritance resolution implementation 2017-03-24 22:18:18 +00:00
Luck cb63b321d0 correct zPermissions migration logging tag 2017-03-23 23:18:03 +00:00
Luck c36b0d2975 Automatically push updates to other servers via the messaging service when commands are ran 2017-03-21 21:58:45 +00:00
Luck f8ad562b95 Rename "debug" classes to "verbose" 2017-03-20 17:22:18 +00:00
Luck c35ab38f36 Slight speed improvement for import processes 2017-03-19 20:52:35 +00:00
Luck 073b775566 Implement nicer json format for Sponge local data 2017-03-19 17:54:12 +00:00
Luck 85c7a7db8d Implement new file layout for YAML / JSON storage files (#211) 2017-03-19 15:37:30 +00:00
Luck 1e134df27d Clarify the file names of H2 and SQLite databases 2017-03-18 23:00:12 +00:00
Luck bdff84885a Enforce that ContextSets use the HashMultimap variant - closes #208 2017-03-18 20:18:18 +00:00
Luck f1a9c3722c Fix PM migration command not being registered 2017-03-18 17:26:44 +00:00
Luck 8b402f6423 Fix NoSuchMethodError being thrown for older Bukkit config classes 2017-03-18 15:34:29 +00:00
Luck a0aec52661 Correct the case of group names when retrieved from Node instances 2017-03-18 14:43:34 +00:00
Luck 407ab43dd1 Update PM migration to support latest version 2017-03-17 23:21:57 +00:00
Luck c2165e0196 Fix primary groups being selected in reverse order 2017-03-17 23:00:37 +00:00
Luck 2e75bc3f3b Add some alternate options for primary group calculation 2017-03-17 22:54:18 +00:00
Luck e91c2ca427 Add method to export Message enum and update language file 2017-03-17 21:17:59 +00:00
Luck 965ff04bcc Fix ClassCastException with MongoDB user search 2017-03-17 20:54:40 +00:00
Luck ac4bd418f3 Rename online-mode config option to use-server-uuids to better reflect/clarify its purpose 2017-03-17 20:51:06 +00:00
Luck e9e844c1f7 Include internal weight value in meta output if not already present 2017-03-13 20:17:54 +00:00
Luck aa2941fcdb Refactor and cleanup commands 2017-03-13 20:03:03 +00:00
Luck dbc909a317 Cleanup migration commands 2017-03-13 19:10:36 +00:00
Luck 11465a4ce9 Fix BungeePerms migration to account for group weights and negated permissions 2017-03-12 20:00:02 +00:00
Luck f5007f94dc Add server version info to the startup banner 2017-03-12 14:59:27 +00:00
Luck d18ac2e6eb Cleanup pom files, maybe improve build speed a bit 2017-03-12 14:28:35 +00:00
Luck b9fc5c39ae Listen for changes in data files and automatically update 2017-03-11 23:05:03 +00:00
Luck 9dc2278083 Cleanup a bit 2017-03-11 21:10:27 +00:00
Luck 281060a972 Make the clone commands work for existing tracks/groups too 2017-03-11 18:30:27 +00:00
Luck 91626f079d Fix mistake in removeprefix/removesuffix command descriptions 2017-03-11 18:26:29 +00:00
Luck 7305c6c54b Migrate group weightings onto the group itself as well as to the chat meta held by it 2017-03-11 18:24:25 +00:00
Luck bce7fa871d Fix getAllNodes cache not working properly, remove pre-process contexts on Bungee & cleanup some unused methods 2017-03-11 17:55:50 +00:00
Luck a35fd80763 remove placeholder expansion from main project 2017-03-11 14:02:17 +00:00
Luck b383f9dfbc Add some more placeholders to the expansion 2017-03-11 13:30:08 +00:00
Luck 680ad3c145 Update hikari version 2017-03-11 10:32:10 +00:00
Luck 7b712e7f90 Update readme 2017-03-10 23:30:08 +00:00
Luck 2462eac497 Run exporter in own thread and process user exports concurrently 2017-03-10 20:44:19 +00:00
Luck bb818c8942 Cleanup powerfulperms migration & remove support for older PP versions 2017-03-09 19:10:48 +00:00
Luck ddc881af11 Add new argument util method for reading context pairs 2017-03-09 17:56:15 +00:00
Luck 838fba9173 Switch all usages of file reader/writers to use java nio methods - closes #204 2017-03-09 17:55:33 +00:00
Luck 0b6f326c18 Fallback to uuid in the search command if the username is unknown 2017-03-07 22:17:49 +00:00
Luck 00895fc7ab Don't send json text on MC 1.7 2017-03-06 18:22:11 +00:00
Luck 07f38aa283 Fix possible issue with context conversion 2017-03-05 22:56:00 +00:00
Luck ecf1d3790c Fix issue where group nodes could be unset using the permission commands, and where users with per-server groups wouldn't be assigned to the default group globally 2017-03-05 19:05:05 +00:00
Luck 53c9e5bb4a Fix race condition with Bukkit join handling where players are denied entry but subsequently login slightly later 2017-03-05 00:59:44 +00:00
Luck b20b03113a Fix groupmanager global group migration on windows systems 2017-03-01 21:26:20 +00:00
Luck ec8273aa9f Change bungeecord leave event priority to wait longer before unloading data 2017-02-26 21:53:03 +00:00
Luck 2862f0dfae Fix creating delimiter patterns 2017-02-26 21:28:40 +00:00
Luck fd4963c74b Fix getUuid and getName API methods returning null for User, Group and Track instances 2017-02-26 20:46:37 +00:00
Luck df99ef4481 misc cleanup 2017-02-26 12:43:35 +00:00
Luck 35d1057673 Refactor sponge commands to use the proxied interfaces (closes #197) 2017-02-26 11:55:59 +00:00
Luck e6a90a18c4 Sort subjects properly 2017-02-26 01:16:54 +00:00
Luck 26afb69a14 Cleanup LuckPermsSubjectData (fixes #194) 2017-02-26 01:00:02 +00:00
Luck e8c0c43e9d misc cleanup 2017-02-24 19:31:45 +00:00
Luck 1707f333bf update javadocs link in readme 2017-02-23 21:59:43 +00:00
652 changed files with 42753 additions and 25885 deletions

No files matched your search

+728
View File
@@ -0,0 +1,728 @@
# LuckPerms Language File
# Locale: en_US (English)
# Author: Luck
prefix: "&7&l[&b&lL&3&lP&7&l] &c"
empty: "{0}"
player-online: "&aOnline"
player-offline: "&cOffline"
loading-error: "&cPermissions data could not be loaded. Please try again later."
op-disabled: "&bThe vanilla OP system is disabled on this server."
op-disabled-sponge: "&2Please note that Server Operator status has no effect on Sponge permission checks when a permission plugin is installed. Please edit user data directly."
log-info: "&7&l[&bL&3P&7&l] &3{0}"
log-warn: "&7&l[&bLuck&3Perms&7&l] &c[WARN] {0}"
log-error: "&7&l[&bLuck&3Perms&7&l] &4[ERROR] {0}"
log: "&3LOG &3&l> {0}"
verbose-log: "&3VB &3&l> {0}"
export-log: "&3EXPORT &3&l> &f{0}"
export-log-progress: "&3EXPORT &3&l> &7{0}"
migration-log: "&3MIGRATION &7[&3{0}&7] &3&l> &f{1}"
migration-log-progress: "&3MIGRATION &7[&3{0}&7] &3&l> &7{1}"
command-not-recognised: "&cCommand not recognised."
command-no-permission: "&cYou do not have permission to use this command!"
user-not-found: "&cA user for &4{0}&c could not be found."
user-not-online: "&aUser &b{0}&a is not online."
user-save-error: "&cThere was an error whilst saving user data for &4{0}&c."
group-not-found: "&cA group named &4{0}&c could not be found."
group-save-error: "&cThere was an error whilst saving group data for &4{0}&c."
track-not-found: "&cA track named &4{0}&c could not be found."
track-save-error: "&cThere was an error whilst saving track data for &4{0}&c."
user-invalid-entry: "&4{0}&c is not a valid username/uuid."
group-invalid-entry: "&4{0}&c is not a valid group name."
track-invalid-entry: "&4{0}&c is not a valid track name."
server-world-invalid-entry: "&cServer/world names can only contain alphanumeric characters and cannot exceed 36 characters in length."
use-inherit-command: "&cUse the 'parent add' and 'parent remove' commands instead of specifying the node."
verbose-invalid-filter: "&4{0}&c is not a valid verbose filter."
verbose-on: "&bVerbose logging &aenabled &bfor checks matching &aANY&b."
verbose-on-query: "&bVerbose logging &aenabled &bfor checks matching &a{0}&b."
verbose-off: "&bVerbose logging &cdisabled&b."
verbose-recording-on: "&bVerbose recording &aenabled &bfor checks matching &aANY&b."
verbose-recording-on-query: "&bVerbose recording &aenabled &bfor checks matching &a{0}&b."
verbose-upload-start: "&bVerbose logging &cdisabled&b. Uploading results..."
verbose-results-url: "&aVerbose results URL:"
tree-upload-start: "&bGenerating permission tree..."
tree-empty: "&cUnable to generate tree. No results were found."
tree-url: "&aPermission tree URL:"
search-searching: "&aSearching for users and groups with &b{0}&a..."
search-searching-members: "&aSearching for users and groups who inherit from &b{0}&a..."
search-result: "&aFound &b{0}&a entries from &b{1}&a users and &b{2}&a groups."
search-showing-users: "&bShowing user entries:"
search-showing-groups: "&bShowing group entries:"
search-showing-users-with-page: "&bShowing user entries: {0}"
search-showing-groups-with-page: "&bShowing group entries: {0}"
apply-edits-invalid-code: "&cInvalid code. &7({0})"
apply-edits-unable-to-read: "&cUnable to read data using the given code. &7({0})"
apply-edits-no-target: "&cUnable to parse the target of the edit. Please supply it as an extra argument."
apply-edits-target-group-not-exists: "&cTarget group &4{0}&c does not exist."
apply-edits-target-user-not-uuid: "&cTarget user &4{0}&c is not a valid uuid."
apply-edits-target-user-unable-to-load: "&cUnable to load target user &4{0}&c."
apply-edits-target-unknown: "&cInvalid target. &7({0})"
apply-edits-success: "&aSuccessfully applied &b{0}&a nodes to &b{1}&a."
editor-upload-failure: "&cUnable to upload permission data to the editor."
editor-url: "&aEditor URL:"
check-result: "&aPermission check result on user &b{0}&a for permission &b{1}&a: &f{2}"
create-success: "&b{0}&a was successfully created."
delete-success: "&b{0}&a was successfully deleted."
rename-success: "&b{0}&a was successfully renamed to &b{1}&a."
clone-success: "&b{0}&a was successfully cloned onto &b{1}&a."
already-inherits: "&b{0}&a already inherits from &b{1}&a in context {2}&a."
does-not-inherit: "&b{0}&a does not inherit from &b{1}&a in context {2}&a."
already-temp-inherits: "&b{0}&a already temporarily inherits from &b{1}&a in context {2}&a."
does-not-temp-inherit: "&b{0}&a does not temporarily inherit from &b{1}&a in context {2}&a."
track-already-contains: "&b{0}&a already contains &b{1}&a."
track-does-not-contain: "&b{0}&a doesn't contain &b{1}&a."
track-ambiguous-call: "&4{0}&c is a member of multiple groups on this track. Unable to determine their location."
already-exists: "&4{0}&c already exists!"
does-not-exist: "&4{0}&c does not exist!"
group-load-error: "&cAn unexpected error occurred. Group not loaded."
groups-load-error: "&cAn unexpected error occurred. Unable to load all groups."
track-load-error: "&cAn unexpected error occurred. Track not loaded."
tracks-load-error: "&cAn unexpected error occurred. Unable to load all tracks."
track-empty: "&4{0}&c cannot be used as it is empty or contains only one group."
update-task-request: "&bAn update task has been requested. Please wait..."
update-task-complete: "&aUpdate task complete."
update-task-complete-network: "&aUpdate task complete. Now attempting to push to other servers."
update-task-push-success: "&aOther servers were notified via &b{0} Messaging &asuccessfully."
update-task-push-failure: "&cError whilst pushing changes to other servers."
update-task-push-failure-not-setup: "&cError whilst pushing changes to other servers. &7(a messaging service has not been configured)"
reload-config-success: "&aThe configuration file was reloaded. &7(some options will only apply after the server has restarted)"
info-top: >
{PREFIX}&2Running &bLuckPerms v{0}&2 by &bLuck&2.\n
{PREFIX}&f- &3Platform: &f{1}\n
{PREFIX}&f- &3Server Brand: &f{2}\n
{PREFIX}&f- &3Server Version: &f{3}
info-middle: >
{PREFIX}&f- &bMessaging Type: &f{0}\n
{PREFIX}&f- &bInstance:\n
{PREFIX}&f- &3Server Name: &f{1}\n
{PREFIX}&f- &3Online Players: &a{2}\n
{PREFIX}&f- &3Unique Connections: &a{3}\n
{PREFIX}&f- &3Uptime: &7{4}\n
{PREFIX}&f- &3Local Data: &a{5} &7users, &a{6} &7groups, &a{7} &7tracks\n
{PREFIX}&f- &3Context Calculators: &a{8}\n
{PREFIX}&f- &3Known permissions: &a{9}\n
{PREFIX}&f- &3Active processors: &7{10}
create-error: "&cThere was an error whilst creating &4{0}&c."
delete-error: "&cThere was an error whilst deleting &4{0}&c."
delete-group-error-default: "&cYou cannot delete the default group."
groups-list: "&aGroups: &7(name, weight, tracks)"
groups-list-entry: "&f- &3{0} &7- &b{1}"
groups-list-entry-with-tracks: "&f- &3{0} &7- &b{1} &7- [&3{2}&7]"
tracks-list: "&aTracks: {0}"
listnodes: "&b{0}'s Permissions:"
listnodes-with-page: "&b{0}'s Permissions: {1}"
listnodes-temp: "&b{0}'s Temporary Permissions:"
listnodes-temp-with-page: "&b{0}'s Temporary Permissions: {1}"
listparents: "&b{0}'s Parent Groups:"
listparents-temp: "&b{0}'s Temporary Parent Groups:"
list-tracks: >
&b{0}'s Tracks:\n
{1}
list-tracks-empty: "&b{0}&a is not on any tracks."
context-pair-inline: "&3{0}=&b{1}"
context-pair--global-inline: "&eglobal"
context-pair-sep: "&a, "
context-pair: "&8(&7{0}=&f{1}&8)"
check-permission: "&b{0}&a has permission &b{1}&a set to {2}&a in context {3}&a."
check-inherits-permission: "&b{0}&a has permission &b{1}&a set to {2}&a in context {3}&a. &7(inherited from &a{4}&7)"
setpermission-success: "&aSet &b{0}&a to &b{1}&a for &b{2}&a in context {3}&a."
already-haspermission: "&b{0}&a already has &b{1}&a set in context {2}&a."
setpermission-temp-success: "&aSet &b{0}&a to &b{1}&a for &b{2}&a for a duration of &b{3}&a in context {4}&a."
already-has-temp-permission: "&b{0}&a already has &b{1}&a set temporarily in context {2}&a."
unsetpermission-success: "&aUnset &b{0}&a for &b{1}&a in context {2}&a."
does-not-have-permission: "&b{0}&a does not have &b{1}&a set in context {2}&a."
unset-temp-permission-success: "&aUnset temporary permission &b{0}&a for &b{1}&a in context {2}&a."
does-not-have-temp-permission: "&b{0}&a does not have &b{1}&a set temporarily in context {2}&a."
set-inherit-success: "&b{0}&a now inherits permissions from &b{1}&a in context {2}&a."
set-temp-inherit-success: "&b{0}&a now inherits permissions from &b{1}&a for a duration of &b{2}&a in context {3}&a."
set-parent-success: "&b{0}&a had their existing parent groups cleared, and now only inherits &b{1}&a in context {2}&a."
set-track-parent-success: "&b{0}&a had their existing parent groups on track &b{1}&a cleared, and now only inherits &b{2}&a in context {3}&a."
unset-inherit-success: "&b{0}&a no longer inherits permissions from &b{1}&a in context {2}&a."
unset-temp-inherit-success: "&b{0}&a no longer temporarily inherits permissions from &b{1}&a in context {2}&a."
clear-success: "&b{0}&a's permissions were cleared in context {1}&a. (&b{2}&a nodes were removed.)"
clear-success-singular: "&b{0}&a's permissions were cleared in context {1}&a. (&b{2}&a node was removed.)"
parent-clear-success: "&b{0}&a's parents were cleared in context {1}&a. (&b{2}&a nodes were removed.)"
parent-clear-success-singular: "&b{0}&a's parents were cleared in context {1}&a. (&b{2}&a node was removed.)"
parent-clear-track-success: "&b{0}&a's parents on track &b{1}&a were cleared in context {2}&a. (&b{3}&a nodes were removed.)"
parent-clear-track-success-singular: "&b{0}&a's parents on track &b{1}&a were cleared in context {2}&a. (&b{3}&a node was removed.)"
meta-clear-success: "&b{0}&a's meta matching type &b{1}&a was cleared in context {2}&a. (&b{3}&a nodes were removed.)"
meta-clear-success-singular: "&b{0}&a's meta matching type &b{1}&a was cleared in context {2}&a. (&b{3}&a node was removed.)"
illegal-date-error: "&cCould not parse date &4{0}&c."
past-date-error: "&cYou cannot set a date in the past!"
chat-meta-prefix-header: "&b{0}'s Prefixes"
chat-meta-suffix-header: "&b{0}'s Suffixes"
meta-header: "&b{0}'s Meta"
chat-meta-entry: "&b-> {0} &f- &f'{1}&f' &8(&7inherited from &a{2}&8)"
chat-meta-entry-with-context: "&b-> {0} &f- &f'{1}&f' &8(&7inherited from &a{2}&8){3}"
meta-entry: "&b-> &a{0} &f= &f'{1}&f' &8(&7inherited from &a{2}&8)"
meta-entry-with-context: "&b-> &a{0} &f= &f'{1}&f' &8(&7inherited from &a{2}&8){3}"
chat-meta-prefix-none: "&b{0} has no prefixes."
chat-meta-suffix-none: "&b{0} has no suffixes."
meta-none: "&b{0} has no meta."
meta-invalid-priority: "&cInvalid priority &4{0}&c. Expected a number."
already-has-chat-meta: "&b{0}&a already has {1} &f'{2}&f'&a set at a priority of &b{3}&a in context {4}&a."
already-has-temp-chat-meta: "&b{0}&a already has {1} &f'{2}&f'&a set temporarily at a priority of &b{3}&a in context {4}&a."
does-not-have-chat-meta: "&b{0}&a doesn't have {1} &f'{2}&f'&a set at a priority of &b{3}&a in context {4}&a."
does-not-have-temp-chat-meta: "&b{0}&a doesn't have {1} &f'{2}&f'&a set temporarily at a priority of &b{3}&a in context {4}&a."
add-chatmeta-success: "&b{0}&a had {1} &f'{2}&f'&a set at a priority of &b{3}&a in context {4}&a."
add-temp-chatmeta-success: "&b{0}&a had {1} &f'{2}&f'&a set at a priority of &b{3}&a for a duration of &b{4}&a in context {5}&a."
remove-chatmeta-success: "&b{0}&a had {1} &f'{2}&f'&a at priority &b{3}&a removed in context {4}&a."
bulk-remove-chatmeta-success: "&b{0}&a had all {1}es at priority &b{2}&a removed in context {3}&a."
remove-temp-chatmeta-success: "&b{0}&a had temporary {1} &f'{2}&f'&a at priority &b{3}&a removed in context {4}&a."
bulk-remove-temp-chatmeta-success: "&b{0}&a had all temporary {1}es at priority &b{2}&a removed in context {3}&a."
already-has-meta: "&b{0}&a already has meta key &f'{1}&f'&a set to &f'{2}&f'&a in context {3}&a."
already-has-temp-meta: "&b{0}&a already has meta key &f'{1}&f'&a temporarily set to &f'{2}&f'&a in context {3}&a."
doesnt-have-meta: "&b{0}&a doesn't have meta key &f'{1}&f'&a set in context {2}&a."
doesnt-have-temp-meta: "&b{0}&a doesn't have meta key &f'{1}&f'&a set temporarily in context {2}&a."
set-meta-success: "&aSet meta key &f'{0}&f'&a to &f'{1}&f'&a for &b{2}&a in context {3}&a."
set-meta-temp-success: "&aSet meta key &f'{0}&f'&a to &f'{1}&f'&a for &b{2}&a for a duration of &b{3}&a in context {4}&a."
unset-meta-success: "&aUnset meta key &f'{0}&f'&a for &b{1}&a in context {2}&a."
unset-meta-temp-success: "&aUnset temporary meta key &f'{0}&f'&a for &b{1}&a in context {2}&a."
bulk-update-invalid-data-type: "&cInvalid type. Was expecting 'all', 'users' or 'groups'."
bulk-update-invalid-constraint: "&cInvalid constraint &4{0}&c. Constraints should be in the format '&f<field> <comparison operator> <value>&c'."
bulk-update-invalid-comparison: "&cInvalid comparison operator '&4{0}&c'. Expected one of the following: &f== != ~~ ~!"
bulk-update-queued: "&aBulk update operation was queued. &7(&f{0}&7)"
bulk-update-confirm: "&aRun &b/{0} bulkupdate confirm {1} &ato execute the update."
bulk-update-unknown-id: "&aOperation with id &b{0}&a does not exist or has expired."
bulk-update-starting: "&aRunning bulk update."
bulk-update-success: "&bBulk update completed successfully."
bulk-update-failure: "&cBulk update failed. Check the console for errors."
user-info-general: >
{PREFIX}&b&l> &bUser Info: &f{0}\n
{PREFIX}&f- &3UUID: &f{1}\n
{PREFIX}&f- &3Status: {2}\n
{PREFIX}&f- &3Primary Group: &f{3}\n
{PREFIX}&f- &aCounts:\n
{PREFIX}&f- &3Nodes: &a{4}\n
{PREFIX}&f- &3Permissions: &a{5}\n
{PREFIX}&f- &3Prefixes: &a{6}\n
{PREFIX}&f- &3Suffixes: &a{7}\n
{PREFIX}&f- &3Meta: &a{8}
user-info-data: >
{PREFIX}&f- &aCached Data:\n
{PREFIX}&f- &3Has contextual data: {0}\n
{PREFIX}&f- &3Current Contexts: {1}\n
{PREFIX}&f- &3Current Prefix: {2}\n
{PREFIX}&f- &3Current Suffix: {3}
info-parent-header: "&f- &aParent Groups:"
info-temp-parent-header: "&f- &aTemporary Parent Groups:"
user-removegroup-error-primary: "&aYou cannot remove a user from their primary group."
user-primarygroup-success: "&b{0}&a's primary group was set to &b{1}&a."
user-primarygroup-warn-option: "&aWarning: The primary group calculation method being used by this server &7({0}) &amay not reflect this change."
user-primarygroup-error-alreadyhas: "&b{0}&a already has &b{1}&a set as their primary group."
user-primarygroup-error-notmember: "&b{0}&a was not already a member of &b{1}&a, adding them now."
user-track-error-not-contain-group: "&b{0}&a isn't already in any groups on &b{1}&a."
user-track-added-to-first: "&b{0}&a isn't in any groups on this track, so they were added to the first group, &b{1}&a in context {2}&a."
user-promote-success: "&aPromoting &b{0}&a along track &b{1}&a from &b{2}&a to &b{3}&a in context {4}&a."
user-promote-error-endoftrack: "&aThe end of track &b{0}&a was reached. Unable to promote &b{1}&a."
user-promote-error-malformed: >
{PREFIX}&aThe next group on the track, &b{0}&a, no longer exists. Unable to promote user.\n
{PREFIX}&aEither create the group, or remove it from the track and try again.
user-demote-success: "&aDemoting &b{0}&a along track &b{1}&a from &b{2}&a to &b{3}&a in context {4}&a."
user-demote-endoftrack: "&aThe end of track &b{0}&a was reached, so &b{1}&a was removed from &b{2}&a."
user-demote-error-malformed: >
{PREFIX}&aThe previous group on the track, &b{0}&a, no longer exists. Unable to demote user.\n
{PREFIX}&aEither create the group, or remove it from the track and try again.
group-info-general: >
{PREFIX}&b&l> &bGroup Info: &f{0}\n
{PREFIX}&f- &3Display Name: &f{1}\n
{PREFIX}&f- &3Weight: &f{2}\n
{PREFIX}&f- &aCounts:\n
{PREFIX}&f- &3Nodes: &a{3}\n
{PREFIX}&f- &3Permissions: &a{4}\n
{PREFIX}&f- &3Prefixes: &a{5}\n
{PREFIX}&f- &3Suffixes: &a{6}\n
{PREFIX}&f- &3Meta: &a{7}
group-set-weight: "&aSet weight to &b{0}&a for group &b{1}&a."
track-info: >
{PREFIX}&b&l> &bShowing Track: &f{0}\n
{PREFIX}&f- &7Path: &f{1}
track-clear: "&b{0}&a's groups track was cleared."
track-append-success: "&aGroup &b{0}&a was appended to track &b{1}&a."
track-insert-success: "&aGroup &b{0}&a was inserted into track &b{1}&a at position &b{2}&a."
track-insert-error-number: "&cExpected number but instead received: {0}"
track-insert-error-invalid-pos: "&cUnable to insert at position &4{0}&c. &7(invalid position)"
track-remove-success: "&aGroup &b{0}&a was removed from track &b{1}&a."
log-load-error: "&cThe log could not be loaded."
log-invalid-page: "&cInvalid page number."
log-invalid-page-range: "&cInvalid page number. Please enter a value between &41&c and &4{0}&c."
log-no-entries: "&bNo log entries to show."
log-entry: "&b#{0} -> &8(&7{1} ago&8) {2}"
log-notify-console: "&cCannot toggle notifications for console."
log-notify-toggle-on: "&aEnabled&b logging output."
log-notify-toggle-off: "&cDisabled&b logging output."
log-notify-already-on: "&cYou are already receiving notifications."
log-notify-already-off: "&cYou aren't currently receiving notifications."
log-notify-unknown: "&cState unknown. Expecting \"on\" or \"off\"."
log-search-header: "&aShowing recent actions for query &b{0} &a(page &f{1}&a of &f{2}&a)"
log-recent-header: "&aShowing recent actions (page &f{0}&a of &f{1}&a)"
log-recent-by-header: "&aShowing recent actions by &b{0} &a(page &f{1}&a of &f{2}&a)"
log-history-user-header: "&aShowing history for user &b{0} &a(page &f{1}&a of &f{2}&a)"
log-history-group-header: "&aShowing history for group &b{0} &a(page &f{1}&a of &f{2}&a)"
log-history-track-header: "&aShowing history for track &b{0} &a(page &f{1}&a of &f{2}&a)"
log-export-already-exists: "&cError: File &4{0}&c already exists."
log-export-not-writable: "&cError: File &4{0}&c is not writable."
log-export-empty: "&cThe log is empty and therefore cannot be exported."
log-export-failure: "&cAn unexpected error occured whilst writing to the file."
log-export-success: "&aSuccessfully exported the log to &b{0}&a."
import-already-running: "&cAnother import process is already running. Please wait for it to finish and try again."
export-already-running: "&cAnother export process is already running. Please wait for it to finish and try again."
import-log-doesnt-exist: "&cError: File &4{0}&c does not exist."
import-log-not-readable: "&cError: File &4{0}&c is not readable."
import-log-failure: "&cAn unexpected error occured whilst reading from the log file."
import-progress: "&b(Import) &b-> &f{0}&f% complete &7- &b{1}&f/&b{2} &foperations complete with &c{3} &ferrors."
import-progress-sin: "&b(Import) &b-> &f{0}&f% complete &7- &b{1}&f/&b{2} &foperations complete with &c{3} &ferror."
import-start: "&b(Import) &b-> &fStarting import process."
import-end-complete: "&b(Import) &a&lCOMPLETED &7- took &b{0} &7seconds - &7No errors."
import-end-complete-err: "&b(Import) &a&lCOMPLETED &7- took &b{0} &7seconds - &c{1} errors."
import-end-complete-err-sin: "&b(Import) &a&lCOMPLETED &7- took &b{0} &7seconds - &c{1} error."
import-end-error-header: >
{PREFIX}&b(Import) &7------------> &fShowing Error #&b{0} &7<------------\n
{PREFIX}&b(Import) &fWhilst executing: &3Command #{1}\n
{PREFIX}&b(Import) &fCommand: &7{2}\n
{PREFIX}&b(Import) &fType: &3{3}\n
{PREFIX}&b(Import) &fOutput:
import-end-error-content: "&b(Import) &b-> &c{0}"
import-end-error-footer: "&b(Import) &7<------------------------------------------>"
command-specs:
user:
description: "User commands"
usage: "/%s user <user>"
group:
description: "Group commands"
usage: "/%s group <group>"
track:
description: "Track commands"
usage: "/%s track <track>"
log:
description: "Log commands"
usage: "/%s log"
sync:
description: "Sync changes with the storage"
usage: "/%s sync"
info:
description: "Print general plugin info"
usage: "/%s info"
verbose:
description: "Manage verbose permission checking"
usage: "/%s verbose <true|false> [filter]"
args:
"on|record|off|paste": "whether to enable/disable logging, or to paste the logged output"
"filter": "the filter to match entries against"
"--slim": "add \"--slim\" to exclude trace data from the pasted output"
tree:
description: "Generate a tree view of permissions"
usage: "/%s tree [selection] [max level] [player]"
args:
"selection": "the root of the tree. specify \".\" to include all permissions"
"max level": "how many branch levels should be returned"
"player": "the name of an online player to check against"
search:
description: "Search for users/groups with a specific permission"
usage: "/%s search <permission>"
args:
"permission": "the permission to search for"
"page": "the page to view"
check:
description: "Perform a standard permission check on an online player"
usage: "/%s check <user> <permission>"
args:
"user": "the user to check"
"permission": "the permission to check for"
network-sync:
description: "Sync changes with the storage and request that all other servers on the network do the same"
usage: "/%s networksync"
import:
description: "Import data from a file"
usage: "/%s import <file>"
args:
"file": "the file to import from"
export:
description: "Export data to a file"
usage: "/%s export <file>"
args:
"file": "the file to export to"
reload-config:
description: "Reload some of the config options"
usage: "/%s reloadconfig"
bulk-update:
description: "Execute bulk change queries on all data"
usage: "/%s bulkupdate"
args:
"data type": "the type of data being changed. ('all', 'users' or 'groups')"
"action": "the action to perform on the data. ('update' or 'delete')"
"action field": "the field to act upon. only required for 'update'. ('permission', 'server' or 'world')"
"action value": "the value to replace with. only required for 'update'."
"constraint...": "the constraints required for the update"
migration:
description: "Migration commands"
usage: "/%s migration"
apply-edits:
description: "Applies permission changes made from the web editor"
usage: "/%s applyedits <code> [target]"
args:
"code": "the unique code for the data"
"target": "who to apply the data to"
create-group:
description: "Create a new group"
usage: "/%s creategroup <group>"
args:
"name": "the name of the group"
delete-group:
description: "Delete a group"
usage: "/%s deletegroup <group>"
args:
"name": "the name of the group"
list-groups:
description: "List all groups on the platform"
usage: "/%s listgroups"
create-track:
description: "Create a new track"
usage: "/%s createtrack <track>"
args:
"name": "the name of the track"
delete-track:
description: "Delete a track"
usage: "/%s deletetrack <track>"
args:
"name": "the name of the track"
list-tracks:
description: "List all tracks on the platform"
usage: "/%s listtracks"
user-info:
description: "Shows info about the user"
user-switchprimarygroup:
description: "Switches the user's primary group"
args:
"group": "the group to switch to"
user-promote:
description: "Promotes the user up a track"
args:
"track": "the track to promote the user up"
"context...": "the contexts to promote the user in"
user-demote:
description: "Demotes the user down a track"
args:
"track": "the track to demote the user down"
"context...": "the contexts to demote the user in"
group-info:
description: "Gives info about the group"
group-listmembers:
description: "Show the users/groups who inherit from this group"
args:
"page": "the page to view"
group-setweight:
description: "Set the groups weight"
args:
"weight": "the weight to set"
group-rename:
description: "Rename the group"
args:
"name": "the new name"
group-clone:
description: "Clone the group"
args:
"name": "the name of the group to clone onto"
holder-editor:
description: "Opens the web permission editor"
holder-showtracks:
description: "Lists the tracks that the object is on"
holder-clear:
description: "Removes all permissions, parents and meta"
args:
"context...": "the contexts to filter by"
permission:
description: "Edit permissions"
parent:
description: "Edit inheritances"
meta:
description: "Edit metadata values"
permission-info:
description: "Lists the permission nodes the object has"
args:
"page": "the page to view"
"filter": "the string to filter by"
permission-set:
description: "Sets a permission for the object"
args:
"node": "the permission node to set"
"true|false": "the value of the node"
"context...": "the contexts to add the permission in"
permission-unset:
description: "Unsets a permission for the object"
args:
"node": "the permission node to unset"
"context...": "the contexts to remove the permission in"
permission-settemp:
description: "Sets a permission for the object temporarily"
args:
"node": "the permission node to set"
"true|false": "the value of the node"
"duration": "the duration until the permission node expires"
"context...": "the contexts to add the permission in"
permission-unsettemp:
description: "Unsets a temporary permission for the object"
args:
"node": "the permission node to unset"
"context...": "the contexts to remove the permission in"
permission-check:
description: "Checks to see if the object has a certain permission node"
args:
"node": "the permission node to check for"
"context...": "the contexts to check in"
permission-check-inherits:
description: "Checks to see if the object inherits a certain permission node"
args:
"node": "the permission node to check for"
"context...": "the contexts to check in"
parent-info:
description: "Lists the groups that this object inherits from"
parent-set:
description: "Removes all other groups the object inherits already and adds them to the one given"
args:
"group": "the group to set to"
"context...": "the contexts to set the group in"
parent-add:
description: "Sets another group for the object to inherit permissions from"
args:
"group": "the group to inherit from"
"context...": "the contexts to inherit the group in"
parent-remove:
description: "Removes a previously set inheritance rule"
args:
"group": "the group to remove"
"context...": "the contexts to remove the group in"
parent-set-track:
description: "Removes all other groups the object inherits from already on the given track and adds them to the one given"
args:
"track": "the track to set on"
"group": "the group to set to, or a number relating to the position of the group on the given track"
"context...": "the contexts to set the group in"
parent-add-temp:
description: "Sets another group for the object to inherit permissions from temporarily"
args:
"group": "the group to inherit from"
"duration": "the duration of the group membership"
"context...": "the contexts to inherit the group in"
parent-remove-temp:
description: "Removes a previously set temporary inheritance rule"
args:
"group": "the group to remove"
"context...": "the contexts to remove the group in"
parent-clear:
description: "Clears all parents"
args:
"context...": "the contexts to filter by"
parent-clear-track:
description: "Clears all parents on a given track"
args:
"track": "the track to remove on"
"context...": "the contexts to filter by"
meta-info:
description: "Shows all chat meta"
meta-set:
description: "Sets a meta value"
args:
"key": "the key to set"
"value": "the value to set"
"context...": "the contexts to add the meta pair in"
meta-unset:
description: "Unsets a meta value"
args:
"key": "the key to unset"
"context...": "the contexts to remove the meta pair in"
meta-settemp:
description: "Sets a meta value temporarily"
args:
"key": "the key to set"
"value": "the value to set"
"duration": "the duration until the meta value expires"
"context...": "the contexts to add the meta pair in"
meta-unsettemp:
description: "Unsets a temporary meta value"
args:
"key": "the key to unset"
"context...": "the contexts to remove the meta pair in"
meta-addprefix:
description: "Adds a prefix"
args:
"priority": "the priority to add the prefix at"
"prefix": "the prefix string"
"context...": "the contexts to add the prefix in"
meta-addsuffix:
description: "Adds a suffix"
args:
"priority": "the priority to add the suffix at"
"suffix": "the suffix string"
"context...": "the contexts to add the suffix in"
meta-removeprefix:
description: "Removes a prefix"
args:
"priority": "the priority to remove the prefix at"
"prefix": "the prefix string"
"context...": "the contexts to remove the prefix in"
meta-removesuffix:
description: "Removes a suffix"
args:
"priority": "the priority to remove the suffix at"
"suffix": "the suffix string"
"context...": "the contexts to remove the suffix in"
meta-addtemp-prefix:
description: "Adds a prefix temporarily"
args:
"priority": "the priority to add the prefix at"
"prefix": "the prefix string"
"duration": "the duration until the prefix expires"
"context...": "the contexts to add the prefix in"
meta-addtemp-suffix:
description: "Adds a suffix temporarily"
args:
"priority": "the priority to add the suffix at"
"suffix": "the suffix string"
"duration": "the duration until the suffix expires"
"context...": "the contexts to add the suffix in"
meta-removetemp-prefix:
description: "Removes a temporary prefix"
args:
"priority": "the priority to remove the prefix at"
"prefix": "the prefix string"
"context...": "the contexts to remove the prefix in"
meta-removetemp-suffix:
description: "Removes a temporary suffix"
args:
"priority": "the priority to remove the suffix at"
"suffix": "the suffix string"
"context...": "the contexts to remove the suffix in"
meta-clear:
description: "Clears all meta"
args:
"type": "the type of meta to remove"
"context...": "the contexts to filter by"
track-info:
description: "Gives info about the track"
track-append:
description: "Appends a group onto the end of the track"
args:
"group": "the group to append"
track-insert:
description: "Inserts a group at a given position along the track"
args:
"group": "the group to insert"
"position": "the position to insert the group at (the first position on the track is 1)"
track-remove:
description: "Removes a group from the track"
args:
"group": "the group to remove"
track-clear:
description: "Clears the groups on the track"
track-rename:
description: "Rename the track"
args:
"name": "the new name"
track-clone:
description: "Clone the track"
args:
"name": "the name of the track to clone onto"
log-recent:
description: "View recent actions"
args:
"user": "the name/uuid of the user to filter by"
"page": "the page number to view"
log-search:
description: "Search the log for an entry"
args:
"query": "the query to search by"
"page": "the page number to view"
log-notify:
description: "Toggle log notifications"
args:
"on|off": "whether to toggle on or off"
log-user-history:
description: "View a user's history"
args:
"user": "the name/uuid of the user"
"page": "the page number to view"
log-group-history:
description: "View an group's history"
args:
"group": "the name of the group"
"page": "the page number to view"
log-track-history:
description: "View a track's history"
args:
"track": "the name of the track"
"page": "the page number to view"
sponge:
description: "Edit extra Sponge data"
usage: "/%s sponge <collection> <subject>"
args:
"collection": "the collection to query"
"subject": "the subject to modify"
sponge-permission-info:
description: "Shows info about the subject's permissions"
args:
"contexts...": "the contexts to filter by"
sponge-permission-set:
description: "Sets a permission for the Subject"
args:
"node": "the permission node to set"
"tristate": "the value to set the permission to"
"contexts...": "the contexts to set the permission in"
sponge-permission-clear:
description: "Clears the Subjects permissions"
args:
"contexts...": "the contexts to clear permissions in"
sponge-parent-info:
description: "Shows info about the subject's parents"
args:
"contexts...": "the contexts to filter by"
sponge-parent-add:
description: "Adds a parent to the Subject"
args:
"collection": "the subject collection where the parent Subject is"
"subject": "the name of the parent Subject"
"contexts...": "the contexts to add the parent in"
sponge-parent-remove:
description: "Removes a parent from the Subject"
args:
"collection": "the subject collection where the parent Subject is"
"subject": "the name of the parent Subject"
"contexts...": "the contexts to remove the parent in"
sponge-parent-clear:
description: "Clears the Subjects parents"
args:
"contexts...": "the contexts to clear parents in"
sponge-option-info:
description: "Shows info about the subject's options"
args:
"contexts...": "the contexts to filter by"
sponge-option-set:
description: "Sets an option for the Subject"
args:
"key": "the key to set"
"value": "the value to set the key to"
"contexts...": "the contexts to set the option in"
sponge-option-unset:
description: "Unsets an option for the Subject"
args:
"key": "the key to unset"
"contexts...": "the contexts to unset the key in"
sponge-option-clear:
description: "Clears the Subjects options"
args:
"contexts...": "the contexts to clear options in"
migration-command:
description: "Migration command"
migration-groupmanager:
description: "Migration command"
args:
"migrate as global": "if world permissions should be ignored, and just migrated as global"
migration-powerfulperms:
description: "Migration command"
args:
"address": "the address of the PP database"
"database": "the name of the PP database"
"username": "the username to log into the DB"
"password": "the password to log into the DB"
"db table": "the name of the PP table where player data is stored"
+338
View File
@@ -0,0 +1,338 @@
# LuckPerms Language File
# Locale: es_ES (Spanish)
# Author: Jose Chavez (Twitter: @JoCHav3z)
prefix: "&7&l[&b&lL&3&lP&7&l] &c"
log-info: "&7&l[&bL&3P&7&l] &3{0}"
log-warn: "&7&l[&bLuck&3Perms&7&l] &c[ADVERTENCIA] {0}"
log-error: "&7&l[&bLuck&3Perms&7&l] &4[ERROR] {0}"
empty: "{0}"
player-online: "&aConectado"
player-offline: "&cDesconectado"
loading-error: "La Informacion de los permisos no pudo ser cargada. Por favor contacte al administrador."
op-disabled: "&bEl sistema OP de Vanilla esta deshabilitado en este servidor."
op-disabled-sponge: "&2El estado de operador del servidor no tiene efecto cuando un plugin de permisos esta instalado. Por favor edite la informacion de usuario directamente."
log: "&3LOG &3&l> {0}"
export-log: "&3EXPORTAR &3&l> &f{0}"
export-log-progress: "&3EXPORTADO &3&l> &7{0}"
migration-log: "&3MIGRACION &7[&3{0}&7] &3&l> &f{1}"
migration-log-progress: "&3MIGRADO &7[&3{0}&7] &3&l> &7{1}"
command-not-recognised: "Comando no reconocido."
command-no-permission: "¡No tienes permiso de usar este comando!"
already-haspermission: "{0} ¡Ya tienes este permiso!"
does-not-havepermission: "{0} No tienes este permiso establecido."
already-has-temp-permission: "{0} ¡Ya tienes este permiso establecido temporalmente!"
does-not-have-temp-permission: "{0} No tienes este permiso establecido temporalmente."
user-not-found: "&bNo se pudo encontrar al usuario."
user-not-online: "&bUsuario &a{0}&b no esta conectado."
user-no-data: "&bUsuario &a{0}&b no tiene ningún dato cargado."
user-save-success: "&7(Los datos del usuario se guardaron en el almacenamiento)"
user-save-error: "Se ha producido un error al guardar el usuario."
user-create-fail: "Se produjo un error al crear un nuevo usuario."
group-not-found: "&bEl Grupo no se pudo encontrar."
group-save-success: "&7(Los datos del grupo se guardaron en el almacenamiento)"
group-save-error: "Se ha producido un error al guardar el grupo."
track-not-found: "&bNo se pudo encontrar la pista."
track-save-success: "&7(Los datos de la pista se guardaron en el almacenamiento)"
track-save-error: "Se ha producido un error al guardar la pista."
user-invalid-entry: "&d{0}&c El username/uuid no es valido."
group-invalid-entry: "Los nombres de grupo sólo pueden contener caracteres alfanuméricos."
track-invalid-entry: "Los nombres de pista sólo pueden contener caracteres alfanuméricos."
server-invalid-entry: "Los nombres de servidor sólo pueden contener caracteres alfanuméricos."
use-inherit-command: "Usa el comando 'parent add' y 'parent remove' en vez de especificar el nodo."
verbose-invalid-filter: "&cFiltro Verbose invalido: &f{0}"
verbose-on: "&bEl chequeo Verbose de salida se ha establecido en &aTRUE &bpara todos los permisos."
verbose-on-query: "&bEl chequeo Verbose de salida que se establecio en &aTRUE &bpara los permisos se ha pareado con los siguientes filtros: &f{0}"
verbose-off: "&bEl chequeo Verbose de salida que se establecio en &cFALSE&b."
verbose-recording-on: "&bGrabacion de Verbose establecida en &aTRUE &bpara todos los permisos."
verbose-recording-on-query: "&bGrabacion de Verbose establecida en &aTRUE &bpara los permisos se ha pareado con los siguientes filtros: &f{0}"
verbose-recording-upload-start: "&bGrabacion de Verbose se ha deshabilitado. Cargando resultados..."
verbose-recording-url: "&aURL de resultados de Verbose:"
tree-upload-start: "&bGenerando arbol de permisos..."
tree-empty: "&aNo se puede generar el árbol. No se encontraron resultados."
tree-url: "&aURL de arbol de permisos:"
search-searching: "&aBúsqueda de usuarios y grupos con &b{0}&a..."
search-result: "&aEncontradas &b{0}&a entradas por parte de &b{1}&a usuarios y &b{2}&a grupos."
search-showing-users: "&bMostrando entradas del usuario:"
search-showing-groups: "&bMostrando entradas del grupo:"
search-showing-users-with-page: "&bMostrando entradas del usuario: {0}"
search-showing-groups-with-page: "&bMostrando entradas del grupo: {0}"
check-result: "&aResultado de la comprobación del permiso en el usuario &b{0}&a para permiso &b{1}&a: &f{2}"
create-success: "&b{0}&a se creó correctamente."
delete-success: "&b{0}&a fue eliminado correctamente."
rename-success: "&b{0}&a fue exitosamente renombrado a &b{1}&a."
clone-success: "&b{0}&a fue clonado con éxito en &b{1}&a."
already-inherits: "{0} ya hereda '{1}'."
does-not-inherit: "{0} no hereda '{1}'."
already-temp-inherits: "{0} ya lo heredo temporalmente a '{1}'."
does-not-temp-inherit: "{0} no lo heredo temporalmente a '{1}'."
track-already-contains: "La pista {0} ya contiene el grupo '{1}'."
track-does-not-contain: "La pista {0} no contiene el grupo '{1}'."
track-ambiguous-call: "El usuario especificado es miembro de varios grupos en esta pista. No se puede determinar su ubicación."
group-already-exists: "¡Ese grupo ya existe!"
group-does-not-exist: "¡Ese grupo no existe!"
group-load-error: "Ocurrió un error inesperado. No se pudo cargar el grupo."
groups-load-error: "Ocurrió un error inesperado. No se pueden cargar todos los grupos."
track-already-exists: "¡La pista ya existe!"
track-does-not-exist: "¡La pista no existe!"
track-load-error: "Ocurrió un error inesperado. No se pudo cargar la pista."
tracks-load-error: "Ocurrió un error inesperado. No se pueden cargar todas las pistas."
track-empty: "La pista no se puede utilizar porque está vacía o contiene sólo un grupo."
update-task-request: "&bActualizar tarea programada."
update-task-complete: "&aLa actualizacion de la tarea a finalizado."
update-task-complete-network: "&aLa actualizacion de la tarea a finalizado. Ahora intentando enviar a otros servidores."
update-task-push-success: "&aOtros servidores fueron notificados vía &b{0} Mensajería &aexitoxamente."
update-task-push-failure: "&cError al empujar cambios a otros servidores."
update-task-push-failure-not-setup: "&cError al presionar los cambios a otros servidores. No se ha configurado un servicio de mensajería."
reload-config-success: "&aSe ha vuelto a cargar el archivo de configuración. &7(Algunas opciones sólo se aplicarán después de que el servidor se haya reiniciado.)"
info: >
{PREFIX}&2Running &bLuckPerms v{0}&2 by &bLuck&2.\n
{PREFIX}&f- &3Platform: &f{1}\n
{PREFIX}&f- &3Storage Method: &f{2}\n
{PREFIX}&f- &3Server Name: &f{3}\n
{PREFIX}&f- &3Sync Interval: &a{4} &fminutes\n
{PREFIX}&f- &3Messaging Service: &f{5}\n
{PREFIX}&f- &bCounts:\n
{PREFIX}&f- &3Online Players: &a{6}\n
{PREFIX}&f- &3Loaded Users: &a{7}\n
{PREFIX}&f- &3Loaded Groups: &a{8}\n
{PREFIX}&f- &3Loaded Tracks: &a{9}\n
{PREFIX}&f- &3Log size: &a{10}\n
{PREFIX}&f- &3UUID Cache size: &a{11}\n
{PREFIX}&f- &3Translations loaded: &a{12}\n
{PREFIX}&f- &3Pre-process contexts: &a{13}\n
{PREFIX}&f- &3Context Calculators: &a{14}\n
{PREFIX}&f- &3Unique permissions: &a{15}\n
{PREFIX}&f- &bConfiguration:\n
{PREFIX}&f- &3Use Server UUIDs: {16}\n
{PREFIX}&f- &bPermission Calculation:\n
{PREFIX}&f- &3Including Global: {17}\n
{PREFIX}&f- &3Including Global World: {18}\n
{PREFIX}&f- &3Applying Global Groups: {19}\n
{PREFIX}&f- &3Applying Global World Groups: {20}\n
{PREFIX}&f- &3Applying Wildcards: {21}\n
{PREFIX}&f- &3Applying Regex: {22}\n
{PREFIX}&f- &3Applying Shorthand: {23}
create-group-error: "Se produjo un error al crear el grupo."
delete-group-error: "Se produjo un error al eliminar el grupo."
delete-group-error-default: "No puede eliminar el grupo predeterminado."
groups-list: "&aGrupos: {0}"
create-track-error: "Se produjo un error al crear la pista."
delete-track-error: "Se produjo un error al eliminar la pista."
tracks-list: "&aPistas: {0}"
listnodes: "&b{0}'s Nodos:"
listnodes-with-page: "&b{0}'s Nodos: {1}"
listnodes-temp: >
&b{0}'s Temporary Nodes:\n
{1}
listparents: >
&b{0}'s Parent Groups:\n
{1}
listparents-temp: >
&b{0}'s Temporary Parent Groups:\n
{1}
listgroups: >
&b{0}'s Groups:\n
{1}
listgroups-temp: >
&b{0}'s Temporary Groups:\n
{1}
list-tracks: >
&b{0}'s Tracks:\n
{1}
list-tracks-empty: "{0} no está en ninguna pista."
setpermission-success: "&aEstablecer &b{0}&a a &b{1}&a para &b{2}&a."
setpermission-server-success: "&aEstablecer &b{0}&a a &b{1}&a para &b{2}&a en el servidor &b{3}&a."
setpermission-server-world-success: "&aEstablecer &b{0}&a a &b{1}&a para &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a."
setpermission-temp-success: "&aEstablecer &b{0}&a a &b{1}&a para &b{2}&a por una duracion de &b{3}&a."
setpermission-temp-server-success: "&aEstablecer &b{0}&a a &b{1}&a para &b{2}&a en el servidor &b{3}&a por una duracion de &b{4}&a."
setpermission-temp-server-world-success: "&aEstablecer &b{0}&a a &b{1}&a para &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a, por una duracion de &b{5}&a."
unsetpermission-success: "&aDesestablecer &b{0}&a para &b{1}&a."
unsetpermission-server-success: "&aDesestablecer &b{0}&a para &b{1}&a en el servidor &b{2}&a."
unsetpermission-server-world-success: "&aDesestablecer &b{0}&a para &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a."
unset-temp-permission-success: "&aDesestablecer temporalmente el permiso &b{0}&a para &b{1}&a."
unset-temp-permission-server-success: "&aDesestablecer temporalmente el permiso &b{0}&a para &b{1}&a en el servidor &b{2}&a."
unset-temp-permission-server-world-success: "&aDesestablecer temporalmente el permiso &b{0}&a para &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a."
set-inherit-success: "&b{0}&a ahora hereda los permisos de &b{1}&a."
set-inherit-server-success: "&b{0}&a ahora hereda los permisos de &b{1}&a en el servidor &b{2}&a."
set-inherit-server-world-success: "&b{0}&a ahora hereda los permisos de &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a."
set-parent-success: "&b{0}&a sus grupos parentales existentes han sido borrados, y ahora solo heredan &b{1}&a."
set-parent-server-success: "&b{0}&a sus grupos parentales existentes han sido borrados, y ahora solo heredan &b{1}&a en el servidor &b{2}&a."
set-parent-server-world-success: "&b{0}&a sus grupos parentales existentes han sido borrados, y ahora solo heredan &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a."
set-temp-inherit-success: "&b{0}&a ahora heredan permisos de &b{1}&a por una duracion de &b{2}&a."
set-temp-inherit-server-success: "&b{0}&a ahora heredan permisos de &b{1}&a en el servidor &b{2}&a por una duracion de &b{3}&a."
set-temp-inherit-server-world-success: "&b{0}&a ahora heredan permisos de &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a, por una duracion de &b{4}&a."
unset-inherit-success: "&b{0}&a ya no hereda los permisos de &b{1}&a."
unset-inherit-server-success: "&b{0}&a ya no hereda los permisos de &b{1}&a en el servidor &b{2}&a."
unset-inherit-server-world-success: "&b{0}&a ya no hereda los permisos de &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a."
unset-temp-inherit-success: "&b{0}&a ya no hereda temporalmente los permisos de &b{1}&a."
unset-temp-inherit-server-success: "&b{0}&a ya no hereda temporalmente los permisos &b{1}&a en el servidor &b{2}&a."
unset-temp-inherit-server-world-success: "&b{0}&a ya no hereda temporalmente los permisos &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a."
clear-success: "&b{0}&a's permisos fueron borrados. (&b{1}&a nodos fueron removidos.)"
clear-success-singular: "&b{0}&a's permisos fueron borrados. (&b{1}&a nodo fue removido.)"
parent-clear-success: "&b{0}&a's los padres fueron eliminados. (&b{1}&a nodos fueron removidos.)"
parent-clear-success-singular: "&b{0}&a's los padres fueron eliminados. (&b{1}&a nodo fue removido.)"
meta-clear-success: "&b{0}&a's meta fue eliminada. (&b{1}&a nodos fueron removidos.)"
meta-clear-success-singular: "&b{0}&a's meta fue eliminada. (&b{1}&a nodo fue removido.)"
illegal-date-error: "No se pudo analizar la fecha '{0}'."
past-date-error: "¡No puedes fijar una fecha en el pasado!"
chat-meta-prefix-header: "&b{0}'s Prefijos"
chat-meta-suffix-header: "&b{0}'s Sufijos"
meta-header: "&b{0}'s Meta"
chat-meta-entry: "&b-> {0} &f- &f\"{1}&f\" &8(&7heredado de &a{2}&8)"
chat-meta-entry-with-context: "&b-> {0} &f- &f\"{1}&f\" &8(&7heredado de &a{2}&8){3}"
meta-entry: "&b-> &a{0} &f= &f\"{1}&f\" &8(&7heredado de &a{2}&8)"
meta-entry-with-context: "&b-> &a{0} &f= &f\"{1}&f\" &8(&7heredado de &a{2}&8){3}"
chat-meta-prefix-none: "&b{0} no tiene prefijos."
chat-meta-suffix-none: "&b{0} no tiene sufijos."
meta-none: "&b{0} no tiene meta."
meta-invalid-priority: "Prioridad no válida '{0}'. Se esperaba un número."
already-has-prefix: "{0} ya tiene ese prefijo establecido."
already-has-suffix: "{0} ya tiene ese sufijo establecido."
does-not-have-prefix: "{0} no tiene ese prefijo establecido."
does-not-have-suffix: "{0} no tiene ese sufijo establecido."
addprefix-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a establecido como prioridad de &b{2}&a."
addprefix-server-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a establecido como prioridad de &b{2}&a en el servidor &b{3}&a."
addprefix-server-world-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a establecido como prioridad de &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a."
removeprefix-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido."
removeprefix-server-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido en el servidor &b{3}&a."
removeprefix-server-world-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido en el servidor &b{3}&a, mundo &b{4}&a."
addsuffix-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a establecido en prioridad de &b{2}&a."
addsuffix-server-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a establecido en prioridad de &b{2}&a en el servidor &b{3}&a."
addsuffix-server-world-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a establecido en prioridad de &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a."
removesuffix-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido."
removesuffix-server-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido en el servidor &b{3}&a."
removesuffix-server-world-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido en el servidor &b{3}&a, mundo &b{4}&a."
add-temp-prefix-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a en prioridad de &b{2}&a por una duracion de &b{3}&a."
add-temp-prefix-server-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a establecido en prioridad de &b{2}&a en el servidor &b{3}&a, por una duracion de &b{4}&a."
add-temp-prefix-server-world-success: "&b{0}&a tiene el prefijo &f\"{1}&f\"&a establecido en prioridad de &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a, por una duracion de &b{5}&a."
remove-temp-prefix-success: "&b{0}&a tiene temporalmente el prefijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido."
remove-temp-prefix-server-success: "&b{0}&a tiene temporalmente el prefijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido en el servidor &b{3}&a."
remove-temp-prefix-server-world-success: "&b{0}&a tiene temporalmente el prefijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido en el servidor &b{3}&a, mundo &b{4}&a."
add-temp-suffix-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a establecido en prioridad de &b{2}&a por una duracion de &b{3}&a."
add-temp-suffix-server-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a establecido en prioridad de &b{2}&a en el servidor &b{3}&a, por una duracion de &b{4}&a."
add-temp-suffix-server-world-success: "&b{0}&a tiene el sufijo &f\"{1}&f\"&a establecido en prioridad de &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a, por una duracion de &b{5}&a."
remove-temp-suffix-success: "&b{0}&a tiene temporalmente el sufijo &f\"{1}&f\"&a en prioridad de &b{1}&a removido."
remove-temp-suffix-server-success: "&b{0}&a tiene temporalmente el sufijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido en el servidor &b{3}&a."
remove-temp-suffix-server-world-success: "&b{0}&a tiene temporalmente el sufijo &f\"{1}&f\"&a en prioridad de &b{2}&a removido en el servidor &b{3}&a, mundo &b{4}&a."
already-has-meta: "{0} ya tiene ese par de valores de llave meta establecido."
set-meta-success: "&aEstablecer el valor meta para la llave &f\"{0}&f\"&a a &f\"{1}&f\"&a para &b{2}&a."
set-meta-server-success: "&aEstablecer el valor meta para la llave &f\"{0}&f\"&a a &f\"{1}&f\"&a para &b{2}&a en el servidor &b{3}&a."
set-meta-server-world-success: "&aEstablecer el valor meta para la llave &f\"{0}&f\"&a a &f\"{1}&f\"&a para &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a."
set-meta-temp-success: "&aEstablecer el valor meta para la llave &f\"{0}&f\"&a a &f\"{1}&f\"&a para &b{2}&a por una duracion de &b{3}&a."
set-meta-temp-server-success: "&aEstablecer el valor meta para la llave &f\"{0}&f\"&a a &f\"{1}&f\"&a para &b{2}&a en el servidor &b{3}&a por una duracion de &b{4}&a."
set-meta-temp-server-world-success: "&aEstablecer el valor meta para la llave &f\"{0}&f\"&a a &f\"{1}&f\"&a para &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a, por una duracion de &b{5}&a."
unset-meta-success: "&aDesestablecer el valor meta con la llave &f\"{0}&f\"&a para &b{1}&a."
unset-meta-server-success: "&aDesestablecer el valor meta con la llave &f\"{0}&f\"&a para &b{1}&a en el servidor &b{2}&a."
unset-meta-server-world-success: "&aDesestablecer el valor meta con la llave &f\"{0}&f\"&a for &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a."
unset-meta-temp-success: "&aDesestablecer temporalmente el valor meta con la llave &f\"{0}&f\"&a para &b{1}&a."
unset-meta-temp-server-success: "&aDesestablecer temporalmente el valor meta con la llave &f\"{0}&f\"&a para &b{1}&a en el servidor &b{2}&a."
unset-meta-temp-server-world-success: "&aDesestablecer temporalmente el valor meta con la llave &f\"{0}&f\"&a para &b{1}&a en el servidor &b{2}&a, mundo &b{3}&a."
bulk-change-type-error: "Tipo no válido. Se estaba esperando 'server' o 'world'."
bulk-change-success: "&aEl cambio masivo fue aplicado correctamente. {0} los registros fueron cambiados."
user-info-general: >
{PREFIX}&b&l> &bUser Info: &f{0}\n
{PREFIX}&f- &3UUID: &f{1}\n
{PREFIX}&f- &3Status: {2}\n
{PREFIX}&f- &3Primary Group: &f{3}\n
{PREFIX}&f- &aCounts:\n
{PREFIX}&f- &3Permissions: &a{4}\n
{PREFIX}&f- &3Temporary Permissions: &a{5}\n
{PREFIX}&f- &3Prefixes: &a{6}\n
{PREFIX}&f- &3Suffixes: &a{7}\n
{PREFIX}&f- &3Meta: &a{8}
user-info-data: >
{PREFIX}&f- &aCached Data:\n
{PREFIX}&f- &3Is Loaded: {0}\n
{PREFIX}&f- &3Current Contexts: {1}\n
{PREFIX}&f- &3Current Prefix: {2}\n
{PREFIX}&f- &3Current Suffix: {3}
info-parent-header: "&f- &aGrupos Parentales:"
info-temp-parent-header: "&f- &aGrupos Temporalmente Parentales:"
user-getuuid: "&bEl UUID de &b{0}&b es &b{1}&b."
user-removegroup-error-primary: "No puede eliminar un usuario de su grupo principal."
user-primarygroup-success: "&b{0}&a's grupo primario se estableció &b{1}&a."
user-primarygroup-warn-option: "&cAdvertencia: El método de cálculo de grupo primario utilizado por este servidor &7({0}) &cpuede no reflejar este cambio."
user-primarygroup-error-alreadyhas: "El usuario ya tiene este grupo configurado como su grupo principal."
user-primarygroup-error-notmember: "&b{0}&a ya no era miembro de &b{1}&a, agregándolos ahora."
user-track-error-not-contain-group: "El usuario especificado ya no está en ningún grupo de esta pista."
user-track-added-to-first: "&b{0}&a no está en ningún grupo en esta pista, por lo que se agregaron al primer grupo, &b{1}&a."
user-track-added-to-first-server: "&b{0}&a no está en ningún grupo en esta pista, por lo que se agregaron al primer grupo, &b{1}&a, en el servidor &b{2}&a."
user-track-added-to-first-server-world: "&b{0}&a no está en ningún grupo en esta pista, por lo que se agregaron al primer grupo, &b{1}&a, en el servidor &b{2}&a, mundo &b{3}&a."
user-promote-success: "&aPromover al usuario a lo largo de la pista &b{0}&a desde &b{1}&a a &b{2}&a."
user-promote-success-server: "&aPromover al usuario a lo largo de la pista &b{0}&a desde &b{1}&a a &b{2}&a en el servidor &b{3}&a."
user-promote-success-server-world: "&aPromover al usuario a lo largo de la pista &b{0}&a desde &b{1}&a a &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a."
user-promote-error-endoftrack: "El final de la pista &4{0}&c fue alcanzado. No se puede promover al usuario."
user-promote-error-malformed: >
{PREFIX}The next group on the track, {0}, no longer exists. Unable to promote user.\n
{PREFIX}Either create the group, or remove it from the track and try again.
user-demote-success: "&aDegradando al usuario a lo largo de la pista &b{0}&a desde &b{1}&a a &b{2}&a."
user-demote-success-server: "&aDegradando al usuario a lo largo de la pista &b{0}&a desde &b{1}&a a &b{2}&a en el servidor &b{3}&a."
user-demote-success-server-world: "&aDegradando al usuario a lo largo de la pista &b{0}&a desde &b{1}&a a &b{2}&a en el servidor &b{3}&a, mundo &b{4}&a."
user-demote-endoftrack: "El final de la pista &4{0}&c fue alcanzado, asi que &4{1}&c fue removido de &4{2}&c."
user-demote-error-malformed: >
{PREFIX}The previous group on the track, {0}, no longer exists. Unable to demote user.\n
{PREFIX}Either create the group, or remove it from the track and try again.
user-showpos: >
&aShowing &b{0}&a's position on track &b{1}&a.\n
{2}
group-info-general: >
{PREFIX}&b&l> &bGroup Info: &f{0}\n
{PREFIX}&f- &3Display Name: &f{1}\n
{PREFIX}&f- &3Weight: &f{2}\n
{PREFIX}&f- &aCounts:\n
{PREFIX}&f- &3Permissions: &a{3}\n
{PREFIX}&f- &3Temporary Permissions: &a{4}\n
{PREFIX}&f- &3Prefixes: &a{5}\n
{PREFIX}&f- &3Suffixes: &a{6}\n
{PREFIX}&f- &3Meta: &a{7}
group-set-weight: "&aEstablesca el peso a &b{0}&a para el grupo &b{1}&a."
track-info: >
{PREFIX}&b&l> &bShowing Track: &f{0}\n
{PREFIX}&f- &7Path: &f{1}
track-clear: "&b{0}&a's pista de grupos fue borrada."
track-append-success: "&aGrupo &b{0}&a fue añadido con éxito a la pista &b{1}&a."
track-insert-success: "&aGrupo &b{0}&a fue insertado con éxito en la pista &b{1}&a en la posicion &b{2}&a."
track-insert-error-number: "Numero esperado en lugar de recibido: {0}"
track-insert-error-invalid-pos: "No se puede insertar en la posición {0}. Indice fuera de los limites."
track-remove-success: "&aGrupo &b{0}&a se eliminó correctamente de la pista &b{1}&a."
log-load-error: "No se pudo cargar el registro."
log-invalid-page: "Número de página no válido."
log-invalid-page-range: "Número de página no válido. Por favor introduzca un valor entre 1 y {0}."
log-no-entries: "&bNo hay entradas de registro que mostrar."
log-entry: "&b#{0} -> &8(&7{1} hace&8) {2}"
log-notify-toggle-on: "&aHabilitada&b salida de registro."
log-notify-toggle-off: "&cDeshabilitada&b salida de registro."
log-notify-already-on: "Ya está recibiendo notificaciones."
log-notify-already-off: "Actualmente no estás recibiendo notificaciones."
log-notify-unknown: "Estado desconocido. Esperando \"on\" o \"off\"."
log-search-header: "&aMostrar acciones recientes para la consulta &b{0} &a(pagina &f{1}&a de &f{2}&a)"
log-recent-header: "&aMostrando acciones recientes (pagina &f{0}&a de &f{1}&a)"
log-recent-by-header: "&aMostrando las acciones recientes por &b{0} &a(pagina &f{1}&a de &f{2}&a)"
log-history-user-header: "&aMostrando el historial del usuario &b{0} &a(pagina &f{1}&a de &f{2}&a)"
log-history-group-header: "&aMostrando la historia del grupo &b{0} &a(pagina &f{1}&a de &f{2}&a)"
log-history-track-header: "&aMostrando la historia de la pista &b{0} &a(pagina &f{1}&a de &f{2}&a)"
log-export-already-exists: "Error: Archivo {0} ya existe."
log-export-not-writable: "Error: Archivo {0} no se puede escribir."
log-export-empty: "El registro está vacío y por lo tanto no puede ser exportado."
log-export-failure: "Se ha producido un error inesperado al escribir en el archivo."
log-export-success: "&aExitosamente exportó el registro a &b{0}&a."
import-already-running: "Ya se está ejecutando otro proceso de importación. Por favor espere a que finalice e inténtelo de nuevo."
export-already-running: "Ya se está ejecutando otro proceso de exportación. Por favor espere a que finalice e inténtelo de nuevo."
import-log-doesnt-exist: "Error: Archivo {0} no existe."
import-log-not-readable: "Error: Archivo {0} no es legible."
import-log-failure: "Se ha producido un error inesperado al leer del archivo de registro."
import-progress: "&b(Import) &b-> &f{0} &fporcentaje completado &7- &b{1}&f/&b{2} &fOperaciones completas con &c{3} &ferrores."
import-progress-sin: "&b(Import) &b-> &f{0} &fporcentaje completado &7- &b{1}&f/&b{2} &fOperaciones completas con &c{3} &ferrores."
import-start: "&b(Import) &b-> &fInicio del proceso de importación."
import-end-complete: "&b(Import) &a&lCOMPLETADO &7- tomo &b{0} &7segundos - &7No errores."
import-end-complete-err: "&b(Import) &a&lCOMPLETADO &7- tomo &b{0} &7segundos - &c{1} errores."
import-end-complete-err-sin: "&b(Import) &a&lCOMPLETADO &7- tomo &b{0} &7segundos - &c{1} errores."
import-end-error-header: >
{PREFIX}&b(Import) &7------------> &fShowing Error #&b{0} &7<------------\n
{PREFIX}&b(Import) &fWhilst executing: &3Command #{1}\n
{PREFIX}&b(Import) &fCommand: &7{2}\n
{PREFIX}&b(Import) &fType: &3{3}\n
{PREFIX}&b(Import) &fOutput:
import-end-error-content: "&b(Import) &b-> &c{0}"
import-end-error-footer: "&b(Import) &7<------------------------------------------>"
+728
View File
@@ -0,0 +1,728 @@
# LuckPerms Language File
# Locale: hu_HU (Hungarian)
# Author: montlikadani
prefix: "&7&l[&b&lL&3&lP&7&l] &c"
empty: "{0}"
player-online: "&aOnline"
player-offline: "&cOffline"
loading-error: "&cNem sikerült betölteni az engedélyezési adatokat. Kérlek, próbáld újra később."
op-disabled: "&bA vanilla OP rendszer le van tiltva ezen a szerveren."
op-disabled-sponge: "&2Kérjük, vegye figyelembe, hogy a kiszolgálói operátor állapota nem befolyásolja a Sponge engedély-ellenőrzéseket, ha engedélyezési bővítmény van telepítve. Kérjük, közvetlenül módosítsa a felhasználói adatokat."
log-info: "&7&l[&bL&3P&7&l] &3{0}"
log-warn: "&7&l[&bLuck&3Perms&7&l] &c[FIGYELEM] {0}"
log-error: "&7&l[&bLuck&3Perms&7&l] &4[HIBA] {0}"
log: "&3LOG &3&l> {0}"
verbose-log: "&3VB &3&l> {0}"
export-log: "&3EXPORT &3&l> &f{0}"
export-log-progress: "&3EXPORT &3&l> &7{0}"
migration-log: "&3VÁNDORLÁS &7[&3{0}&7] &3&l> &f{1}"
migration-log-progress: "&3VÁNDORLÁS &7[&3{0}&7] &3&l> &7{1}"
command-not-recognised: "&cA parancs nem ismeretes."
command-no-permission: "&cNincs jogosultságod a használatára!"
user-not-found: "&cEgy felhasználó számára &4{0}&c nem található."
user-not-online: "&b{0}&a felhasználó nem elérhető."
user-save-error: "&cHiba történt a felhasználói adatok mentése közben &4{0}&c."
group-not-found: "&cEgy nevű csoport &4{0}&c nem található."
group-save-error: "&cHiba történt a csoportadatok mentése közben &4{0}&c."
track-not-found: "&cEgy nevű sáv &4{0}&c nem található."
track-save-error: "&cHiba történt a track adatok mentése közben &4{0}&c."
user-invalid-entry: "&4{0}&c nem érvényes felhasználónév/uuid."
group-invalid-entry: "&4{0}&c nem érvényes csoportnév."
track-invalid-entry: "&4{0}&c nem érvényes track név."
server-world-invalid-entry: "&cA szerver/világnév csak alfanumerikus karaktereket tartalmazhat, és nem haladhatja meg a 36 karakter hosszúságú karaktereket."
use-inherit-command: "&cHasználd a 'parent add' és a 'parent remove' parancsokat ahelyett, hogy meghatározta volna a csomópontot."
verbose-invalid-filter: "&4{0}&c nem érvényes kiterjedt szűrő."
verbose-on: "&bRészletes naplózás &aengedélyezve &ba megfelelő ellenőrzésekhez &aBÁRMILYEN&b."
verbose-on-query: "&bRészletes naplózás &aengedélyezve &ba megfelelő ellenőrzésekhez &a{0}&b."
verbose-off: "&bRészletes naplózás &cletiltva&b."
verbose-recording-on: "&bRészletes felvétel &aengedélyezve &ba megfelelő ellenőrzésekhez &aBÁRMILYEN&b."
verbose-recording-on-query: "&bRészletes felvétel &aengedélyezve &ba megfelelő ellenőrzésekhez &a{0}&b."
verbose-upload-start: "&bRészletes naplózás &cletiltva&b. Eredmények feltöltése..."
verbose-results-url: "&aRészletes eredmények URL-je:"
tree-upload-start: "&bEngedélyezési fa létrehozása..."
tree-empty: "&cNem lehet létrehozni a fát. Nincs találat."
tree-url: "&aEngedélyezési fa URL:"
search-searching: "&aFelhasználók és csoportok keresése a következővel: &b{0}&a..."
search-searching-members: "&aFelhasználók és csoportok keresése, akik örökölnek &b{0}&a..."
search-result: "&aTalál &b{0}&a bejegyzéseket a &b{1}&a felhasználók és &b{2}&a csoportok."
search-showing-users: "&bFelhasználói bejegyzések megjelenítése:"
search-showing-groups: "&bCsoportbejegyzések megjelenítése:"
search-showing-users-with-page: "&bFelhasználói bejegyzések megjelenítése: {0}"
search-showing-groups-with-page: "&bCsoportbejegyzések megjelenítése: {0}"
apply-edits-invalid-code: "&cÉrvénytelen kód. &7({0})"
apply-edits-unable-to-read: "&cA megadott kóddal nem lehet adatokat olvasni. &7({0})"
apply-edits-no-target: "&cNem lehet elemezni a szerkesztés célját. Kérjük, nyújtsa be, mint egy extra érv."
apply-edits-target-group-not-exists: "&cCélcsoport &4{0}&c nem létezik."
apply-edits-target-user-not-uuid: "&cCélcsoport &4{0}&c nem érvényes uuid."
apply-edits-target-user-unable-to-load: "&cA célfelhasználó nem tölthető be &4{0}&c."
apply-edits-target-unknown: "&cÉrvénytelen cél. &7({0})"
apply-edits-success: "&aSikeresen alkalmazott &b{0}&a csomópontok &b{1}&a."
editor-upload-failure: "&cNem sikerült feltölteni az engedélyezési adatokat a szerkesztőhöz."
editor-url: "&aSzerkesztő URL:"
check-result: "&aEngedély-ellenőrzés eredmény a felhasználó számára &b{0}&a engedélyért &b{1}&a: &f{2}"
create-success: "&b{0}&a sikeresen létrehozva."
delete-success: "&b{0}&a sikeresen törölve."
rename-success: "&b{0}&a sikeresen átnevezve &b{1}&a."
clone-success: "&b{0}&a sikeresen klónozva &b{1}&a."
already-inherits: "&b{0}&a már örökli &b{1}&a a szövegkörnyezetben {2}&a."
does-not-inherit: "&b{0}&a nem örököl &b{1}&a a szövegkörnyezetben {2}&a."
already-temp-inherits: "&b{0}&a már átmenetileg örökli &b{1}&a a szövegkörnyezetben {2}&a."
does-not-temp-inherit: "&b{0}&a nem átmenetileg örökli &b{1}&a a szövegkörnyezetben {2}&a."
track-already-contains: "&b{0}&a már tartalmazza &b{1}&a."
track-does-not-contain: "&b{0}&a nem tartalmaz &b{1}&a."
track-ambiguous-call: "&4{0}&c ezen a pályán több csoport tagja. Nem tudják meghatározni a helyüket."
already-exists: "&4{0}&c már létezik!"
does-not-exist: "&4{0}&c nem létezik!"
group-load-error: "&cVáratlan hiba történt. A csoport nincs betöltve."
groups-load-error: "&cVáratlan hiba történt. Nem sikerült betölteni az összes csoportot."
track-load-error: "&cVáratlan hiba történt. A nyomvonal nincs betöltve."
tracks-load-error: "&cVáratlan hiba történt. Nem sikerült betölteni az összes számot."
track-empty: "&4{0}&c nem használható, mivel üres vagy csak egy csoportot tartalmaz."
update-task-request: "&bFrissítési feladatot kértek. Kérlek, várj..."
update-task-complete: "&aA feladat frissítése befejeződött."
update-task-complete-network: "&aA feladat frissítése befejeződött. Most más szerverekre próbálkozni."
update-task-push-success: "&aMás szervereket értesítettek &b{0} Üzenetek &asikeresen."
update-task-push-failure: "&cHiba történt a többi kiszolgálóra történő változtatás közben."
update-task-push-failure-not-setup: "&cHiba történt a többi kiszolgálóra történő változtatás közben. &7(egy üzenetkezelő szolgáltatást nem konfigurált)"
reload-config-success: "&aA konfigurációs fájl újratöltve. &7(néhány opció csak a szerver újraindítása után fog működni)"
info-top: >
{PREFIX}&2Running &bLuckPerms v{0}&2 by &bLuck&2.\n
{PREFIX}&f- &3Emelvény: &f{1}\n
{PREFIX}&f- &3Szerver márka: &f{2}\n
{PREFIX}&f- &3Szerver verzió: &f{3}
info-middle: >
{PREFIX}&f- &bÜzenet típusa: &f{0}\n
{PREFIX}&f- &bPélda:\n
{PREFIX}&f- &3Szerver név: &f{1}\n
{PREFIX}&f- &3Elérhető játékosok: &a{2}\n
{PREFIX}&f- &3Egyedi kapcsolatok: &a{3}\n
{PREFIX}&f- &3Üzemidő: &7{4}\n
{PREFIX}&f- &3Helyi adatok: &a{5} &7felhasználó, &a{6} &7csoport, &a{7} &7pályák\n
{PREFIX}&f- &3Kontextus számológépek: &a{8}\n
{PREFIX}&f- &3Ismert engedélyek: &a{9}\n
{PREFIX}&f- &3Aktív processzorok: &7{10}
create-error: "&cHiba történt a létrehozás során &4{0}&c."
delete-error: "&cTörlés közben hiba történt &4{0}&c."
delete-group-error-default: "&cAz alapértelmezett csoportot nem törölheti."
groups-list: "&aCsoportok: &7(név, súly, pályák)"
groups-list-entry: "&f- &3{0} &7- &b{1}"
groups-list-entry-with-tracks: "&f- &3{0} &7- &b{1} &7- [&3{2}&7]"
tracks-list: "&aPályák: {0}"
listnodes: "&b{0}'s Jogok:"
listnodes-with-page: "&b{0} Jogai: {1}"
listnodes-temp: "&b{0} Ideiglenes engedélyei:"
listnodes-temp-with-page: "&b{0} Ideiglenes engedélyei: {1}"
listparents: "&b{0} Szülői csoportok:"
listparents-temp: "&b{0} Ideiglenes szülői csoportok:"
list-tracks: >
&b{0} Pályái:\n
{1}
list-tracks-empty: "&b{0}&a nincs rajta."
context-pair-inline: "&3{0}=&b{1}"
context-pair--global-inline: "&eglobális"
context-pair-sep: "&a, "
context-pair: "&8(&7{0}=&f{1}&8)"
check-permission: "&b{0}&a engedéllyel rendelkezik &b{1}&a állítva {2}&a a szövegkörnyezetben {3}&a."
check-inherits-permission: "&b{0}&a engedéllyel rendelkezik &b{1}&a állítva {2}&a a szövegkörnyezetben {3}&a. &7(örökölt &a{4}&7)"
setpermission-success: "&aBeállítva &b{0}&a-nak/-nek &b{1}&a, mert &b{2}&a a szövegkörnyezetben {3}&a."
already-haspermission: "&b{0}&a már van &b{1}&a összefüggésben {2}&a."
setpermission-temp-success: "&aBeállítva &b{0}&a-nak/-nek &b{1}&a, mert &b{2}&a egy ideig &b{3}&a a szövegkörnyezetben {4}&a."
already-has-temp-permission: "&b{0}&a már van &b{1}&a ideiglenesen környezetben {2}&a."
unsetpermission-success: "&aVisszaállítva &b{0}&a, mert &b{1}&a a szövegkörnyezetben {2}&a."
does-not-have-permission: "&b{0}&a nem rendelkezik &b{1}&a összefüggésben {2}&a."
unset-temp-permission-success: "&aIdeiglenes engedély érvénytelenítése &b{0}&a, mert &b{1}&a a szövegkörnyezetben {2}&a."
does-not-have-temp-permission: "&b{0}&a nem rendelkezik &b{1}&a ideiglenesen környezetben {2}&a."
set-inherit-success: "&b{0}&a most örökli az engedélyeket &b{1}&a a szövegkörnyezetben {2}&a."
set-temp-inherit-success: "&b{0}&a most örökli az engedélyeket &b{1}&a egy ideig &b{2}&a a szövegkörnyezetben {3}&a."
set-parent-success: "&b{0}&a meglévő szüloi csoportjaik tiszták, és most csak örökölnek &b{1}&a a szövegkörnyezetben {2}&a."
set-track-parent-success: "&b{0}&a meglévő szülői csoportjaik voltak a pályán &b{1}&a törölni, és most csak örökli &b{2}&a a szövegkörnyezetben {3}&a."
unset-inherit-success: "&b{0}&a többé nem örökli az engedélyeket &b{1}&a a szövegkörnyezetben {2}&a."
unset-temp-inherit-success: "&b{0}&a már nem ideiglenesen örökli az engedélyeket &b{1}&a a szövegkörnyezetben {2}&a."
clear-success: "&b{0}&a a jogosultságokat a kontextusban törölték {1}&a. (&b{2}&a a csomópontokat eltávolítottuk.)"
clear-success-singular: "&b{0}&a a jogosultságokat a kontextusban törölték {1}&a. (&b{2}&a csomópontot eltávolítottuk.)"
parent-clear-success: "&b{0}&a a szülei a kontextusban megszűntek {1}&a. (&b{2}&a a csomópontokat eltávolítottuk.)"
parent-clear-success-singular: "&b{0}&a a szülei a kontextusban megszűntek {1}&a. (&b{2}&a a csomópontokat eltávolítottuk.)"
parent-clear-track-success: "&b{0}&a szülők a pályán &b{1}&a összefüggésben {2}&a. (&b{3}&a a csomópontokat eltávolítottuk.)"
parent-clear-track-success-singular: "&b{0}&a szülők a pályán &b{1}&a összefüggésben {2}&a. (&b{3}&a csomópontot eltávolítottuk.)"
meta-clear-success: "&b{0}&a meta egyezéstípusa &b{1}&a összefüggésben {2}&a. (&b{3}&a a csomópontokat eltávolítottuk.)"
meta-clear-success-singular: "&b{0}&a meta egyezéstípusa &b{1}&a összefüggésben {2}&a. (&b{3}&a csomópontot eltávolítottuk.)"
illegal-date-error: "&cNem sikerült elemezni a dátumot &4{0}&c."
past-date-error: "&cNem állíthat be dátumot a múltban!"
chat-meta-prefix-header: "&b{0} Előtagjai"
chat-meta-suffix-header: "&b{0} Utótagja"
meta-header: "&b{0} Metája"
chat-meta-entry: "&b-> {0} &f- &f'{1}&f' &8(&7örökölt &a{2}&8)"
chat-meta-entry-with-context: "&b-> {0} &f- &f'{1}&f' &8(&7örökölt &a{2}&8){3}"
meta-entry: "&b-> &a{0} &f= &f'{1}&f' &8(&7örökölt &a{2}&8)"
meta-entry-with-context: "&b-> &a{0} &f= &f'{1}&f' &8(&7örökölt &a{2}&8){3}"
chat-meta-prefix-none: "&b{0} nincsenek előtagjai."
chat-meta-suffix-none: "&b{0} nincs utótagja."
meta-none: "&b{0} nincs meta."
meta-invalid-priority: "&cÉrvénytelen prioritás &4{0}&c. Várható szám."
already-has-chat-meta: "&b{0}&a már van {1} &f'{2}&f'&a elsőbbséget élvez &b{3}&a a szövegkörnyezetben {4}&a."
already-has-temp-chat-meta: "&b{0}&a már van {1} &f'{2}&f'&a ideiglenesen &b{3}&a a szövegkörnyezetben {4}&a."
does-not-have-chat-meta: "&b{0}&a nincs {1} &f'{2}&f'&a elsőbbséget élvez &b{3}&a a szövegkörnyezetben {4}&a."
does-not-have-temp-chat-meta: "&b{0}&a nincs {1} &f'{2}&f'&a ideiglenesen &b{3}&a a szövegkörnyezetben {4}&a."
add-chatmeta-success: "&b{0}&a volt {1} &f'{2}&f'&a elsőbbséget élvez &b{3}&a a szövegkörnyezetben {4}&a."
add-temp-chatmeta-success: "&b{0}&a volt {1} &f'{2}&f'&a elsőbbséget élvez &b{3}&a egy ideig &b{4}&a a szövegkörnyezetben {5}&a."
remove-chatmeta-success: "&b{0}&a volt {1} &f'{2}&f'&a prioritásként &b{3}&a eltávolítva a kontextusban {4}&a."
bulk-remove-chatmeta-success: "&b{0}&a minden volt {1}es a prioritás &b{2}&a eltávolítva a kontextusban {3}&a."
remove-temp-chatmeta-success: "&b{0}&a ideiglenes volt {1} &f'{2}&f'&a prioritásként &b{3}&a eltávolítva a kontextusban {4}&a."
bulk-remove-temp-chatmeta-success: "&b{0}&a minden ideiglenes volt {1}es prioritásként &b{2}&a eltávolítva a kontextusban {3}&a."
already-has-meta: "&b{0}&a már van meta kulcs &f'{1}&f'&a állítva &f'{2}&f'&a a szövegkörnyezetben {3}&a."
already-has-temp-meta: "&b{0}&a már van meta kulcs &f'{1}&f'&a átmenetileg beállítva &f'{2}&f'&a a szövegkörnyezetben {3}&a."
doesnt-have-meta: "&b{0}&a nincs meta kulcs &f'{1}&f'&a összefüggésben {2}&a."
doesnt-have-temp-meta: "&b{0}&a nincs meta kulcs &f'{1}&f'&a ideiglenesen a környezetben {2}&a."
set-meta-success: "&aÁllítva a meta kulcsot &f'{0}&f'&a-nak/-nek &f'{1}&f'&a, mert &b{2}&a a szövegkörnyezetben {3}&a."
set-meta-temp-success: "&aÁllítva a meta kulcsot &f'{0}&f'&a-nak/-nek &f'{1}&f'&a, mert &b{2}&a egy ideig &b{3}&a a szövegkörnyezetben {4}&a."
unset-meta-success: "&aLeválasztva a meta kulcsot &f'{0}&f'&a, mert &b{1}&a a szövegkörnyezetben {2}&a."
unset-meta-temp-success: "&aIdeiglenes meta kulcs kikapcsolása &f'{0}&f'&a, mert &b{1}&a a szövegkörnyezetben {2}&a."
bulk-update-invalid-data-type: "&cÉrvénytelen típus. Típusok 'all', 'users' vagy 'groups'."
bulk-update-invalid-constraint: "&cÉrvénytelen kényszer &4{0}&c. A korlátozásoknak formátumban kell lenniük '&f<mező> <összehasonlító operátor> <érték>&c'."
bulk-update-invalid-comparison: "&cÉrvénytelen összehasonlító operátor '&4{0}&c'. Várható az alábbiak közül: &f== != ~~ ~!"
bulk-update-queued: "&aA tömeges frissítés sorba állt. &7(&f{0}&7)"
bulk-update-confirm: "&aA futtatáshoz &b/{0} a tömeges frissítés megerősítéséhez {1} &aa frissítés végrehajtásához."
bulk-update-unknown-id: "&aMűködési id &b{0}&a nem létezik, vagy lejárt."
bulk-update-starting: "&aFolyamatos frissítés futtatása."
bulk-update-success: "&bA tömeges frissítés sikeresen befejeződött."
bulk-update-failure: "&cA tömeges frissítés sikertelen. Ellenőrizze a konzolt hiba esetén."
user-info-general: >
{PREFIX}&b&l> &bFelhasználói adatok: &f{0}\n
{PREFIX}&f- &3UUID: &f{1}\n
{PREFIX}&f- &3Állapot: {2}\n
{PREFIX}&f- &3Elsődleges csoport: &f{3}\n
{PREFIX}&f- &aGrófok:\n
{PREFIX}&f- &3Csomópontok: &a{4}\n
{PREFIX}&f- &3Jogok: &a{5}\n
{PREFIX}&f- &3Előtagok: &a{6}\n
{PREFIX}&f- &3Utótagok: &a{7}\n
{PREFIX}&f- &3Meta: &a{8}
user-info-data: >
{PREFIX}&f- &aGyorsítótár adat:\n
{PREFIX}&f- &3Kontextuális adatok vannak: {0}\n
{PREFIX}&f- &3Jelenlegi kontextusok: {1}\n
{PREFIX}&f- &3Jelenlegi előtag: {2}\n
{PREFIX}&f- &3Jelenlegi utótag: {3}
info-parent-header: "&f- &aSzülői csoportok:"
info-temp-parent-header: "&f- &aIdeiglenes szülői csoportok:"
user-removegroup-error-primary: "&aNem lehet eltávolítani egy felhasználót az elsődleges csoportból."
user-primarygroup-success: "&b{0}&a az elsődleges csoportot &b{1}&a."
user-primarygroup-warn-option: "&aFigyelmeztetés: A kiszolgáló által használt elsődleges csoportszámítási módszer &7({0}) &anem tükrözheti ezt a változást."
user-primarygroup-error-alreadyhas: "&b{0}&a már van &b{1}&a mint elsődleges csoportjuk."
user-primarygroup-error-notmember: "&b{0}&a már nem tagja &b{1}&a, hozzátéve őket."
user-track-error-not-contain-group: "&b{0}&a már nincsenek benne &b{1}&a."
user-track-added-to-first: "&b{0}&a nincsenek ezen a pályán egyetlen csoportban sem, így az első csoportba kerültek, &b{1}&a a szövegkörnyezetben {2}&a."
user-promote-success: "&aElőmozdítása &b{0}&a a pályán &b{1}&a-tól/-től &b{2}&a-nak/-nek &b{3}&a a szövegkörnyezetben {4}&a."
user-promote-error-endoftrack: "&aA pálya vége &b{0}&a elérte. Nem lehetett népszerűsíteni &b{1}&a."
user-promote-error-malformed: >
{PREFIX}&aA következő csoport a pályán, &b{0}&a, nem létezik többé. Nem sikerült népszerűsíteni a felhasználót.\n
{PREFIX}&aVagy hozzon létre egy csoportot, vagy távolítsa el a zeneszámból, és próbálkozzon újra.
user-demote-success: "&aLefokozandó &b{0}&a a pályán &b{1}&a-tól/-től &b{2}&a-nak/-nek &b{3}&a a szövegkörnyezetben {4}&a."
user-demote-endoftrack: "&aA pálya vége &b{0}&a, így elérte &b{1}&a eltávolították &b{2}&a."
user-demote-error-malformed: >
{PREFIX}&aAz előző csoport a pályán, &b{0}&a, nem létezik többé. Nem lehet lecsökkenteni a felhasználót.\n
{PREFIX}&aVagy hozzon létre egy csoportot, vagy távolítsa el a zeneszámból, és próbálkozzon újra.
group-info-general: >
{PREFIX}&b&l> &bCsoport info: &f{0}\n
{PREFIX}&f- &3Megjelenítendő név: &f{1}\n
{PREFIX}&f- &3Súly: &f{2}\n
{PREFIX}&f- &aGrófok:\n
{PREFIX}&f- &3Csomópontok: &a{3}\n
{PREFIX}&f- &3Jogok: &a{4}\n
{PREFIX}&f- &3Előtagok: &a{5}\n
{PREFIX}&f- &3Utótagok: &a{6}\n
{PREFIX}&f- &3Meta: &a{7}
group-set-weight: "&aA súly beállítása &b{0} &b{1}&a csoporthoz."
track-info: >
{PREFIX}&b&l> &bPálya megjelenítése: &f{0}\n
{PREFIX}&f- &7Pálya: &f{1}
track-clear: "&b{0}&a csoportok nyomvonalát törölték."
track-append-success: "&aCsoport &b{0}&a csatolták a követéshez &b{1}&a."
track-insert-success: "&aCsoport &b{0}&a beillesztésre került a pályára &b{1}&a helyzetben &b{2}&a."
track-insert-error-number: "&cVárható szám, de helyette beérkezett: {0}"
track-insert-error-invalid-pos: "&cNem helyezhető be a pozícióba &4{0}&c. &7(érvénytelen pozíció)"
track-remove-success: "&aCsoport &b{0}&a eltávolították a pályáról &b{1}&a."
log-load-error: "&cA napló nem tölthető be."
log-invalid-page: "&cÉrvénytelen oldalszám."
log-invalid-page-range: "&cÉrvénytelen oldalszám. Kérjük, adjon meg egy értéket &41&c és &4{0}&c."
log-no-entries: "&bNincs megjeleníthető naplóbejegyzés."
log-entry: "&b#{0} -> &8(&7{1} ezelőtt&8) {2}"
log-notify-console: "&cA konzolra vonatkozó értesítések átváltása nem lehetséges."
log-notify-toggle-on: "&aEngedélyezve a&b naplózási kimenet."
log-notify-toggle-off: "&cLetiltva a&b naplózási kimenet."
log-notify-already-on: "&cMár értesítést kapsz."
log-notify-already-off: "&cJelenleg nem kap értesítést."
log-notify-unknown: "&cÁllam ismeretlen. Várandós \"on\" vagy \"off\"."
log-search-header: "&aA lekérdezés legutóbbi műveleteinek megjelenítése &b{0} &a(oldal &f{1} &f{2}&a-ból/-ből)"
log-recent-header: "&aA legutóbbi műveletek megjelenítése (oldal &f{0} &f{1}&a-ból/-ből)"
log-recent-by-header: "&aLegutóbbi fellépések megjelenítése a következővel: &b{0} &a(oldal &f{1} &f{2}&a-ból/-ből)"
log-history-user-header: "&aA felhasználó előzményeinek megjelenítése &b{0} &a(oldal &f{1} &f{2}&a-ból/-ből)"
log-history-group-header: "&aA csoport előzményeinek megjelenítése &b{0} &a(oldal &f{1} &f{2}&a-ból/-ből)"
log-history-track-header: "&aA sáv előzményeinek megjelenítése &b{0} &a(oldal &f{1} &f{2}&a-ból/-ből)"
log-export-already-exists: "&cHiba: A fájl &4{0}&c már létezik."
log-export-not-writable: "&cHiba: A fájl &4{0}&c nem írható."
log-export-empty: "&cA napló üres, ezért nem exportálható."
log-export-failure: "&cVáratlan hiba történt a fájl írása közben."
log-export-success: "&aSikeresen exportálta a naplót &b{0}&a."
import-already-running: "&cEgy másik behozatali folyamat már fut. Kérjük, várjon, amíg befejeződik, és próbálkozzon újra."
export-already-running: "&cEgy másik exportfolyamat már fut. Kérjük, várjon, amíg befejeződik, és próbálkozzon újra."
import-log-doesnt-exist: "&cHiba: A fájl &4{0}&c nem létezik."
import-log-not-readable: "&cHiba: A fájl &4{0}&c nem olvasható."
import-log-failure: "&cA naplófájl olvasása közben váratlan hiba történt."
import-progress: "&b(Import) &b-> &f{0}&f% sikeres &7- &b{1}&f/&b{2} &fműveletekkel kiegészítve &c{3} &fhibák."
import-progress-sin: "&b(Import) &b-> &f{0}&f% sikeres &7- &b{1}&f/&b{2} &fműveletekkel kiegészítve &c{3} &fhiba."
import-start: "&b(Import) &b-> &fImportálás indítása."
import-end-complete: "&b(Import) &a&lSIKERES &7- vett &b{0} &7másodperc - &7Nincs hibák."
import-end-complete-err: "&b(Import) &a&lSIKERES &7- vett &b{0} &7másodperc - &c{1} hibák."
import-end-complete-err-sin: "&b(Import) &a&lSIKERES &7- vett &b{0} &7másodperc - &c{1} hiba."
import-end-error-header: >
{PREFIX}&b(Import) &7------------> &fHiba megjelenítése #&b{0} &7<------------\n
{PREFIX}&b(Import) &fFuttatás közben: &3Parancs #{1}\n
{PREFIX}&b(Import) &fParancs: &7{2}\n
{PREFIX}&b(Import) &fTípus: &3{3}\n
{PREFIX}&b(Import) &fKibocsátás:
import-end-error-content: "&b(Import) &b-> &c{0}"
import-end-error-footer: "&b(Import) &7<------------------------------------------>"
command-specs:
user:
description: "Felhasználói parancsok"
usage: "/%s user <felhasználó>"
group:
description: "Csoport parancsok"
usage: "/%s group <csoport>"
track:
description: "Sávparancsok"
usage: "/%s track <track>"
log:
description: "Napló parancsok"
usage: "/%s log"
sync:
description: "A szinkronizálás megváltozik a tárolóval"
usage: "/%s sync"
info:
description: "Általános plugin info nyomtatása"
usage: "/%s info"
verbose:
description: "Rendszeres engedély ellenőrzése"
usage: "/%s verbose <true|false> [szűrő]"
args:
"on|record|off|paste": "hogy bekapcsolják/kikapcsolják a naplózást, vagy beillesztik-e a naplózott kimenetet"
"filter": "a szűrő a bejegyzések ellen"
"--slim": "hozzáad \"--slim\" a nyomkövetési adatok kizárása a beillesztett kimenetből"
tree:
description: "Hozzon létre egy fa nézetet az engedélyekről"
usage: "/%s tree [kiválasztás] [max. szint] [játékos]"
args:
"selection": "a fa gyökere. Meghatározza \".\", hogy az összes engedélyt tartalmazza"
"max level": "hány fiókszintet kell visszaküldeni"
"player": "az elérhető játékos nevét ellenőrizni"
search:
description: "Keressen olyan felhasználókat/csoportokat, akiknek engedélye van"
usage: "/%s search <jog>"
args:
"permission": "a keresési engedély"
"page": "a megtekinteni kívánt oldalt"
check:
description: "Végezzen el egy szabványos engedélyt egy elérhető játékoson"
usage: "/%s check <felhasználó> <jog>"
args:
"user": "a felhasználó ellenőrizni"
"permission": "az ellenőrzés engedélye"
network-sync:
description: "A szinkronizálás a tárhely módosításával történik, és kérni fogja, hogy a hálózaton lévő összes többi szerver ugyanezt tegye"
usage: "/%s networksync"
import:
description: "Adatok importálása fájlból"
usage: "/%s import <fájl>"
args:
"file": "az importálni kívánt fájl"
export:
description: "Adatok exportálása egy fájlba"
usage: "/%s export <fájl>"
args:
"file": "az exportálni kívánt fájl"
reload-config:
description: "Betölt néhány konfigurációs beállítást"
usage: "/%s reloadconfig"
bulk-update:
description: "Végezzen ömlesztett változás lekérdezéseket az összes adaton"
usage: "/%s bulkupdate"
args:
"data type": "a megváltoztatott adatok típusa. ('all', 'users' vagy 'groups')"
"action": "az elvégzendő művelet az adatokon. ('update' vagy 'delete')"
"action field": "a cselekvési terület. Csak a szükséges 'update'. ('permission', 'server' vagy 'world')"
"action value": "a cserélendő érték. Csak a szükséges 'update'."
"constraint...": "a frissítéshez szükséges korlátokat"
migration:
description: "Migrációs parancsok"
usage: "/%s migration"
apply-edits:
description: "A webszerkesztő által végrehajtott engedély módosításokat alkalmazza"
usage: "/%s applyedits <kód> [cél]"
args:
"code": "az adatok egyedi kódja"
"target": "aki az adatokat alkalmazza"
create-group:
description: "Létrehoz egy új csoportot"
usage: "/%s creategroup <csoport>"
args:
"name": "a csoport neve"
delete-group:
description: "Csoport eltávolítása"
usage: "/%s deletegroup <csoport>"
args:
"name": "a csoport neve"
list-groups:
description: "Felsorolja az összes csoportot a platformon"
usage: "/%s listgroups"
create-track:
description: "Hozzon létre egy új számot"
usage: "/%s createtrack <track>"
args:
"name": "a pálya neve"
delete-track:
description: "Egy szám törlése"
usage: "/%s deletetrack <track>"
args:
"name": "a pálya neve"
list-tracks:
description: "Felsorolja az összes zeneszámot a platformon"
usage: "/%s listtracks"
user-info:
description: "Információkat jelenít meg a felhasználóról"
user-switchprimarygroup:
description: "A felhasználó elsődleges csoportjának átváltása"
args:
"group": "a csoport, amelyre váltani"
user-promote:
description: "Segít a felhasználónak egy számot feltölteni"
args:
"track": "a pálya, hogy elősegítse a felhasználó fel"
"context...": "a felhasználót elősegítő kontextusok"
user-demote:
description: "Megszünteti a felhasználót egy pályán"
args:
"track": "a sáv, amely leállítja a felhasználót"
"context...": "a felhasználókat lecsökkentő kontextusok"
group-info:
description: "Információkat ad a csoportról"
group-listmembers:
description: "Mutassa be a csoportból öröklődő felhasználókat/csoportokat"
args:
"page": "a megtekinteni kívánt oldal"
group-setweight:
description: "Beállítja a csoportok súlyát"
args:
"weight": "a beállítani kívánt súly"
group-rename:
description: "Átnevezi a csoportot"
args:
"name": "az új név"
group-clone:
description: "Klónozza a csoportot"
args:
"name": "a klónozni kívánt csoport neve"
holder-editor:
description: "Megnyitja a webes engedély-szerkesztőt"
holder-showtracks:
description: "Megadja az objektum sávjait"
holder-clear:
description: "Eltávolítja az összes jogosultságot, szülőt és meta-t"
args:
"context...": "a szövegszűrés körülményei"
permission:
description: "Jogok szerkesztése"
parent:
description: "Örökletek szerkesztése"
meta:
description: "Metaadatértékek szerkesztése"
permission-info:
description: "Megadja az objektum engedélyezési csomópontjait"
args:
"page": "a megtekinteni a kívánt oldalt"
"filter": "a string szűrni"
permission-set:
description: "Beállítja az objektum engedélyét"
args:
"node": "a beállítandó jog csomópont"
"true|false": "a csomópont értéke"
"context...": "az engedéllyel rendelkező szövegkörnyezetek"
permission-unset:
description: "Megadja az objektum engedélyét"
args:
"node": "az engedéllyel rendelkező csomópont kiiktatásához"
"context...": "az engedélyek eltávolításának kontextusai"
permission-settemp:
description: "Beállítja az objektum ideiglenes engedélyét"
args:
"node": "a beállítandó engedély csomópont"
"true|false": "a csomópont értéke"
"duration": "amíg az engedély csomópontja lejárt"
"context...": "az engedéllyel rendelkező szövegkörnyezetek"
permission-unsettemp:
description: "Megadja az ideiglenes engedélyt az objektumhoz"
args:
"node": "az engedéllyel rendelkező csomópont kiiktatásához"
"context...": "az engedélyek eltávolításának kontextusai"
permission-check:
description: "Ellenőrzi, hogy az objektumnak van-e engedélyezési csomópontja"
args:
"node": "az engedéllyel rendelkező csomópontot"
"context...": "a bejelentkezés körülményei"
permission-check-inherits:
description: "Ellenőrzi, hogy az objektum örökli-e az engedélyezési csomópontot"
args:
"node": "az engedéllyel rendelkező csomópontot"
"context...": "a bejelentkezés körülményei"
parent-info:
description: "Megadja azokat a csoportokat, amelyeket ez az objektum örököl"
parent-set:
description: "Eltávolítja az összes többi csoportot, amelyet az objektum már örököl, és hozzáadja azokat az adott objektumhoz"
args:
"group": "a csoport, amelyre állít"
"context...": "a csoport beállításához szükséges kontextusok"
parent-add:
description: "Beállít egy másik csoportot az objektumhoz, hogy örökli a jogokat"
args:
"group": "a csoport örökölni"
"context...": "a csoportot örökli a szövegkörnyezetben"
parent-remove:
description: "Eltávolítja a korábban beállított öröklési szabályt"
args:
"group": "a csoport eltávolítani"
"context...": "a csoport eltávolításának kontextusai"
parent-set-track:
description: "Eltávolítja az összes többi olyan csoportot, amelyet az adott objektum már örököl az adott sávon, és hozzáadja azokat az adott sávhoz"
args:
"track": "a pálya bekapcsolásához"
"group": "a beállítani kívánt csoportot, vagy a csoport helyzetét az adott sávon"
"context...": "a csoport beállításához szükséges kontextusok"
parent-add-temp:
description: "Egy másik csoportot állít be, hogy az objektum átmenetileg átengedi az engedélyeket"
args:
"group": "a csoport örökölni"
"duration": "a csoporttagság időtartamát"
"context...": "a csoportot örökli a szövegkörnyezetben"
parent-remove-temp:
description: "Eltávolítja a korábban beállított ideiglenes öröklési szabályt"
args:
"group": "a csoport eltávolítani"
"context...": "a csoport eltávolításának kontextusai"
parent-clear:
description: "Törli az összes szülőt"
args:
"context...": "a szövegszűrés körülményei"
parent-clear-track:
description: "Törli az összes szülőt egy adott pályán"
args:
"track": "az eltávolítandó sáv"
"context...": "a szövegszűrés körülményei"
meta-info:
description: "Az összes csevegési meta megjelenítése"
meta-set:
description: "Meta értéket állít be"
args:
"key": "a beállítani kívánt kulcs"
"value": "a beállítani kívánt érték"
"context...": "a meta-pár beillesztésének kontextusai"
meta-unset:
description: "Megszünteti a meta értéket"
args:
"key": "a kiiktatás kulcsa"
"context...": "a meta-pár eltávolításának kontextusai"
meta-settemp:
description: "Átmenetileg beállítja a metaértéket"
args:
"key": "a beállítani kívánt kulcs"
"value": "a beállítani kívánt érték"
"duration": "amíg a meta érték lejár"
"context...": "a meta-pár beillesztésének kontextusai"
meta-unsettemp:
description: "Megszünteti az ideiglenes metaértéket"
args:
"key": "a kiiktatás kulcsa"
"context...": "a metapár eltávolításának kontextusai"
meta-addprefix:
description: "Hozzáad egy előtagot"
args:
"priority": "az elsőbbséget az előtag hozzáadásához"
"prefix": "az előtag karaktersorozat"
"context...": "az előtag beillesztésének kontextusai"
meta-addsuffix:
description: "Hozzáad egy utótagot"
args:
"priority": "elsőbbséget kell adni az utótagnak a következőhöz:"
"suffix": "az utótagsor"
"context...": "azok a kontextusok, amelyekhez hozzá kell adni az utótagot"
meta-removeprefix:
description: "Eltávolít egy előtagot"
args:
"priority": "az elsőbbség eltávolítása az előtagon"
"prefix": "az előtag karaktersorozat"
"context...": "az előtagot eltávolító szövegkörnyezetek"
meta-removesuffix:
description: "Eltávolít egy utótagot"
args:
"priority": "az elsőbbség, hogy eltávolítsák az utótagot"
"suffix": "az utótagsor"
"context...": "azok a kontextusok, amelyek eltávolítják az utótagot"
meta-addtemp-prefix:
description: "Privát előtagot ad át ideiglenesen"
args:
"priority": "az elsőbbséget az előtag hozzáadásához"
"prefix": "az előtag karaktersorozat"
"duration": "az időtartam, amíg az előtag lejár"
"context...": "az előtag beillesztésének kontextusai"
meta-addtemp-suffix:
description: "Egy ideiglenes utótagot ad hozzá"
args:
"priority": "elsőbbséget kell adni az utótagnak a következőhöz:"
"suffix": "az utótagsor"
"duration": "az időtartam, amíg az utótag lejár"
"context...": "azok a kontextusok, amelyekhez hozzá kell adni az utótagot"
meta-removetemp-prefix:
description: "Eltávolít ideiglenes előtagot"
args:
"priority": "az elsőbbség eltávolítása az előtagon"
"prefix": "az előtag karaktersorozat"
"context...": "az előtagot eltávolító szövegkörnyezetek"
meta-removetemp-suffix:
description: "Eltávolítja az ideiglenes utótagot"
args:
"priority": "az elsőbbség, hogy eltávolítsák az utótagot"
"suffix": "az utótagsor"
"context...": "azok a kontextusok, amelyek eltávolítják az utótagot"
meta-clear:
description: "Törli az összes metódust"
args:
"type": "a eltávolítandó meta típusa"
"context...": "a szövegszűrés körülményei"
track-info:
description: "Ad információt a pályáról"
track-append:
description: "Csatol egy csoportot a pálya végére"
args:
"group": "a csoport hozzáfűzése"
track-insert:
description: "Beilleszti a csoportot egy adott pozícióban a pálya mentén"
args:
"group": "beszúrni a kívánt csoport"
"position": "a helyzet a csoport beillesztésére (az első pozíció a pályán 1)"
track-remove:
description: "Eltávolít egy csoportot a sávról"
args:
"group": "a csoport eltávolítani"
track-clear:
description: "Törli a csoportokat a pályán"
track-rename:
description: "Nevezze át a sávot"
args:
"name": "az új név"
track-clone:
description: "Klónozza a sávot"
args:
"name": "a pálya neve, hogy klónozzanak"
log-recent:
description: "A legutóbbi műveletek megtekintése"
args:
"user": "a felhasználó neve/uuid-je, hogy kiszűrje"
"page": "a megtekinteni kívánt oldalszám"
log-search:
description: "Keressen egy bejegyzés naplót"
args:
"query": "keresési lekérdezés"
"page": "a megtekinteni kívánt oldalszám"
log-notify:
description: "A naplóbejelentések módosítása"
args:
"on|off": "hogy be vagy ki van kapcsolva"
log-user-history:
description: "A felhasználó előzményeinek megtekintése"
args:
"user": "a felhasználó neve/uuid-je"
"page": "a megtekinteni kívánt oldalszám"
log-group-history:
description: "A csoport előzményeinek megtekintése"
args:
"group": "a csoport neve"
"page": "a megtekinteni kívánt oldalszám"
log-track-history:
description: "A sáv előzményeinek megtekintése"
args:
"track": "a pálya neve"
"page": "a megtekinteni kívánt oldalszám"
sponge:
description: "Extra Szivacs adatok szerkesztése"
usage: "/%s sponge <gyűjtemény> <tárgy>"
args:
"collection": "a lekérdezésre kerülő gyűjtemény"
"subject": "módosítani a tárgyat"
sponge-permission-info:
description: "A tárgyhoz tartozó jogokkal kapcsolatos információkat jeleníti meg"
args:
"contexts...": "a szövegszűrés körülményei"
sponge-permission-set:
description: "Engedélyt ad a Tárgyhoz"
args:
"node": "a beállítandó engedély csomópont"
"tristate": "az engedélyhez szükséges érték megadása"
"contexts...": "az engedéllyel rendelkező szövegkörnyezetek"
sponge-permission-clear:
description: "Törli a Tárgy engedélyeit"
args:
"contexts...": "a szövegkörnyezetek engedélyeinek törlése"
sponge-parent-info:
description: "Megjeleníti a tárgy szüleinek adatait"
args:
"contexts...": "a szövegszűrés körülményei"
sponge-parent-add:
description: "Szülő hozzáadása a tárgyhoz"
args:
"collection": "az a tárgyi gyűjtemény, ahol a szülő Tárgy"
"subject": "a szülő Tárgy neve"
"contexts...": "a szülő hozzáadásának összefüggései"
sponge-parent-remove:
description: "Eltávolít egy szülőt a tárgytól"
args:
"collection": "az a tárgyi gyűjtemény, ahol a szülő Tárgy"
"subject": "a szülő Tárgy neve"
"contexts...": "a szülő eltávolításának összefüggései"
sponge-parent-clear:
description: "Törli a Tárgyi szülőket"
args:
"contexts...": "a szülők egyértelművé tételének összefüggései"
sponge-option-info:
description: "Megmutatja a tárggyal kapcsolatos információkat"
args:
"contexts...": "a szövegszűrés körülményei"
sponge-option-set:
description: "Beállít egy opciót a Tárgyhoz"
args:
"key": "a beállítani kívánt kulcs"
"value": "a kulcs beállításához"
"contexts...": "az opció beállításához szükséges kontextusok"
sponge-option-unset:
description: "Lefuttat egy opciót a Tárgyhoz"
args:
"key": "a kiiktatás kulcsa"
"contexts...": "a kulcshoz való hozzáférés megszüntetésének kontextusai"
sponge-option-clear:
description: "Törli a Tárgy opciókat"
args:
"contexts...": "a szövegkörnyezetek egyértelművé tételének lehetőségei"
migration-command:
description: "Migrációs parancs"
migration-groupmanager:
description: "Migrációs parancs"
args:
"migrate as global": "ha a világi engedélyeket figyelmen kívül kell hagyni, és csak globális áttérést kell alkalmazni"
migration-powerfulperms:
description: "Migrációs parancs"
args:
"address": "a PP adatbázis címe"
"database": "a PP adatbázis neve"
"username": "a DB-be való bejelentkezéshez használt felhasználónév"
"password": "a DB-be való bejelentkezéshez használt jelszó"
"db table": "a PP tábla neve, ahol a játékos adatait tárolják"
+290
View File
@@ -0,0 +1,290 @@
# LuckPerms Language File
# Locale: pl_PL (Polish)
# Author: dzegarek
prefix: "&7&l[&b&lL&3&lP&7&l] &c"
log-info: "&7&l[&bL&3P&7&l] &3{0}"
log-warn: "&7&l[&bLuck&3Perms&7&l] &c[UWAGA] {0}"
log-error: "&7&l[&bLuck&3Perms&7&l] &4[BŁĄD] {0}"
empty: "{0}"
player-online: "&aOnline"
player-offline: "&cOffline"
loading-error: "Nie można załadować danych uprawnień. Spróbuj ponownie później."
op-disabled: "&bSystem vanilla OP jest wyłączony na tym serwerze."
op-disabled-sponge: "&2Stan operatora serwera nie ma wpływu na instalację wtyczki uprawnień. Proszę bezpośrednio edytować dane użytkownika."
log: "&3LOG &3&l> {0}"
export-log: "&3Eksport &3&l> &f{0}"
export-log-progress: "&3Eksport &3&l> &7{0}"
migration-log: "&3MIGRACJA &7[&3{0}&7] &3&l> &f{1}"
migration-log-progress: "&3MIGRACJA &7[&3{0}&7] &3&l> &7{1}"
command-not-recognised: "Komenda nie została rozpoznana."
command-no-permission: "Nie masz uprawnień do korzystania z tego polecenia!"
already-haspermission: "{0} ma już to uprawnienie!"
does-not-havepermission: "{0} nie ma tego zestawu uprawnień."
already-has-temp-permission: "{0} ma już tymczasowo uprawnienie!"
does-not-have-temp-permission: "{0} nie ma tymczasowego zezwolenia."
user-not-found: "&bNie można znaleźć użytkownika."
user-not-online: "&bUżytkownik &a{0}&b nie jest online."
user-save-success: "&7(Dane użytkownika zostały zapisane w pamięci)"
user-save-error: "Podczas zapisywania użytkownika wystąpił błąd."
group-not-found: "&bNie można odnaleźć grupy."
group-save-success: "&7(Dane grupy zostały zapisane w pamięci)"
group-save-error: "Podczas zapisywania grupy wystąpił błąd."
track-not-found: "&bŚcieżka nie została odnaleziona."
track-save-success: "&7(Dane ścieżki zostały zapisane w pamięci)"
track-save-error: "Podczas zapisywania ścieżki wystąpił błąd."
user-invalid-entry: "&d{0}&c nie jest poprawną nazwą użytkownika/uuid."
group-invalid-entry: "Nazwy grup mogą zawierać tylko znaki alfanumeryczne."
track-invalid-entry: "Nazwy dróg mogą zawierać tylko znaki alfanumeryczne."
server-world-invalid-entry: "Nazwy serwerów/świata mogą zawierać znaki alfanumeryczne, ale nie mogą przekraczać 36 znaków."
use-inherit-command: "Użyj komendy 'parent add' lub 'parent remove' zamiast określenia węzła."
verbose-invalid-filter: "&cNieprawidłowy szczegółowy filtr: &f{0}"
verbose-on: "&bZapis szczegółowych plików wyjściowych ustawiony jest na & aTRUE & bdla wszystkich uprawnień."
verbose-on-query: "&bZapis szczegółowych plików wyjściowych ustawiony jest na & aTRUE & bdla wszystkich pasujących do filtra: &f{0}"
verbose-off: "&bZapis szczegółowych plików wyjściowych jest ustawiony na &cFALSE&b."
verbose-recording-on: "&bVerbose recording set to &aTRUE &bfor all permissions."
verbose-recording-on-query: "&bZapis szczegółowych zapisów jest ustawiony na &aTRUE &bdla wszystkich uprawnień pasujących do filtra: &f{0}"
verbose-recording-upload-start: "&Zapis szczegółowych zapisów został wyłączony. Przesyłanie wyników..."
verbose-recording-url: "&aSzczegółowe URL wyników:"
tree-upload-start: "&bGenerowanie drzewa uprawnień..."
tree-empty: "&aNie można wygenerować drzewa. Nie znaleziono żadnych wyników."
tree-url: "&aURL drzewa uprawnień:"
search-searching: "&aPoszukiwanie użytkowników i grup &b{0}&a..."
search-searching-members: "&aWyszukiwanie użytkowników i grup, które dziedziczą &b{0}&a..."
search-result: "&aZnaleziono &b{0}&a wpisy z &b{1}&a użytkowników i &b{2}&a grup."
search-showing-users: "&bWyświetlanie wpisów użytkownika:"
search-showing-groups: "&bWyświetlanie wpisów grup:"
search-showing-users-with-page: "&bWyświetlanie wpisów użytkownika: {0}"
search-showing-groups-with-page: "&bWyświetlanie wpisów grup: {0}"
check-result: "&aWynik sprawdzenia uprawnień dla użytkownika &b{0}&a dla uprawnień &b{1}&a: &f{2}"
create-success: "&b{0}&a został pomyślnie utworzone."
delete-success: "&b{0}&a został pomyślnie usunięty."
rename-success: "&b{0}&a został pomyślnie zmieniony na &b{1}&a."
clone-success: "&b{0}&a został pomyślnie sklonowany na &b{1}&a."
already-inherits: "{0} już dziedziczy '{1}'."
does-not-inherit: "{0} nie dziedziczy '{1}'."
already-temp-inherits: "{0} już tymczasowo dziedziczy '{1}'."
does-not-temp-inherit: "{0} nie dziedziczy tymczasowo '{1}'."
track-already-contains: "Ścieżka {0} zawiera już grupę '{1}'."
track-does-not-contain: "Ścieżka {0} nie zawiera tej grupy '{1}'."
track-ambiguous-call: "Podany użytkownik jest członkiem wielu grup na tej ścieżce. Nie można określić ich lokalizacji."
group-already-exists: "Ta grupa już istnieje!"
group-does-not-exist: "Ta grupa nie istnieje!"
group-load-error: "Wystąpił nieoczekiwany błąd. Grupa nie została załadowana."
groups-load-error: "Wystąpił nieoczekiwany błąd. Nie można załadować wszystkich grup."
track-already-exists: "Ta ścieżka już istnieje!"
track-does-not-exist: "Ta ścieżka nie istnieje!"
track-load-error: "Wystąpił nieoczekiwany błąd. Ścieżka nie została załadowana."
tracks-load-error: "Wystąpił nieoczekiwany błąd. Nie można wczytać wszystkich ścieżek."
track-empty: "Ścieżka nie może być używana jako pusta lub zawierać tylko jedną grupę."
update-task-request: "&bZaktualizuj zaplanowane zadanie."
update-task-complete: "&aZaktualizowano zadanie."
update-task-complete-network: "&aZaktualizowano zadanie. Teraz próbuj wejść na inne serwery."
update-task-push-success: "&aInne serwery zostały powiadomione za pośrednictwem & b {0} wiadomości &apomyślnie."
update-task-push-failure: "&cPodczas przesyłania zmian do innych serwerów wystąpił błąd."
update-task-push-failure-not-setup: "&cPodczas przesyłania zmian do innych serwerów wystąpił błąd. Usługa wiadomości nie została skonfigurowana."
reload-config-success: "&aPlik konfiguracyjny został ponownie załadowany. &7(niektóre opcje będą obowiązywać tylko po ponownym uruchomieniu serwera.)"
info: >
{PREFIX}&2Uruchamianie &bLuckPerms v{0}&2 by &bLuck&2.\n
{PREFIX}&f- &3Platforma: &f{1}\n
{PREFIX}&f- &3Metoda Składowania: &f{2}\n
{PREFIX}&f- &3Nazwa Serwera: &f{3}\n
{PREFIX}&f- &3Synchronizacja Czasu: &a{4} &fminuty\n
{PREFIX}&f- &3Usługa wiadomości: &f{5}\n
{PREFIX}&f- &bLiczba:\n
{PREFIX}&f- &3Online Graczy: &a{6}\n
{PREFIX}&f- &3Załadowanych użytkowników: &a{7}\n
{PREFIX}&f- &3Załadowanych grup: &a{8}\n
{PREFIX}&f- &3Załadowanych Ścieżek: &a{9}\n
{PREFIX}&f- &3Rozmiar Loga: &a{10}\n
{PREFIX}&f- &3Rozmiar pamięci podręcznej UUID: &a{11}\n
{PREFIX}&f- &3Załadowane tłumaczenia: &a{12}\n
{PREFIX}&f- &3Kontrole wstępne: &a{13}\n
{PREFIX}&f- &3Kalkulatory Kontekstu: &a{14}\n
{PREFIX}&f- &3Unikalne uprawnienia: &a{15}\n
{PREFIX}&f- &bKonfiguracja:\n
{PREFIX}&f- &3Używany Server UUIDs: {16}\n
{PREFIX}&f- &bObliczanie uprawnień:\n
{PREFIX}&f- &3Łącznie ze światem: {17}\n
{PREFIX}&f- &3Łącznie z globalnym światem: {18}\n
{PREFIX}&f- &3Stosowanie grup globalnych: {19}\n
{PREFIX}&f- &3Stosowanie Globalnych Grup Światowych: {20}\n
{PREFIX}&f- &3Stosowanie symboli wieloznacznych: {21}\n
{PREFIX}&f- &3Stosowanie Regex: {22}\n
{PREFIX}&f- &3Stosowanie skrótu: {23}
create-group-error: "Podczas tworzenia grupy wystąpił błąd."
delete-group-error: "Podczas usuwania grupy wystąpił błąd."
delete-group-error-default: "Nie można usunąć grupy domyślnej."
groups-list: "&aGrupy: {0}"
create-track-error: "Podczas tworzenia ścieżki wystąpił błąd."
delete-track-error: "Podczas usuwania ścieżki wystąpił błąd."
tracks-list: "&aŚcieżki: {0}"
listnodes: "&b{0}'s Węzły:"
listnodes-with-page: "&b{0}'s Węzły: {1}"
listnodes-temp: >
&b{0}'s Tymczasowe Węzły:\n
{1}
listparents: >
&b{0}'s Grupy Rodzicielskie:\n
{1}
listparents-temp: >
&b{0}'s Tymczasowe Grupy Rodzicielskie:\n
{1}
list-tracks: >
&b{0}'s Ścieżki:\n
{1}
list-tracks-empty: "{0} nie jest na żadnej ścieżce."
context-pair-inline: "&3{0}=&b{1}"
context-pair--global-inline: "&eglobalnie"
context-pair-sep: "&a, "
context-pair: "&8(&7{0}=&f{1}&8)"
check-permission: "&b{0}&a ma uprawnienia &b{1}&a ustawione na {2}&a w kontekście {3}&a."
setpermission-success: "&aUstawiono &b{0}&a na &b{1}&a dla &b{2}&a w kontekście {3}&a."
setpermission-temp-success: "&aUstawiono &b{0}&a na &b{1}&a dla &b{2}&a na czas trwania &b{3}&a w kontekście {4}&a."
unsetpermission-success: "&aUsunięto &b{0}&a dla &b{1}&a w kontekście {2}&a."
unset-temp-permission-success: "&aTymczasowe uprawnienia &b{0}&a dla &b{1}&a w kontekście {2}&a."
set-inherit-success: "&b{0}&a teraz dziedziczy uprawnienia od &b{1}&a w kontekście {2}&a."
set-temp-inherit-success: "&b{0}&a teraz dziedziczy uprawnienia &b{1}&a na czas trwania &b{2}&a w kontekście {3}&a."
set-parent-success: "&b{0}&a gdyby istniejące grupy macierzyste zostały wyczyszczone, a teraz tylko dziedziczą &b{1}&a w kontekście {2}&a."
set-track-parent-success: "&b{0}&a miały istniejące grupy macierzyste na ścieżce &b{1}&a wyczyszczone, a teraz dziedziczą &b{2}&a w kontekście {3}&a."
unset-inherit-success: "&b{0}&a już nie dziedziczy uprawnień &b{1}&a w kontekście {2}&a."
unset-temp-inherit-success: "&b{0}&a przestaje tymczasowo dziedziczyć uprawnienia &b{1}&a w kontekście {2}&a."
clear-success: "&b{0}&a's uprawnienia zostały wyczyszczone w kontekście {1}&a. (&b{2}&a węzły zostały usunięte.)"
clear-success-singular: "&b{0}&a's uprawnienia zostały wyczyszczone w kontekście {1}&a. (&b{2}&a węzeł został usunięty.)"
parent-clear-success: "&b{0}&a's rodzice zostali wyczyszczeni w kontekście {1}&a. &b{2}&a węzły zostały usunięte.)"
parent-clear-success-singular: "&b{0}&a's rodzice zostali wyczyszczeni w kontekście {1}&a. &b{2}&a węzeł został usunięty.)"
parent-clear-track-success: "&b{0}&a's rodzice na ścieżce &b{1}&a zostali wyczyszczeni w kontekście {2}&a. &b{3}&a węzły zostały usunięte.)"
parent-clear-track-success-singular: "&b{0}&a's parents on track &b{1}&a were cleared in context {2}&a. &b{3}&a węzeł został usunięty.)"
meta-clear-success: "&b{0}&a's meta zostały usunięte w kontekście {1}&a. &b{2}&a węzły zostały usunięte.)"
meta-clear-success-singular: "&b{0}&a's meta zostały usunięte w kontekście {1}&a. &b{2}&a węzeł został usunięty.)"
illegal-date-error: "Nie można przeanalizować daty '{0}'."
past-date-error: "W przeszłości nie można ustawić daty!"
chat-meta-prefix-header: "&b{0}'s Prefiksy"
chat-meta-suffix-header: "&b{0}'s Suffiksy"
meta-header: "&b{0}'s Meta"
chat-meta-entry: "&b-> {0} &f- &f\"{1}&f\" &8(&7odziedziczone po &a{2}&8)"
chat-meta-entry-with-context: "&b-> {0} &f- &f\"{1}&f\" &8(&7odziedziczone po &a{2}&8){3}"
meta-entry: "&b-> &a{0} &f= &f\"{1}&f\" &8(&7odziedziczone po &a{2}&8)"
meta-entry-with-context: "&b-> &a{0} &f= &f\"{1}&f\" &8(&7odziedziczone po &a{2}&8){3}"
chat-meta-prefix-none: "&b{0} nie ma prefiksów."
chat-meta-suffix-none: "&b{0} has no suffiksów."
meta-none: "&b{0} nie ma meta."
meta-invalid-priority: "Nieprawidłowy priorytet '{0}'. Spodziewany numer."
already-has-chat-meta: "{0} już to ma {1} ustawione."
does-not-have-chat-meta: "{0} nie ma tego {1} ustawionego."
add-chatmeta-success: "&b{0}&a ma {1} &f\"{2}&f\"&a ustawiony priorytet z &b{3}&a w kontekście {4}&a."
add-temp-chatmeta-success: "&b{0}&a ma {1} &f\"{2}&f\"&a ustawiony priorytet z &b{3}&a na czas trwania &b{4}&a w kontekście {5}&a."
remove-chatmeta-success: "&b{0}&a ma {1} &f\"{2}&f\"&a at priority &b{3}&a removed in context {4}&a."
bulk-remove-chatmeta-success: "&b{0}&a ma cały {1}es na priorytecie &b{2}&a usunięte w kontekście {3}&a."
remove-temp-chatmeta-success: "&b{0}&a ma tymczasowo {1} &f\"{2}&f\"&a at priority &b{3}&a usunięte w kontekście {4}&a."
bulk-remove-temp-chatmeta-success: "&b{0}&a miał wszystkie tymczasowe {1}es na priorytecie &b{2}&a usunięte w kontekście {3}&a."
already-has-meta: "{0} ma już tę parę par kluczy meta."
set-meta-success: "&aUstawiono wartość meta dla klucza &f\"{0}&f\"&a do &f\"{1}&f\"&a dla &b{2}&a w kontekście {3}&a."
set-meta-temp-success: "&aUstawiono wartości meta dla klucza &f\"{0}&f\"&a do &f\"{1}&f\"&a dla &b{2}&a na czas trwania &b{3}&a w kontekście {4}&a."
unset-meta-success: "&aUsunięto wartość meta za pomocą klucza &f\"{0}&f\"&a dla &b{1}&a w kontekście {2}&a."
unset-meta-temp-success: "&aUsunięcie tymczasowej wartości meta za pomocą klucza &f\"{0}&f\"&a dla &b{1}&a w kontekście {2}&a."
bulk-update-invalid-data-type: "Nieprawidłowy typ. Spodziewane 'all', 'users' lub 'groups'."
bulk-update-invalid-constraint: "Nieprawidłowe ograniczenie &4{0}&c. Ograniczenia powinny być w formacie '&f<field> <comparison operator> <value>&c'."
bulk-update-invalid-comparison: "Nieprawidłowy operator porównania '&4{0}&c'. Oczekiwany jeden z poniższych: &f== != ~~ ~!"
bulk-update-queued: "&aOperacja aktualizacji została w kolejce. &7(&f{0}&7)"
bulk-update-confirm: "&aUruchom &b/{0} uaktualnianie zbiorcze {1} &a i wykonaj aktualizacje."
bulk-update-unknown-id: "&aOperacja z id &b{0}&a nie istnieje lub upłynął."
bulk-update-starting: "&aUaktualnianie zbiorcze."
bulk-update-success: "&bAktualizacja zbiorcza powiodła się pomyślnie."
bulk-update-failure: "&cAktualizacja zbiorcza nie powiodła się. Sprawdź, czy konsola nie zawiera błędów."
user-info-general: >
{PREFIX}&b&l> &bInfo Użytkownika: &f{0}\n
{PREFIX}&f- &3UUID: &f{1}\n
{PREFIX}&f- &3Status: {2}\n
{PREFIX}&f- &3Grupa Podstawowa: &f{3}\n
{PREFIX}&f- &aLiczba:\n
{PREFIX}&f- &3Uprawnień: &a{4}\n
{PREFIX}&f- &3Tymczasowych uprawnień: &a{5}\n
{PREFIX}&f- &3Prefiksów: &a{6}\n
{PREFIX}&f- &3Suffiksów: &a{7}\n
{PREFIX}&f- &3Meta: &a{8}
user-info-data: >
{PREFIX}&f- &aDane buforowane:\n
{PREFIX}&f- &3Posiada dane kontekstowe: {0}\n
{PREFIX}&f- &3Obecne konteksty: {1}\n
{PREFIX}&f- &3Obecny Prefiks: {2}\n
{PREFIX}&f- &3Obecny Suffiks: {3}
info-parent-header: "&f- &aGrupy Rodziców:"
info-temp-parent-header: "&f- &aTymczasowe Grupy Rodziców:"
user-removegroup-error-primary: "Nie można usunąć użytkownika z podstawowej grupy."
user-primarygroup-success: "&b{0}&a's pierwsza grupa została ustawiona na &b{1}&a."
user-primarygroup-warn-option: "&cUwaga: Podstawowa metoda obliczania grupy jest używana przez ten serwer &7({0}) &cmoże nie odzwierciedlać tej zmiany."
user-primarygroup-error-alreadyhas: "Użytkownik ma już tę grupę jako pierwszą."
user-primarygroup-error-notmember: "&b{0}&a nie był już użytkownikiem z &b{1}&a, dodając je teraz."
user-track-error-not-contain-group: "Podany przez Ciebie użytkownik nie jest jeszcze w żadnej z grup na tej ścieżce."
user-track-added-to-first: "&b{0}&a nie jest w żadnej grupie na tej ścieżce, więc został dodany do pierwszej grupy, &b{1}&a w kontekście {2}&a."
user-promote-success: "&aPromowanie użytkownika wzdłuż toru &b{0}&a z &b{1}&a do &b{2}&a w kontekście {3}&a."
user-promote-error-endoftrack: "Koniec ścieżki &4{0}&c został osiągnięty. Nie można promować użytkownika."
user-promote-error-malformed: >
{PREFIX}Następna grupa na ścieżce, {0}, już nie istnieje. Nie można promować użytkownika..\n
{PREFIX}Albo utwórz grupę lub usuń ją ze ścieżki i spróbuj ponownie.
user-demote-success: "&aObniżanie użytkownika wzdłuż ścieżki &b{0}&a z &b{1}&a do &b{2}&a w kontekście {3}&a."
user-demote-endoftrack: "Koniec ścieżki &4{0}&c został osiągnięty, więc &4{1}&c zostało usunięte z &4{2}&c."
user-demote-error-malformed: >
{PREFIX}Poprzednia grupa na ścieżce, {0}, już nie istnieje. Nie można obniżyć użytkownika.\n
{PREFIX}Albo utwórz grupę lub usuń ją ze ścieżki i spróbuj ponownie.
group-info-general: >
{PREFIX}&b&l> &bInfo Grupy: &f{0}\n
{PREFIX}&f- &3Wyświetlana Nazwa: &f{1}\n
{PREFIX}&f- &3Rola: &f{2}\n
{PREFIX}&f- &aLiczba:\n
{PREFIX}&f- &3Uprawnienia: &a{3}\n
{PREFIX}&f- &3Tymczasowe Uprawnienia: &a{4}\n
{PREFIX}&f- &3Prefiksy: &a{5}\n
{PREFIX}&f- &3Suffiksy: &a{6}\n
{PREFIX}&f- &3Meta: &a{7}
group-set-weight: "&aSet weight to &b{0}&a for group &b{1}&a."
track-info: >
{PREFIX}&b&l> &bWyświetlanie ścieżki: &f{0}\n
{PREFIX}&f- &7Ścieżka: &f{1}
track-clear: "&b{0}&a grupy zostały usunięte"
track-append-success: "&aGrupa &b{0}&a została z powodzeniem dodana do śledzenia &b{1}&a."
track-insert-success: "&aGrupa &b{0}&a została pomyślnie wstawiona do ścieżki &b{1}&a na pozycji &b{2}&a."
track-insert-error-number: "Przewidywany numer, ale otrzymany: {0}"
track-insert-error-invalid-pos: "Nie można wstawić w pozycji {0}. Wyloguj się z granic."
track-remove-success: "&aGrupa &b{0}&a została z powodzeniem usunięta ze ścieżki &b{1}&a."
log-load-error: "Nie można załadować loga."
log-invalid-page: "Nieprawidłowy numer strony."
log-invalid-page-range: "Nieprawidłowy numer strony. Wprowadź wartość między 1 a {0}."
log-no-entries: "&bBrak wpisów do logów."
log-entry: "&b#{0} -> &8(&7{1} temu&8) {2}"
log-notify-toggle-on: "&aWłączone&b wyjście z logowania."
log-notify-toggle-off: "&cWyłączone&b wyjście z logowania."
log-notify-already-on: "Obecnie otrzymujesz powiadomienia."
log-notify-already-off: "Obecnie nie otrzymujesz powiadomień."
log-notify-unknown: "Stan nieznany. Oczekiwanie na \"on\" lub \"off\"."
log-search-header: "&aWyświetlanie ostatnich działań dotyczących zapytania &b{0} &a(strona &f{1}&a z &f{2}&a)"
log-recent-header: "&aWyświetlanie ostatnich działań (strona &f{0}&a z &f{1}&a)"
log-recent-by-header: "&aPokazanie ostatnich działań przez &b{0} &a(strona &f{1}&a z &f{2}&a)"
log-history-user-header: "&aWyświetlanie historii dla użytkowników &b{0} &a(strona &f{1}&a z &f{2}&a)"
log-history-group-header: "&aWyświetlanie historii dla grup &b{0} &a(strona &f{1}&a z &f{2}&a)"
log-history-track-header: "&aWyświetlanie historii dla ścieżki &b{0} &a(strona &f{1}&a z &f{2}&a)"
log-export-already-exists: "Błąd: Plik {0} już istnieje."
log-export-not-writable: "Błąd: Plik {0} jest tylko do odczytu."
log-export-empty: "Log jest pusty i dlatego nie może być eksportowany"
log-export-failure: "Podczas zapisywania do pliku wystąpił nieoczekiwany błąd."
log-export-success: "&aPomyślnie exportowano log do &b{0}&a."
import-already-running: "Inny proces importowania jest już uruchomiony. Spróbuj ponownie później."
export-already-running: "Inny proces eksportowania jest już uruchomiony. Spróbuj ponownie później."
import-log-doesnt-exist: "Błąd: Plik {0} nie istnieje."
import-log-not-readable: "Błąd: Plik {0} nie można odczytać."
import-log-failure: "Podczas odtwarzania loga wystąpił nieoczekiwany błąd."
import-progress: "&b(Import) &b-> &f{0} &fprocent kompletne &7- &b{1}&f/&b{2} &foperacja zakończona z &c{3} &fbłędami."
import-progress-sin: "&b(Import) &b-> &f{0} &fprocent kompletne &7- &b{1}&f/&b{2} &foperacja zakończona z &c{3} &fbłędami."
import-start: "&b(Import) &b-> &fUruchamianie procesu importowania."
import-end-complete: "&b(Import) &a&lZAKOŃCZONE &7- zajęło &b{0} &7sekundy - &7Nie ma błędów."
import-end-complete-err: "&b(Import) &a&lZAKOŃCZONE &7- zajęło &b{0} &7sekundy - &c{1} błędy."
import-end-complete-err-sin: "&b(Import) &a&lZAKOŃCZONE &7- zajęło &b{0} &7sekundy - &c{1} błąd."
import-end-error-header: >
{PREFIX}&b(Import) &7------------> &fWyświetlanie błędu #&b{0} &7<------------\n
{PREFIX}&b(Import) &fWykonując: &3Komende #{1}\n
{PREFIX}&b(Import) &fKomenda: &7{2}\n
{PREFIX}&b(Import) &fWpisane: &3{3}\n
{PREFIX}&b(Import) &fWyjście:
import-end-error-content: "&b(Import) &b-> &c{0}"
import-end-error-footer: "&b(Import) &7<------------------------------------------>"
+303
View File
@@ -0,0 +1,303 @@
# LuckPerms Language File
# Locale: ru_RU
# Author: Pixelmon.PRO
prefix: "&7&l[&b&lL&3&lP&7&l] &c"
log-info: "&7&l[&bL&3P&7&l] &3{0}"
log-warn: "&7&l[&bLuck&3Perms&7&l] &c[ПРЕДУПРЕЖДЕНИЕ] {0}"
log-error: "&7&l[&bLuck&3Perms&7&l] &4[ОШИБКА] {0}"
empty: "{0}"
player-online: "&aОнлайн"
player-offline: "&cОффлайн"
loading-error: "Не удалось загрузить данные разрешений. Пожалуйста обратитесь к Администратору."
op-disabled: "&bНа этом сервере отключена ванильная система OP."
op-disabled-sponge: "&2Статус Оператора Сервера не влияет на установленный плагин разрешений. Пожалуйста, отредактируйте данные пользователя напрямую."
log: "&3LOG &3&l> {0}"
export-log: "&3ЭКСПОРТ &3&l> &f{0}"
export-log-progress: "&3ЭКСПОРТ &3&l> &7{0}"
migration-log: "&3ПЕРЕМЕЩЕНИЕ &7[&3{0}&7] &3&l> &f{1}"
migration-log-progress: "&3ПЕРЕМЕЩЕНИЕ &7[&3{0}&7] &3&l> &7{1}"
command-not-recognised: "Команда не распознана."
command-no-permission: "У вас нет разрешения на использование этой команды!"
already-haspermission: "{0} уже имеет такое разрешение!"
does-not-havepermission: "{0} не имеет этого набора разрешений."
already-has-temp-permission: "{0} уже имеет этот набор временных разрешений!"
does-not-have-temp-permission: "{0} не имеет этого набора временных разрешений."
user-not-found: "&bНе удалось найти пользователя."
user-not-online: "&bПользователь &a{0}&b не онлайн."
user-no-data: "&bПользователь &a{0}&b не имеет загруженных данных."
user-save-success: "&7(Пользовательские данные были сохранены на хранение)"
user-save-error: "При сохранении пользователя произошла ошибка."
user-create-fail: "При создании нового пользователя возникла ошибка."
group-not-found: "&bНе удалось найти группу."
group-save-success: "&7(Групповые данные были сохранены на хранение)"
group-save-error: "При сохранении группы произошла ошибка."
track-not-found: "&bНе удалось найти путь."
track-save-success: "&7(Данные о путе сохранены на хранение)"
track-save-error: "При сохранении пути произошла ошибка."
user-invalid-entry: "&d{0}&c не является допустимым именем пользователя/uuid."
group-invalid-entry: "Имена групп могут содержать только буквенно-цифровые символы."
track-invalid-entry: "Имена путей могут содержать только буквенно-цифровые символы."
server-invalid-entry: "Имена серверов могут содержать только буквенно-цифровые символы."
use-inherit-command: "Используйте команды «parent add» и «parent remove» вместо указания узла (node)."
verbose-invalid-filter: "&cНедопустимый подробный (verbose) фильтр: &f{0}"
verbose-on: "&Запись подробной проверки установлена на &aTRUE &bдля всех разрешений (пермов)."
verbose-on-query: "&Запись подробной проверки установлена на &aTRUE &bДля разрешений, соответствующих следующим фильтрам: &f{0}"
verbose-off: "&Запись подробной проверки установлена на &cFALSE&b."
verbose-recording-on: "&Запись подробной записи установлена ​​на &aTRUE &bдля всех разрешений."
verbose-recording-on-query: "&Запись подробной записи установлена ​​на &aTRUE &bДля разрешений, соответствующих следующим фильтрам: &f{0}"
verbose-recording-upload-start: "&bЗапись подробной информации была отключена. Загрузка результатов..."
verbose-recording-url: "&aПодробный URL результатов:"
tree-upload-start: "&bСоздание дерева разрешений..."
tree-empty: "&aНе удалось создать дерево. Ничего не найдено."
tree-url: "&aURL дерева разрешений:"
search-searching: "&aПоиск пользователей и групп с помощью &b{0}&a..."
search-searching-members: "&aПоиск пользователей и групп, унаследовавших от &b{0}&a..."
search-result: "&aНайдено &b{0}&a записи из &b{1}&a пользователей и &b{2}&a групп."
search-showing-users: "&bПросмотр записей пользователя:"
search-showing-groups: "&bПросмотр записей в группах:"
search-showing-users-with-page: "&bПросмотр записей пользователя: {0}"
search-showing-groups-with-page: "&bПросмотр записей в группах: {0}"
check-result: "&aРезультат проверки разрешения пользователя &b{0}&a на разрешение &b{1}&a: &f{2}"
create-success: "&b{0}&a было успешно создано."
delete-success: "&b{0}&a успешно удален."
rename-success: "&b{0}&a был успешно переименован в &b{1}&a."
clone-success: "&b{0}&a был успешно клонирован на &b{1}&a."
already-inherits: "{0} уже наследует '{1}'."
does-not-inherit: "{0} не наследует '{1}'."
already-temp-inherits: "{0} уже временно наследует '{1}'."
does-not-temp-inherit: "{0} временно не наследует '{1}'."
track-already-contains: "Путь {0} уже содержит группу '{1}'."
track-does-not-contain: "Путь {0} не содержит группу '{1}'."
track-ambiguous-call: "Указанный пользователь является членом нескольких групп на этом пути. Невозможно определить их местоположение."
group-already-exists: "Эта группа уже существует!"
group-does-not-exist: "Эта группа не существует!"
group-load-error: "Произошла непредвиденная ошибка. Группа не загружена."
groups-load-error: "Произошла непредвиденная ошибка. Не удалось загрузить все группы."
track-already-exists: "Этот путь уже существует!"
track-does-not-exist: "Этот путь не существует!"
track-load-error: "Произошла непредвиденная ошибка. Путь не загружен."
tracks-load-error: "Произошла непредвиденная ошибка. Не удалось загрузить все пути."
track-empty: "Путь нельзя использовать, так как он пуст или содержит только одну группу."
update-task-request: "&bОбновление задачи запланировано."
update-task-complete: "&aОбновление задачи завершено."
update-task-complete-network: "&aОбновление задачи завершено. Теперь пытаемся подключиться к другим серверам."
update-task-push-success: "&aДругие серверы были уведомлены через &b{0} cообщения и удаленный доступ."
update-task-push-failure: "&cОшибка при нажатии изменений на других серверах."
update-task-push-failure-not-setup: "&cОшибка при нажатии изменений на других серверах. Служба обмена сообщениями не настроена."
reload-config-success: "&aФайл конфигурации был перезагружен. &7(Некоторые параметры будут применяться только после перезапуска сервера.)"
info: >
{PREFIX}&2Выполнение &bLuckPerms v{0}&2 by &bLuck&2.\n
{PREFIX}&f- &3Платформа: &f{1}\n
{PREFIX}&f- &3Способ Хранения: &f{2}\n
{PREFIX}&f- &3Имя Сервера: &f{3}\n
{PREFIX}&f- &3Интервал Синхронизации: &a{4} &fминут\n
{PREFIX}&f- &3Служба обмена сообщениями: &f{5}\n
{PREFIX}&f- &bГрафы:\n
{PREFIX}&f- &3Игроки Онлайн: &a{6}\n
{PREFIX}&f- &3Загруженные пользователи: &a{7}\n
{PREFIX}&f- &3Загруженные группы: &a{8}\n
{PREFIX}&f- &3Загруженные пути: &a{9}\n
{PREFIX}&f- &3Размер лога: &a{10}\n
{PREFIX}&f- &3UUID Размер кеша: &a{11}\n
{PREFIX}&f- &3Переводы загружены: &a{12}\n
{PREFIX}&f- &3Контексты перед обработкой: &a{13}\n
{PREFIX}&f- &3Счетчик контекста: &a{14}\n
{PREFIX}&f- &3Уникальные разрешения: &a{15}\n
{PREFIX}&f- &bКонфигурация:\n
{PREFIX}&f- &3Использует сервер UUIDs: {16}\n
{PREFIX}&f- &bСчетчик разрешений:\n
{PREFIX}&f- &3Включая глобальные: {17}\n
{PREFIX}&f- &3Включая глобальный мир: {18}\n
{PREFIX}&f- &3Применения глобальных групп: {19}\n
{PREFIX}&f- &3Применения глобальных групп мира: {20}\n
{PREFIX}&f- &3Применения подстановочных знаков (Wildcards): {21}\n
{PREFIX}&f- &3Применение регулярных выражений: {22}\n
{PREFIX}&f- &3Применение стенографии (Shorthand): {23}
create-group-error: "При создании группы произошла ошибка."
delete-group-error: "При удалении группы произошла ошибка."
delete-group-error-default: "Вы не можете удалить группу по умолчанию."
groups-list: "&aГруппы: {0}"
create-track-error: "При создании пути произошла ошибка."
delete-track-error: "При удалении пути произошла ошибка."
tracks-list: "&aГенеральные пути: {0}"
listnodes: "&b{0}'s Узлы:"
listnodes-with-page: "&b{0}'s Узлы: {1}"
listnodes-temp: >
&b{0}'s Временные узлы:\n
{1}
listparents: >
&b{0}'s Родительские группы:\n
{1}
listparents-temp: >
&b{0}'s Временные родительские группы:\n
{1}
listgroups: >
&b{0}'s Группы:\n
{1}
listgroups-temp: >
&b{0}'s Временные группы:\n
{1}
list-tracks: >
&b{0}'s Пути:\n
{1}
list-tracks-empty: "{0} не на каких-либо путях."
context-pair-inline: "&3{0}=&b{1}"
context-pair--global-inline: "&eГлобальный"
context-pair-end: "&a."
context-pair-sep: "&a, "
context-pair: "&8(&7{0}=&f{1}&8)"
check-permission: "&b{0}&a имеет разрешение &b{1}&a установленое в {2}&a в контексте {3}&a."
check-permission-inherited: "&b{0}&a имеет разрешение &b{1}&a установленое в {2}&a в контексте {3}&a, наследованный от &b{4}&a."
setpermission-success: "&aУстановить &b{0}&a к &b{1}&a для &b{2}&a."
setpermission-temp-success: "&aУстановить &b{0}&a к &b{1}&a для &b{2}&a На срок &b{3}&a."
unsetpermission-success: "&aСбросить &b{0}&a для &b{1}&a."
unset-temp-permission-success: "&aСбросить временное разрешение &b{0}&a для &b{1}&a."
set-inherit-success: "&b{0}&a теперь наследует разрешения от &b{1}&a."
set-parent-success: "&b{0}&a их существующие родительские группы очищены, и теперь только наследуют &b{1}&a."
set-temp-inherit-success: "&b{0}&a теперь наследует разрешения от &b{1}&a на срок &b{2}&a."
unset-inherit-success: "&b{0}&a больше не наследует разрешения от &b{1}&a."
unset-temp-inherit-success: "&b{0}&a больше не наследует временные разрешения (пермы) &b{1}&a."
clear-success: "&b{0}&a's разрешения были очищены. (&b{1}&a узлы были удалены.)"
clear-success-singular: "&b{0}&a's разрешения были очищены. (&b{1}&a узел удален.)"
parent-clear-success: "&b{0}&a's родители очищены. (&b{1}&a узлы были удалены.)"
parent-clear-success-singular: "&b{0}&a's родители очищены. (&b{1}&a узел удален.)"
meta-clear-success: "&b{0}&a's мета очищена. (&b{1}&a узлы были удалены.)"
meta-clear-success-singular: "&b{0}&a's мета очищена. (&b{1}&a узел удален.)"
illegal-date-error: "Не удалось выполнить синтаксический анализ даты '{0}'."
past-date-error: "Вы не можете установить дату в прошлом!"
chat-meta-prefix-header: "&b{0}'s Префиксы"
chat-meta-suffix-header: "&b{0}'s Суффиксы"
meta-header: "&b{0}'s Мета"
chat-meta-entry: "&b-> {0} &f- &f\"{1}&f\" &8(&7унаследованный от &a{2}&8)"
chat-meta-entry-with-context: "&b-> {0} &f- &f\"{1}&f\" &8(&7унаследованный от &a{2}&8){3}"
meta-entry: "&b-> &a{0} &f= &f\"{1}&f\" &8(&7унаследованный от &a{2}&8)"
meta-entry-with-context: "&b-> &a{0} &f= &f\"{1}&f\" &8(&7унаследованный от &a{2}&8){3}"
chat-meta-prefix-none: "&b{0} не имеет префиксов."
chat-meta-suffix-none: "&b{0} не имеет суффиксов."
meta-none: "&b{0} не имеет мету."
meta-invalid-priority: "Недопустимый приоритет '{0}'. Ожидается число."
already-has-chat-meta: "{0} уже имеет этот {1}."
does-not-have-chat-meta: "{0} не имеет этого {1}."
add-chatmeta-success: "&b{0}&a имеет {1} &f\"{2}&f\"&a установлен в приоритете &b{3}&a в контексте {4}&a."
add-temp-chatmeta-success: "&b{0}&a имеет {1} &f\"{2}&f\"&a установлен в приоритете &b{3}&a на срок &b{4}&a в контексте {5}&a."
remove-chatmeta-success: "&b{0}&a имеет {1} &f\"{2}&f\"&a в приоритете &b{3}&a удален из контекста {4}&a."
bulk-remove-chatmeta-success: "&b{0}&a все временные {1}es в приоритете &b{3}&a были удалены из контекста {4}&a."
remove-temp-chatmeta-success: "&b{0}&a временно установленный {1} &f\"{2}&f\"&a в приоритете &b{3}&a был удален из контекста {4}&a."
bulk-remove-temp-chatmeta-success: "&b{0}&a все временные {1}es в приоритете &b{3}&a были удалены из контекста {4}&a."
already-has-meta: "{0} уже установлено, что установлена ​​пара мета-значения клавиши."
set-meta-success: "&aУстановить метазначение для клавиши &f\"{0}&f\"&a к &f\"{1}&f\"&a для &b{2}&a."
set-meta-temp-success: "&aУстановить метазначение для клавиши &f\"{0}&f\"&a к &f\"{1}&f\"&a для &b{2}&a на срок &b{3}&a."
unset-meta-success: "&aСбросить метазначение с помощью клавиши &f\"{0}&f\"&a для &b{1}&a."
unset-meta-temp-success: "&aСбросить временное мета-значение с помощью клавиши &f\"{0}&f\"&a для &b{1}&a."
bulk-update-invalid-data-type: "Недопустимый тип. Выберите 'all', 'users' или 'groups'."
bulk-update-invalid-constraint: "Недопустимое ограничение &4{0}&c. Ограничения должны быть в формате '&f<поле> <оператор сравнения> <значение>&c'."
bulk-update-invalid-comparison: "Недопустимый оператор сравнения '&4{0}&c'. Выберите одно из следующих: &f== != ~~ ~!"
bulk-update-queued: "&aМассовое обновление было поставлено в очередь. &7(&f{0}&7)"
bulk-update-confirm: "&aВведите &b/{0} bulkupdate confirm {1} &aчтобы подтвердить выполнение массового обновления."
bulk-update-unknown-id: "&aОперация с id &b{0} &aне существует или истек срок действия."
bulk-update-starting: "&aВыполнение массового обновления."
bulk-update-success: "&bМассовое обновление успешно завершено."
bulk-update-failure: "&cНе удалось выполнить массовое обновление. Проверьте консоль на наличие ошибок."
bulk-change-type-error: "Недопустимый тип. Введите 'server' или 'world'."
bulk-change-success: "&aПрименение изменения успешно. {0} записей изменено."
user-info-general: >
{PREFIX}&b&l> &bИнформация о пользователе: &f{0}\n
{PREFIX}&f- &3UUID: &f{1}\n
{PREFIX}&f- &3Статус: {2}\n
{PREFIX}&f- &3Первичная группа: &f{3}\n
{PREFIX}&f- &aГрафы:\n
{PREFIX}&f- &3Разрешения: &a{4}\n
{PREFIX}&f- &3Временные разрешения: &a{5}\n
{PREFIX}&f- &3Префиксы: &a{6}\n
{PREFIX}&f- &3Суффиксы: &a{7}\n
{PREFIX}&f- &3Мета: &a{8}
user-info-data: >
{PREFIX}&f- &aКэшированные данные:\n
{PREFIX}&f- &3Загружается: {0}\n
{PREFIX}&f- &3Текущие контексты: {1}\n
{PREFIX}&f- &3Текущие префиксы: {2}\n
{PREFIX}&f- &3Текущие суффиксы: {3}
info-parent-header: "&f- &aРодительские группы:"
info-temp-parent-header: "&f- &aВременные родительские группы:"
user-getuuid: "&bUUID для &b{0}&b это &b{1}&b."
user-removegroup-error-primary: "Вы не можете удалить пользователя из своей основной группы."
user-primarygroup-success: "&b{0}&a's для основной группы установил значение &b{1}&a."
user-primarygroup-warn-option: "&cВнимание: метод расчета основной группы, используемый этим сервером &7({0}) &cМожет не отражать это изменение."
user-primarygroup-error-alreadyhas: "Пользователь уже установил эту группу в качестве основной группы."
user-primarygroup-error-notmember: "&b{0}&a еще не был членом &b{1}&a, Добавить его сейчас."
user-track-error-not-contain-group: "Указанный пользователь не находится ни в каких группах на этом пути."
user-track-added-to-first: "&b{0}&a не находится ни в каких группах на этом треке, поэтому они были добавлены в первую группу &b{1}&a в контексте {2}&a."
user-promote-success: "&aПовышение пользователя &b{0}&a от &b{1}&a к &b{2}&a в контексте {3}&a."
user-promote-error-endoftrack: "Конец пути &4{0}&c, был достигнут. Не удалось повысить пользователя."
user-promote-error-malformed: >
{PREFIX}Следующая группа на пути, {0}, более не существует. Не удалось продвинуть пользователя.\n
{PREFIX}Создайте группу или удалите ее с пути и повторите попытку.
user-demote-success: "&aПонижение пользователя по пути &b{0}&a от &b{1}&a к &b{2}&a в контексте {3}&a."
user-demote-endoftrack: "Конец пути &4{0}&c был достингут, поэтому &4{1}&c был удален из &4{2}&c."
user-demote-error-malformed: >
{PREFIX}Предыдущая группа на пути, {0}, более не существуют. Невозможно понизить рейтинг пользователя.\n
{PREFIX}Создайте группу или удалите ее с пути и повторите попытку.
user-showpos: >
&aпоказывает &b{0}&a's позицию на пути &b{1}&a.\n
{2}
group-info-general: >
{PREFIX}&b&l> &bИнфо о группе: &f{0}\n
{PREFIX}&f- &3Отображаемое имя: &f{1}\n
{PREFIX}&f- &3Значение: &f{2}\n
{PREFIX}&f- &aГрафы:\n
{PREFIX}&f- &3Разрешения: &a{3}\n
{PREFIX}&f- &3Временные разрешения: &a{4}\n
{PREFIX}&f- &3Префиксы: &a{5}\n
{PREFIX}&f- &3Суффиксы: &a{6}\n
{PREFIX}&f- &3Мета: &a{7}
group-set-weight: "&aУстановить значение &b{0}&a для группы &b{1}&a."
track-info: >
{PREFIX}&b&l> &bПоказать путь: &f{0}\n
{PREFIX}&f- &7Путь: &f{1}
track-clear: "&b{0}&a's путь групп был очищен."
track-append-success: "&aГруппа &b{0}&a успешно добавлен в путь &b{1}&a."
track-insert-success: "&aГруппа &b{0}&a был успешно добавлен в путь &b{1}&a на позиции &b{2}&a."
track-insert-error-number: "Ожидаемое число, но вместо этого получено: {0}"
track-insert-error-invalid-pos: "Невозможно вставить в позицию {0}. Индекс вне пределов."
track-remove-success: "&aГруппа &b{0}&a Успешно удален с пути &b{1}&a."
log-load-error: "Не удалось загрузить лог."
log-invalid-page: "Недействительный номер страницы."
log-invalid-page-range: "Недопустимый номер страницы. Введите значение от 1 до {0}."
log-no-entries: "&bНет логов для отображения."
log-entry: "&b#{0} -> &8(&7{1} тому назад&8) {2}"
log-notify-toggle-on: "&aВключен&b вывод лога."
log-notify-toggle-off: "&cОтключен&b вывод лога."
log-notify-already-on: "Вы уже получаете оповещения."
log-notify-already-off: "Вы не получаете оповещений."
log-notify-unknown: "Состояние неизвестно. Ожидание \"on\" или \"off\"."
log-search-header: "&aПросмотр недавних действий для запроса &b{0} &a(страница &f{1}&a из &f{2}&a)"
log-recent-header: "&aПоследние действия (страница &f{0}&a из &f{1}&a)"
log-recent-by-header: "&aПоказывать последние действия &b{0} &a(страница &f{1}&a из &f{2}&a)"
log-history-user-header: "&aОтображение истории для пользователя &b{0} &a(страница &f{1}&a из &f{2}&a)"
log-history-group-header: "&aОтображение истории для группы &b{0} &a(страница &f{1}&a из &f{2}&a)"
log-history-track-header: "&aОтображение истории для пути &b{0} &a(страница &f{1}&a из &f{2}&a)"
log-export-already-exists: "Ошибка: файл {0} уже существует."
log-export-not-writable: "Ошибка: файл {0} недоступен для записи."
log-export-empty: "Логи пусты и поэтому не могут быть экспортированы."
log-export-failure: "Произошла непредвиденная ошибка во время записи в файл."
log-export-success: "&aФайл логов успешно экспортирован в &b{0}&a."
import-already-running: "Другой процесс импорта уже запущен. Дождитесь завершения и повторите попытку."
export-already-running: "Другой процесс экспорта уже запущен. Дождитесь завершения и повторите попытку."
import-log-doesnt-exist: "Ошибка: файл {0} не существует."
import-log-not-readable: "Ошибка: файл {0} не читается."
import-log-failure: "Произошла непредвиденная ошибка при чтении лога."
import-progress: "&b(Импортировать) &b-> &f{0} &fПроцент завершен &7- &b{1}&f/&b{2} &fОперации в комплекте с &c{3} &fошибками."
import-progress-sin: "&b(Импортировать) &b-> &f{0} &fПроцент завершен &7- &b{1}&f/&b{2} &fОперации в комплекте с &c{3} &fошибками."
import-start: "&b(Импортировать) &b-> &fЗапуск процесса импорта."
import-end-complete: "&b(Импортировать) &a&lЗАВЕРШЕНО &7- заняло &b{0} &7секунд - &7Нет ошибок."
import-end-complete-err: "&b(Импортировать) &a&lЗАВЕРШЕНО &7- заняло &b{0} &7секунд - &c{1} ошибок."
import-end-complete-err-sin: "&b(Импортировать) &a&lЗАВЕРШЕНО &7- заняло &b{0} &7секунд - &c{1} ошибок."
import-end-error-header: >
{PREFIX}&b(Import) &7------------> &fОтображение ошибки #&b{0} &7<------------\n
{PREFIX}&b(Import) &fВыполняя: &3Команду #{1}\n
{PREFIX}&b(Import) &fКоманда: &7{2}\n
{PREFIX}&b(Import) &fТип: &3{3}\n
{PREFIX}&b(Import) &fВывод:
import-end-error-content: "&b(Импортировать) &b-> &c{0}"
import-end-error-footer: "&b(Импортировать) &7<------------------------------------------>"
+728
View File
@@ -0,0 +1,728 @@
# LuckPerms Language File
# Locale: zh-CN (Simplified Chinese)
# Author: EuropeJing
prefix: "&7&l[&b&lL&3&lP&7&l] &c"
empty: "{0}"
player-online: "&a在线"
player-offline: "&c离线"
loading-error: "&c权限数据无法加载,请稍后再试。"
op-disabled: "&b此服务器上禁用了vanilla OP系统。"
op-disabled-sponge: "&2请注意,当安装权限插件时,服务器OP状态无效。请直接编辑用户数据。"
log-info: "&7&l[&bL&3P&7&l] &3{0}"
log-warn: "&7&l[&bLuck&3Perms&7&l] &c[警告] {0}"
log-error: "&7&l[&bLuck&3Perms&7&l] &4[错误] {0}"
log: "&3日志 &3&l> {0}"
verbose-log: "&3详细 &3&l> {0}"
export-log: "&3输出 &3&l> &f{0}"
export-log-progress: "&3输出 &3&l> &7{0}"
migration-log: "&3移动 &7[&3{0}&7] &3&l> &f{1}"
migration-log-progress: "&3移动 &7[&3{0}&7] &3&l> &7{1}"
command-not-recognised: "&c命令未被识别。"
command-no-permission: "&c你没有权限使用这个命令!"
user-not-found: "&c找不到用户&4{0}&c。"
user-not-online: "&a用户&b{0}&a不在线。"
user-save-error: "&c保存用户&4{0}&c数据时出错。"
group-not-found: "&b找不到名为&4{0}&c的组。"
group-save-error: "&c保存组&4{0}&c数据时出错。"
track-not-found: "&b无法找到为&4{0}&c的权限组轨道。"
track-save-error: "&c保存权限组轨道&4{0}&c时出错。"
user-invalid-entry: "&4{0}&c不是有效的用户名/uuid。"
group-invalid-entry: "&4{0}&c不是有效的组名称。"
track-invalid-entry: "&4{0}&c不是有效的权限组轨道名称。"
server-world-invalid-entry: "&c服务器/世界名称只能包含字母数字字符,长度不能超过36个字符。"
use-inherit-command: "&c使用'parent add'和'parent remove'命令,而不是指定节点。"
verbose-invalid-filter: "&4{0}&c不是有效的详细过滤器"
verbose-on: "&b将匹配过滤器:&aANY&b的权限详细输出设置为&a开&b。"
verbose-on-query: "&b将匹配过滤器:&a{0}&b的权限详细输出设置为&a开&b。"
verbose-off: "&b详细输出设置为&c关&b。"
verbose-recording-on: "&b将匹配过滤器:&aANY&b的权限详细记录设置为&a开&b。"
verbose-recording-on-query: "&b将匹配过滤器:&a{0}&b的权限详细记录设置为&a开&b。"
verbose-upload-start: "&b详细记录被禁用。正在上传结果..."
verbose-results-url: "&a详细结果的网址:"
tree-upload-start: "&b生成权限树..."
tree-empty: "&c无法生成树,没有找到结果。"
tree-url: "&a权限树网址:"
search-searching: "&a使用&b{0}&a搜索用户和组..."
search-searching-members: "&a正在搜索继承自&b{0}&a的用户和组..."
search-result: "&a从&b{1}&a用户和&b{2}&a组找到&b{0}&a个条目。"
search-showing-users: "&b显示用户条目:"
search-showing-groups: "&b显示组条目:"
search-showing-users-with-page: "&b显示用户条目: {0}"
search-showing-groups-with-page: "&b显示组条目: {0}"
apply-edits-invalid-code: "&c无效的代码。&7({0})"
apply-edits-unable-to-read: "&c无法使用给定代码读取数据。&7({0})"
apply-edits-no-target: "&c无法解析编辑的目标。请提供它作为一个额外的参数。"
apply-edits-target-group-not-exists: "&c目标组&4{0}&c不存在。"
apply-edits-target-user-not-uuid: "&c目标用户&4{0}&c不是一个有效的uuid。"
apply-edits-target-user-unable-to-load: "&c无法加载目标用户&4{0}&c。"
apply-edits-target-unknown: "&c目标无效。&7({0})"
apply-edits-success: "&a成功应用&b{0}&a节点到&b{1}&a。"
editor-upload-failure: "&c无法将权限数据上传到编辑器。"
editor-url: "&a编辑器URL:"
check-result: "&a用户&b{0}&a获得允许&b{1}&a权限的检查结果&a: &f{2}"
create-success: "&b{0}&a创建成功。"
delete-success: "&b{0}&a删除成功。"
rename-success: "&b{0}&a成功被重命名为&b{1}&a。"
clone-success: "&b{0}&a成功地复制到&b{1}&a。"
already-inherits: "&b{0}&a已经从环境&b{2}&a中继承&b{1}&a。"
does-not-inherit: "&b{0}&a不再从环境&b{2}&a中继承&b{1}&a。"
already-temp-inherits: "&b{0}&a已经从环境&b{2}&a中临时继承&b{1}&a。"
does-not-temp-inherit: "&b{0}&a不再从环境&b{2}&a中临时继承&b{1}&a。"
track-already-contains: "&a权限组轨道&b{0}&a已经包含组&b{1}&a。"
track-does-not-contain: "&a权限组轨道&b{0}&a不包含组&b{1}&a。"
track-ambiguous-call: "&c用户&4{0}&c是此权限组轨道上多个组的成员,无法确定他们的位置。"
already-exists: "&4{0}&c已存在!"
does-not-exist: "&4{0}&c不存在!"
group-load-error: "&c发生意外错误,组未加载。"
groups-load-error: "&c发生意外错误,无法加载所有组。"
track-load-error: "&c发生意外错误。权限组轨道未加载。"
tracks-load-error: "&c发生意外错误,无法加载所有权限组轨道。"
track-empty: "&c权限组轨道&4{0}&c不能使用,因为它是空的或只包含一个组。"
update-task-request: "&b已请求更新任务。请稍候..."
update-task-complete: "&a更新任务完成。"
update-task-complete-network: "&a更新任务完成,现在尝试推送到其他服务器。"
update-task-push-success: "&a其他服务器通过&b{0}消息传递成功通知。"
update-task-push-failure: "&c将更改推送到其他服务器时出错。"
update-task-push-failure-not-setup: "&c将更改推送到其他服务器时出错。&7(未配置消息传递服务)"
reload-config-success: "&a重新加载配置文件。&7(一些选项将仅在服务器重新启动后才应用)"
info-top: >
{PREFIX}&2运行 &bLuckPerms v{0}&2 by &bLuck&2。\n
{PREFIX}&f- &3平台: &f{1}\n
{PREFIX}&f- &3服务器型号: &f{2}\n
{PREFIX}&f- &3服务器版本: &f{3}
info-middle: >
{PREFIX}&f- &3消息类型: &f{0}\n
{PREFIX}&f- &b详情:\n
{PREFIX}&f- &3服务器名称: &f{1}\n
{PREFIX}&f- &3在线玩家: &a{2}\n
{PREFIX}&f- &3唯一连接: &a{3}\n
{PREFIX}&f- &3正常运行时间: &7{4}\n
{PREFIX}&f- &3本地数据: &a{5}&7用户,&a{6}&7组,&a{7}&7权限组轨道\n
{PREFIX}&f- &3环境统计: &a{8}\n
{PREFIX}&f- &3已知权限: &a{9}\n
{PREFIX}&f- &3活动处理器: &7{10}
create-group-error: "&c创建组&4{0}&c时出错。"
delete-group-error: "&c删除组&4{0}&c时出错。"
delete-group-error-default: "&c你不能删除默认组。"
groups-list: "&a组: &7(名字,权重,权限组轨道)"
groups-list-entry: "&f- &3{0} &7- &b{1}"
groups-list-entry-with-tracks: "&f- &3{0} &7- &b{1} &7- [&3{2}&7]"
tracks-list: "&a权限组轨道: {0}"
listnodes: "&b{0}的权限:"
listnodes-with-page: "&b{0}的权限: {1}"
listnodes-temp: "&b{0}的临时权限:"
listnodes-temp-with-page: "&b{0}的临时权限: {1}"
listparents: "&b{0}的父组"
listparents-temp: "&b{0}的临时父组:"
list-tracks: >
&b{0}的权限组轨道:\n
{1}
list-tracks-empty: "&b{0}&a不在任何权限组轨道上。"
context-pair-inline: "&3{0}=&b{1}"
context-pair--global-inline: "&e全局"
context-pair-sep: "&a,"
context-pair: "&8(&7{0}=&f{1}&8)"
check-permission: "&b在环境&b{3}&b中,&b{0}&b的权限&b{1}&b被设置为&b{2}&b。"
check-inherits-permission: "&a在环境&b{3}&a中将&b{0}&a的&b{1}&a权限设置为&b{2}&a。&7(从&a{4}&7继承)"
setpermission-success: "&a在环境&b{3}&a中将&b{2}&a的&b{0}&a权限设置为&b{1}&a。"
already-haspermission: "&a在环境&b{2}&a中&b{0}&a已经设置权限&b{1}&a。"
setpermission-temp-success: "&a在环境&b{4}&a中将&b{2}&a的&b{0}&a临时权限设为&b{1}&a,持续时间&b{3}&a。"
already-has-temp-permission: "&a在环境&b{2}&a中&b{0}&a已经设置临时权限&b{1}&a。"
unsetpermission-success: "&a在环境&b{2}&a中取消&b{1}&a的&b{0}&a权限。"
does-not-have-permission: "&a在环境&b{2}&a中&b{0}&a没有设置权限&b{1}&a。"
unset-temp-permission-success: "&a在环境&b{2}&a中取消&b{1}&a的临时权限&b{0}&a。"
does-not-have-temp-permission: "&a在环境&b{2}&a中&b{0}&a没有设置临时权限&b{1}&a。"
set-inherit-success: "&b{0}&a现在在环境&b{2}&a中继承&b{1}&a的权限&a。"
set-temp-inherit-success: "&b{0}&a现在在环境&b{3}&a中继承&b{1}&a的权限,持续时间&b{2}&a。"
set-parent-success: "&b{0}&a现有的父组被清除,现在只在&b{2}&a中继承&b{1}&a。"
set-track-parent-success: "在权限组轨道&b{1}&a上&b{0}&a现有的父组被清除,现在只在&b{3}&a中继承&b{2}&a。"
unset-inherit-success: "&b{0}&a不再继承环境&b{2}&a中&b{1}&a的权限。"
unset-temp-inherit-success: "&b{0}&a在环境&b{2}&a中不再继承临时权限&b{1}&a。"
clear-success: "&b{0}&a的权限在环境&b{1}&a中被清除。(&b{2}&a节点已被删除。)"
clear-success-singular: "&b{0}&a的权限在环境&b{1}&a中被清除。(&b{2}&a节点已被删除。)"
parent-clear-success: "&b{0}&a的父组在环境&b{1}&a中被清除。(&b{2}&a节点被删除。)"
parent-clear-success-singular: "&b{0}&a的父组在环境&b{1}&a中被清除。(&b{2}&a节点被删除。)"
parent-clear-track-success: "在权限组轨道&b{1}&a上,&b{0}&a的父组在环境&b{2}&a中被清除。(&b{3}&a节点被删除。)"
parent-clear-track-success-singular: "在权限组轨道&b{1}&a上,&b{0}&a的父组在环境&b{2}&a中被清除。(&b{3}&a节点被删除。)"
meta-clear-success: "&b{0}&a的元数据匹配类型&b{1}&a在环境&b{2}&a中被清除。(&b{3}&a节点已被删除。)"
meta-clear-success-singular: "&b{0}&a的元数据匹配类型&b{1}&a在环境&b{2}&a中被清除。(&b{3}&a节点已被删除。)"
illegal-date-error: "&c无法解析日期'{0}'。"
past-date-error: "&c你不能设定一个过去的日期!"
chat-meta-prefix-header: "&b{0}的前缀"
chat-meta-suffix-header: "&b{0}的后缀"
meta-header: "&b{0}的元信息"
chat-meta-entry: "&b-> {0} &f- &f'{1}&f' &8(&7继承自&a{2}&8)"
chat-meta-entry-with-context: "&b-> {0} &f- &f'{1}&f' &8(&7继承自&a{2}&8){3}"
meta-entry: "&b-> &a{0} &f= &f'{1}&f' &8(&7继承自&a{2}&8)"
meta-entry-with-context: "&b-> &a{0} &f= &f'{1}&f' &8(&7继承自&a{2}&8){3}"
chat-meta-prefix-none: "&b{0}没有前缀。"
chat-meta-suffix-none: "&b{0}没有后缀。"
meta-none: "&b{0}没有元信息。"
meta-invalid-priority: "&c优先级&4{0}&c无效。应该是一个数字。"
already-has-chat-meta: "在环境&b{4}&a中设置&b{0}&a的元信息{1}&f'{2}&f'&a优先级为&b{3}&a。"
already-has-temp-chat-meta: "在环境&b{4}&a中设置&b{0}&a的元信息{1}&f'{2}&f'&a临时优先级为&b{3}&a。"
does-not-have-chat-meta: "在环境&b{4}&a中没有设置&b{0}&a的元信息{1}&f'{2}&f'&a优先级为&b{3}&a。"
does-not-have-temp-chat-meta: "在环境&b{4}&a中没有设置&b{0}&a的元信息{1}&f'{2}&f'&a临时优先级为&b{3}&a。"
add-chatmeta-success: "在环境&b{4}&a中将&b{0}&a的{1}设置为&f'{2}&f',优先级&b{3}&a。"
add-temp-chatmeta-success: "在环境&b{5}&a中将&b{0}&a的{1}设置为&f'{2}&f'&a,优先级&b{3}&a,持续时间为&b{4}&a。"
remove-chatmeta-success: "在环境&b{4}&a中删除了&b{0}&a优先级为&b{3}&a的{1} &f'{2}&f'&a。"
bulk-remove-chatmeta-success: "在环境&b{3}&a中删除了&b{0}&a优先级&b{2}&a的所有&b{1}&a。"
remove-temp-chatmeta-success: "在环境&b{4}&a中删除了&b{0}&a优先级&b{3}&a的临时{1} &f'{2}&f'&a。"
bulk-remove-temp-chatmeta-success: "在环境&b{3}&a中删除了&b{0}&a优先级&b{2}&a的所有临时&b{1}&a。"
already-has-meta: "&b{0}&a在环境&b{3}&a中设置了元信息&f'{1}&f'&a为&b&f'{2}&f'&a。"
already-has-temp-meta: "&b{0}&a在环境&b{3}&a中设置了临时元信息&f'{1}&f'&a为&b&f'{2}&f'&a。"
doesnt-have-meta: "&b{0}&a在环境&b{2}&a中没有设置元信息&f'{1}&f'&a。"
doesnt-have-temp-meta: "&b{0}&a在环境&b{2}&a中没有设置临时元信息&f'{1}&f'&a。"
set-meta-success: "&a在环境&b{3}&a中将&b{2}&a的元信息&f'{0}&f'&a设置为&f'{1}&f'&a。"
set-meta-temp-success: "&a在环境&b{4}&a中将&b{2}&a的元信息&f'{0}&f'&a设置为&f'{1}&f'&a,持续时间为&b{3}&a。"
unset-meta-success: "&a在环境{2}中取消设置&b{1}&a的元信息&f'{0}&f'&a。"
unset-meta-temp-success: "&a在环境{2}中取消设置&b{1}&a的临时元信息&f'{0}&f'&a。"
bulk-update-invalid-data-type: "&c类型无效。期望类型是'全部'、'用户'或'组'。"
bulk-update-invalid-constraint: "&c无效的约束&4{0}&c。约束的格式应该是'&f<field> <comparison operator> <value>&c'。"
bulk-update-invalid-comparison: "&c比较运算符无效'&4{0}&c'。应该以下之一: &f== != ~~ ~!"
bulk-update-queued: "&a批量更新操作已排队。&7(&f{0}&7)"
bulk-update-confirm: "&a运行&b/{0} bulkupdate &a确认{1}执行更新。"
bulk-update-unknown-id: "&a使用id&b{0}&a的操作不存在或已过期。"
bulk-update-starting: "&a运行批量更新。"
bulk-update-success: "&b批量更新已成功完成。"
bulk-update-failure: "&c批量更新失败。检查控制台是否有错误。"
user-info-general: >
{PREFIX}&b&l> &b用户信息: &f{0}\n
{PREFIX}&f- &3UUID: &f{1}\n
{PREFIX}&f- &3状态: {2}\n
{PREFIX}&f- &3主组: &f{3}\n
{PREFIX}&f- &a统计:\n
{PREFIX}&f- &3节点: &a{4}\n
{PREFIX}&f- &3权限: &a{5}\n
{PREFIX}&f- &3前缀: &a{6}\n
{PREFIX}&f- &3后缀: &a{7}\n
{PREFIX}&f- &3元信息: &a{8}
user-info-data: >
{PREFIX}&f- &a缓存数据:\n
{PREFIX}&f- &3具有环境数据: {0}\n
{PREFIX}&f- &3当前环境: {1}\n
{PREFIX}&f- &3当前前缀: {2}\n
{PREFIX}&f- &3当前后缀: {3}
info-parent-header: "&f- &a父组:"
info-temp-parent-header: "&f- &a临时父组:"
user-removegroup-error-primary: "&a你不能从其主组中删除用户。"
user-primarygroup-success: "&b{0}&a的主组已设为&b{1}&a。"
user-primarygroup-warn-option: "&a警告:此服务器&7({0})&a使用的主组计算方法可能无法反映此更改。"
user-primarygroup-error-alreadyhas: "&a已将&b{1}&a设置为用户&b{0}&a的主组。"
user-primarygroup-error-notmember: "&b{0}&a不是&b{1}&a的成员,现在添加它们。"
user-track-error-not-contain-group: "&a用户&b{0}&a尚未在权限组轨道&b{1}&a上的任何组中。"
user-track-added-to-first: "&b{0}&a不在此权限组轨道上的任何组中,因此在环境&b{2}&a中将其添加到第一组&b{2}&a。"
user-promote-success: "&a在环境&b{4}&a中将用户&b{0}&a沿权限组轨道&b{1}&a从&b{2}&a提升到&b{3}&a。"
user-promote-error-endoftrack: "&a已达到权限组轨道&b{0}&a的结尾,所以无法提升用户&b{1}&a。"
user-promote-error-malformed: >
{PREFIX}&a权限组轨道上的下一个组&b{0}&a不存在。无法提升用户。\n
{PREFIX}&a创建组,或将其从权限组轨道中删除,然后重试。
user-demote-success: "&a在环境&b{4}&a中将&b{0}&a沿权限组轨道&b{1}&a从&b{2}&a下降到&b{3}&a。"
user-demote-endoftrack: "&a已达到权限组轨道&b{0}&a的结尾,所以&b{1}&a已从&b{2}&a中移除。"
user-demote-error-malformed: >
{PREFIX}&a权限组轨道上的上一组&b{0}&a不存在。无法降级用户。\n
{PREFIX}&a创建组,或将其从权限组轨道中删除,然后重试。
group-info-general: >
{PREFIX}&b&l> &b组信息: &f{0}\n
{PREFIX}&f- &3显示名称: &f{1}\n
{PREFIX}&f- &3权重: &f{2}\n
{PREFIX}&f- &a统计:\n
{PREFIX}&f- &3节点: &a{3}\n
{PREFIX}&f- &3临时权限: &a{4}\n
{PREFIX}&f- &3前缀: &a{5}\n
{PREFIX}&f- &3后缀: &a{6}\n
{PREFIX}&f- &3元信息: &a{7}
group-set-weight: "&a将组&b{1}&a的权重设置为&b{0}&a。"
track-info: >
{PREFIX}&b&l> &b显示权限组轨道: &f{0}\n
{PREFIX}&f- &7路径: &f{1}
track-clear: "&b{0}&a的权限组轨道已被清除。"
track-append-success: "&a组&b{0}&a已成功添加到权限组轨道&b{1}&a。"
track-insert-success: "&a组&b{0}&a已成功插入权限组轨道&b{1}&a的&b{2}&a位置。"
track-insert-error-number: "&c没有预期的数量,而是收到: {0}"
track-insert-error-invalid-pos: "&c无法插入位置&4{0}&c。&7(无效位置)"
track-remove-success: "&a组&b{0}&a已成功从权限组轨道&b{1}&a移除。"
log-load-error: "&c日志无法加载。"
log-invalid-page: "&c页码无效。"
log-invalid-page-range: "&c页码无效,请输入&41&c和&4{0}&c之间的值。"
log-no-entries: "&b没有日志条目显示。"
log-entry: "&b#{0} -> &8(&7{1} ago&8) {2}"
log-notify-console: "&c无法切换控制台通知。"
log-notify-toggle-on: "&a启用&b记录输出。"
log-notify-toggle-off: "&c禁用&b记录输出。"
log-notify-already-on: "&c你已经收到通知。"
log-notify-already-off: "&c你目前没有收到通知。"
log-notify-unknown: "&c状态未知。输入 \"on\" 或者 \"off\"。"
log-search-header: "&a显示最近的查询操作&b{0} &a(&f{1}&a/&f{2}&a)"
log-recent-header: "&a显示最近操作 (&f{0}&a/&f{1}&a)"
log-recent-by-header: "&a显示&b{0}&a的最近操作 (&f{1}&a/&f{2}&a)"
log-history-user-header: "&a显示用户的历史记录&b{0} &a(&f{1}&a/&f{2}&a)"
log-history-group-header: "&a显示组的历史记录 &b{0} &a(&f{1}&a/&f{2}&a)"
log-history-track-header: "&a显示权限组轨道的历史记录 &b{0} &a(&f{1}&a/&f{2}&a)"
log-export-already-exists: "&c错误: 文件&4{0}&c已经存在。"
log-export-not-writable: "&c错误: 文件&4{0}&c不可写。"
log-export-empty: "&c日志为空,因此无法导出。"
log-export-failure: "&c写入文件时出现意外错误。"
log-export-success: "&a成功导出日志&b{0}&a。"
import-already-running: "&c另一个导入过程已经在运行,请等待完成,然后重试。"
export-already-running: "&c另一个导出过程已经在运行,请等待完成,然后重试。"
import-log-doesnt-exist: "&c错误: 文件&4{0}&c不存在。"
import-log-not-readable: "&c错误: 文件&4{0}&c不可读。"
import-log-failure: "&c从日志文件读取时发生意外错误。"
import-progress: "&b(导入) &b-> &f{0}&f%完成 &7- &b{1}&f/&b{2} &f完成 &c{3}&f错误。"
import-progress-sin: "&b(导入) &b-> &f{0}&f%完成 &7- &b{1}&f/&b{2} &f完成 &c{3}&f错误。"
import-start: "&b(导入) &b-> &f开始导入过程。"
import-end-complete: "&b(导入) &a&l已完成 &7- 用了 &b{0} &7秒 - &7没有错误。"
import-end-complete-err: "&b(导入) &a&l已完成 &7- 用了 &b{0} &7秒 - &c{1}错误。"
import-end-complete-err-sin: "&b(导入) &a&l已完成 &7- 用了 &b{0} &7秒 - &c{1}错误。"
import-end-error-header: >
{PREFIX}&b(导入) &7------------> &f显示错误 #&b{0} &7<------------\n
{PREFIX}&b(导入) &f执行: &3命令 #{1}\n
{PREFIX}&b(导入) &f命令: &7{2}\n
{PREFIX}&b(导入) &f类型: &3{3}\n
{PREFIX}&b(导入) &f输出:
import-end-error-content: "&b(导入) &b-> &c{0}"
import-end-error-footer: "&b(导入) &7<------------------------------------------>"
command-specs:
user:
description: "用户命令"
usage: "/%s user <用户>"
group:
description: "组命令"
usage: "/%s group <组>"
track:
description: "权限组轨道指令"
usage: "/%s track <权限组轨道>"
log:
description: "日志命令"
usage: "/%s log"
sync:
description: "与存储同步更改"
usage: "/%s sync"
info:
description: "打印通用插件信息"
usage: "/%s info"
verbose:
description: "管理详细权限检查"
usage: "/%s verbose <true|false> [filter]"
args:
"on|record|off|paste": "是否启用/禁用日志记录,或粘贴记录的输出"
"filter": "过滤器匹配项"
"--slim": "添加\"--slim\"从粘贴的输出中排除跟踪数据"
tree:
description: "生成权限的树视图"
usage: "/%s tree [selection] [max level] [player]"
args:
"selection": "树的根,指定\".\"包括所有权限"
"max level": "应该返回多少分支级别"
"player": "要检查的在线玩家的名称"
search:
description: "搜索具有特定权限的用户/组"
usage: "/%s search <permission>"
args:
"permission": "权限搜索"
"page": "页面查看"
check:
description: "对用户执行标准的权限检查"
usage: "/%s check <user> <permission>"
args:
"user": "检查用户"
"permission": "允许检查"
network-sync:
description: "与存储同步更改并请求网络上所有其他服务器都同步"
usage: "/%s networksync"
import:
description: "从文件导入数据"
usage: "/%s import <file>"
args:
"file": "要导入的文件"
export:
description: "将数据导出到文件"
usage: "/%s export <file>"
args:
"file": "要导出的文件"
reload-config:
description: "重新加载部分配置选项"
usage: "/%s reloadconfig"
bulk-update:
description: "对所有数据执行批量更改查询"
usage: "/%s bulkupdate"
args:
"data type": "正在更改的数据类型。('all'、'users'、'groups')"
"action": "对数据执行的操作。('update' or 'delete')"
"action field": "要采取行动的领域。只需要'update'。('permission'、'server'、'world')"
"action value": "替换的值。只需要'update'。"
"constraint...": "更新所需的限制"
migration:
description: "迁移命令"
usage: "/%s migration"
apply-edits:
description: "应用从Web编辑器进行的权限更改"
usage: "/%s applyedits <code> [target]"
args:
"code": "数据的唯一代码"
"target": "应用数据到谁"
create-group:
description: "创建新组"
usage: "/%s creategroup <group>"
args:
"name": "组的名称"
delete-group:
description: "删除组"
usage: "/%s deletegroup <group>"
args:
"name": "组的名称"
list-groups:
description: "列出平台上的所有组"
usage: "/%s listgroups"
create-track:
description: "创建新权限组轨道"
usage: "/%s createtrack <track>"
args:
"name": "权限组轨道的名称"
delete-track:
description: "删除权限组轨道"
usage: "/%s deletetrack <track>"
args:
"name": "权限组轨道的名称"
list-tracks:
description: "列出平台上的所有权限组轨道"
usage: "/%s listtracks"
user-info:
description: "显示有关用户的信息"
user-switchprimarygroup:
description: "切换用户的主组"
args:
"group": "组切换到"
user-promote:
description: "在权限组轨道提升用户"
args:
"track": "提升用户的权限组轨道"
"context...": "提升用户使用的环境"
user-demote:
description: "在权限组轨道降低用户"
args:
"track": "降低用户的权限组轨道"
"context...": "降低用户使用的环境"
group-info:
description: "提供有关组的信息"
group-listmembers:
description: "显示从该组继承的用户/组"
args:
"page": "页面查看"
group-setweight:
description: "设置组权重"
args:
"weight": "设定权重"
group-rename:
description: "重命名组"
args:
"name": "新名字"
group-clone:
description: "克隆组"
args:
"name": "要克隆到的组的名称"
holder-editor:
description: "打开网页权限编辑器"
holder-showtracks:
description: "列出对象所在的权限组轨道"
holder-clear:
description: "删除所有权限,父组和元"
args:
"context...": "过滤的环境"
permission:
description: "编辑权限"
parent:
description: "编辑继承"
meta:
description: "编辑元数据值"
permission-info:
description: "列出对象拥有的权限节点"
args:
"page": "页面查看"
"filter": "要过滤的字符串"
permission-set:
description: "设置对象的权限"
args:
"node": "要添加的权限"
"true|false": "节点的值"
"context...": "添加权限的环境"
permission-unset:
description: "取消对象的权限"
args:
"node": "取消的权限节点"
"context...": "删除权限的环境"
permission-settemp:
description: "设置对象的临时权限"
args:
"node": "要添加的权限"
"true|false": "节点的值"
"duration": "到期时间"
"context...": "权限的环境"
permission-unsettemp:
description: "取消对对象的临时权限"
args:
"node": "要取消的权限"
"context...": "权限的环境"
permission-check:
description: "检查对象是否具有某个权限节点"
args:
"node": "要检查的权限"
"context...": "权限的环境"
permission-check-inherits:
description: "检查对象是否继承某个权限节点"
args:
"node": "要检查的权限"
"context...": "权限的环境"
parent-info:
description: "列出此对象从哪里继承的组"
parent-set:
description: "删除对象已经继承的所有其他组,并将其添加到给定的对象组中。"
args:
"group": "组设为"
"context...": "设置组的环境"
parent-add:
description: "为该对象设置另一个组来继承权限"
args:
"group": "要继承的组"
"context...": "继承该组的环境"
parent-remove:
description: "删除先前设置的继承规则"
args:
"group": "删除的组"
"context...": "删除组的环境"
parent-set-track:
description: "删除对象在给定权限组轨道上继承的所有其他组,并将其添加到给定的权限组轨道上。"
args:
"track": "设置的权限组轨道"
"group": "组中设置的组,或与组中在给定权限组轨道上的位置相关的数字"
"context...": "设置组的环境"
parent-add-temp:
description: "为对象设置另一个组,以便从临时继承权限"
args:
"group": "要继承的组"
"duration": "持续时间"
"context...": "继承该组的环境"
parent-remove-temp:
description: "删除以前设置的临时继承规则"
args:
"group": "组删除"
"context...": "删除组的环境"
parent-clear:
description: "清除所有的父组"
args:
"context...": "过滤的环境"
parent-clear-track:
description: "清除给定权限组轨道上的所有父组"
args:
"track": "删除的权限组轨道"
"contexts...": "选择的环境"
meta-info:
description: "显示所有聊天元"
meta-set:
description: "设置元值"
args:
"key": "设定的键"
"value": "要设置的值"
"context...": "将元添加到"
meta-unset:
description: "取消设定元值"
args:
"key": "要取消的键"
"context...": "删除元的环境"
meta-settemp:
description: "暂时设置一个元值"
args:
"key": "设定的键"
"value": "要设置的值"
"duration": "持续时间"
"context...": "将元组添加到"
meta-unsettemp:
description: "取消设置临时元值"
args:
"key": "要取消的键"
"context...": "删除元的环境"
meta-addprefix:
description: "添加前缀"
args:
"priority": "添加的前缀优先级"
"prefix": "前缀字符串"
"context...": "添加前缀的环境"
meta-addsuffix:
description: "添加后缀"
args:
"priority": "添加的后缀优先级"
"suffix": "后缀字符串"
"context...": "添加后缀的环境"
meta-removeprefix:
description: "删除前缀"
args:
"priority": "删除的前缀优先级"
"prefix": "前缀字符串"
"context...": "删除前缀的环境"
meta-removesuffix:
description: "删除后缀"
args:
"priority": "删除的后缀优先级"
"suffix": "后缀字符串"
"context...": "删除后缀的环境"
meta-addtemp-prefix:
description: "添加临时前缀"
args:
"priority": "删除的前缀优先级"
"prefix": "前缀字符串"
"duration": "持续时间"
"context...": "添加前缀的环境"
meta-addtemp-suffix:
description: "添加临时后缀"
args:
"priority": "添加的后缀优先级"
"suffix": "后缀字符串"
"duration": "持续时间"
"context...": "添加后缀的环境"
meta-removetemp-prefix:
description: "删除临时前缀"
args:
"priority": "删除的前缀优先级"
"prefix": "前缀字符串"
"context...": "删除前缀的环境"
meta-removetemp-suffix:
description: "删除临时后缀"
args:
"priority": "删除的后缀优先级"
"suffix": "后缀字符串"
"context...": "删除后缀的环境"
meta-clear:
description: "清除所有元"
args:
"type": "要删除的元数据类型"
"context...": "过滤的环境"
track-info:
description: "提供有关权限组轨道的信息"
track-append:
description: "追加一个组到权限组轨道的最后"
args:
"group": "追加的组"
track-insert:
description: "在权限组轨道上的给定位置插入一个组"
args:
"group": "插入的组"
"position": "插入组的位置(权限组轨道上的第一个位置为1)"
track-remove:
description: "从权限组轨道中删除一个组"
args:
"group": "删除的组"
track-clear:
description: "清除权限组轨道上的组"
track-rename:
description: "重命名权限组轨道"
args:
"name": "新名字"
track-clone:
description: "克隆权限组轨道"
args:
"name": "要克隆的权限组轨道的名称"
log-recent:
description: "查看最近的登陆"
args:
"user": "用户的名称/uuid"
"page": "要查看的页码"
log-search:
description: "搜索日志中的条目"
args:
"query": "搜索的查询"
"page": "要查看的页码"
log-notify:
description: "切换日志通知"
args:
"on|off": "是否打开或关闭"
log-user-history:
description: "查看用户的历史记录"
args:
"user": "用户名/ uuid"
"page": "要查看的页码"
log-group-history:
description: "查看组的历史"
args:
"group": "组名"
"page": "要查看的页码"
log-track-history:
description: "查看权限组轨道的历史"
args:
"track": "权限组轨道的名称"
"page": "要查看的页码"
sponge:
description: "编辑额外的Sponge数据"
usage: "/%s sponge <collection> <subject>"
args:
"collection": "要查询的集合"
"subject": "修改的主题"
sponge-permission-info:
description: "显示有关主题权限的信息"
args:
"contexts...": "选择的环境"
sponge-permission-set:
description: "设置一个主题的权限"
args:
"node": "要设置的权限节点"
"tristate": "设置允许值"
"contexts...": "选择的环境"
sponge-permission-clear:
description: "清除一个主题的权限"
args:
"contexts...": "清除的环境"
sponge-parent-info:
description: "显示关于该主题父项的信息"
args:
"contexts...": "选择的环境"
sponge-parent-add:
description: "向主题添加父项"
args:
"collection": "父主题的主题集合"
"subject": "父主题的名称"
"contexts...": "添加父项的环境"
sponge-parent-remove:
description: "从主题中删除父项"
args:
"collection": "父主题的主题集合"
"subject": "父主题的名称"
"contexts...": "删除父项的环境"
sponge-parent-clear:
description: "清除主题父项"
args:
"contexts...": "清除父项的主题"
sponge-option-info:
description: "显示有关主题选项的信息"
args:
"contexts...": "选择的环境"
sponge-option-set:
description: "为主题设置一个选项"
args:
"key": "设定键"
"value": "设置的键"
"contexts...": "设置选项的环境"
sponge-option-unset:
description: "取消主题一个选项"
args:
"key": "取消的键"
"contexts...": "取消选项的环境"
sponge-option-clear:
description: "清除对象的选择"
args:
"contexts...": "清除选项的环境"
migration-command:
description: "迁移命令"
migration-groupmanager:
description: "迁移命令"
args:
"migrate as global": "如果世界权限应该被忽略,并且刚刚迁移为全局"
migration-powerfulperms:
description: "迁移命令"
args:
"address": "PP数据库的地址"
"database": "PP数据库的名称"
"username": "登录DB的用户名"
"password": "登录DB的用密码"
"db table": "存储用户数据的PP表的名称"
+3 -2
View File
@@ -1,6 +1,7 @@
MIT License
The MIT License.
Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
Copyright (c) lucko (Luck) <luck@lucko.me>
Copyright (c) contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+28 -9
View File
@@ -11,34 +11,53 @@ LuckPerms also includes an extensive API for developers, and support for a varie
## Useful Links
* **Development Builds** - <https://ci.lucko.me/job/LuckPerms>
* **Javadocs** - <https://jd.lucko.me/LuckPerms>
* **Javadocs** - <https://luckperms.lucko.me/javadocs/>
* **Wiki** - <https://github.com/lucko/LuckPerms/wiki>
## Building
LuckPerms uses Maven to handle dependencies.
#### Requirements
* Java 8 JDK
* Maven 3.3.x (the older versions will not work.)
* Git
#### Then run
```sh
git clone https://github.com/lucko/LuckPerms.git
cd LuckPerms/
mvn clean package
```
You can find the output jars in the `target` directories.
## Contributing
In order to contribute to and make changes to the plugin, you will need the dependencies listed above, plus the [Lombok plugin](https://projectlombok.org/download.html) for your IDE.
#### Pull Requests
If you make any changes or improvements to the plugin which you think would be beneficial upstream, please consider making a Pull Request to merge your changes back into the upstream project. (especially if your changes are bug fixes!)
LuckPerms loosely follows the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html), with a few minor changes here and there. Generally, try to copy the style of code found in the class you're editing.
#### Project Layout
The project is split up into 5 seperate modules.
* **API** - The public facing API classes used by other plugins when integrating with LuckPerms. This is basically just a collection of interfaces implemented inside of the plugin.
* **Common** - This includes all of the classes shared by separate implementations of LuckPerms. This is where the bulk of the project is.
* **Bukkit, BungeeCord & Sponge** - Includes all of the platform specific classes used to implement LuckPerms on each of the 3 supported platforms.
## License
LuckPerms is licensed under the permissive MIT license. Please see [`LICENSE.txt`](https://github.com/lucko/LuckPerms/blob/master/LICENSE.txt) for more info.
## LuckPerms vs Other Plugins
## Why use LuckPerms?
The main features of LuckPerms are detailed on this wiki page: [Why LuckPerms?](https://github.com/lucko/LuckPerms/wiki/Why-LuckPerms)
### 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 let me know._
See [here](https://luckperms.lucko.me/assets/bukkit-compare.png) for a comparison chart showing the differences between LuckPerms and other permission plugins for Bukkit.
### 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 let me know._
LuckPerms is currently the only maintained and 'working' permissions plugin for Sponge servers.
## Thanks
[![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").
+81 -2
View File
@@ -5,17 +5,28 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>3.0-SNAPSHOT</version>
<version>4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms-api</artifactId>
<build>
<finalName>LuckPerms-API-${release.version}</finalName>
<resources>
<resource>
<directory>../</directory>
<filtering>false</filtering>
<includes>
<include>LICENSE.txt</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>${compiler.version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -24,6 +35,68 @@
</plugins>
</build>
<profiles>
<profile>
<id>deployment</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.4</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<links>
<link>https://google.github.io/guava/releases/21.0/api/docs/</link>
</links>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>sign</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${gpg.version}</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<!-- guava -->
<dependency>
@@ -32,6 +105,12 @@
<version>19.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,46 +29,62 @@ import me.lucko.luckperms.api.LuckPermsApi;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Singleton for the {@link LuckPermsApi}.
* Provides static access to 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.
* <p>Ideally, the ServiceManager for the platform should be used to obtain an instance,
* however, this provider can be used if you need static access.</p>
*/
public final class LuckPerms {
private static LuckPermsApi api = null;
private static LuckPermsApi instance = null;
/**
* Gets an instance of {@link LuckPermsApi}, throwing {@link IllegalStateException} if the API is not loaded.
* Gets an instance of the {@link LuckPermsApi},
* throwing {@link IllegalStateException} if an instance is not yet loaded.
*
* <p>Will never return null.</p>
*
* @return an api instance
* @throws IllegalStateException if the api is not loaded
*/
@Nonnull
public static LuckPermsApi getApi() {
if (api == null) {
if (instance == null) {
throw new IllegalStateException("API is not loaded.");
}
return api;
return instance;
}
/**
* Gets an instance of {@link LuckPermsApi} safely.
* Gets an instance of {@link LuckPermsApi}, if it is loaded.
*
* <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}.
* <p>Unlike {@link LuckPerms#getApi}, this method will not throw an
* {@link IllegalStateException} if an instance is not yet loaded, rather return
* an empty {@link Optional}.
*
* @return an optional api instance
*/
@Nonnull
public static Optional<LuckPermsApi> getApiSafe() {
return Optional.ofNullable(api);
return Optional.ofNullable(instance);
}
static void registerProvider(LuckPermsApi luckPermsApi) {
api = luckPermsApi;
/**
* Registers an instance of the {@link LuckPermsApi} with this provider.
*
* @param instance the instance
*/
static void registerProvider(LuckPermsApi instance) {
LuckPerms.instance = instance;
}
/**
* Removes the current instance from this provider.
*/
static void unregisterProvider() {
api = null;
LuckPerms.instance = null;
}
private LuckPerms() {
@@ -0,0 +1,126 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.base.Preconditions;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Represents a type of chat meta
*
* @since 3.2
*/
public enum ChatMetaType {
/**
* Represents a prefix
*/
PREFIX {
@Override
public boolean matches(Node node) {
return Preconditions.checkNotNull(node, "node").isPrefix();
}
@Override
public boolean shouldIgnore(Node node) {
return !Preconditions.checkNotNull(node, "node").isPrefix();
}
@Override
public Map.Entry<Integer, String> getEntry(Node node) {
return Preconditions.checkNotNull(node, "node").getPrefix();
}
},
/**
* Represents a suffix
*/
SUFFIX {
@Override
public boolean matches(Node node) {
return Preconditions.checkNotNull(node, "node").isSuffix();
}
@Override
public boolean shouldIgnore(Node node) {
return !Preconditions.checkNotNull(node, "node").isSuffix();
}
@Override
public Map.Entry<Integer, String> getEntry(Node node) {
return Preconditions.checkNotNull(node, "node").getSuffix();
}
};
/**
* Returns if the passed node matches the type
*
* @param node the node to test
* @return true if the node has the same type
*/
public abstract boolean matches(@Nonnull Node node);
/**
* Returns if the passed node should be ignored when searching for meta of this type
*
* @param node the node to test
* @return true if the node does not share the same type
*/
public abstract boolean shouldIgnore(@Nonnull Node node);
/**
* Maps the corresponding entry from the given node
*
* @param node the node to retrieve the entry from
* @return the entry
* @throws IllegalStateException if the node does not share the same type
*/
@Nonnull
public abstract Map.Entry<Integer, String> getEntry(@Nonnull Node node);
/**
* Parses a ChatMetaType from the given node.
*
* @param node the node
* @return the parsed chat meta type
* @since 3.4
*/
@Nonnull
public static Optional<ChatMetaType> ofNode(@Nonnull Node node) {
if (node.isPrefix()) {
return Optional.of(PREFIX);
} else if (node.isSuffix()) {
return Optional.of(SUFFIX);
} else {
return Optional.empty();
}
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,41 +25,80 @@
package me.lucko.luckperms.api;
import com.google.common.base.Preconditions;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import javax.annotation.Nonnull;
/**
* Context and options for a permission lookup.
* Encapsulates the options and settings for a permission lookup.
*
* <p> All values are immutable.
* <p>This class is immutable.</p>
*
* @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
* The context key used to denote the subjects server
*/
public static final String SERVER_KEY = "server";
/**
* The context key used to denote the subjects world
*/
public static final String WORLD_KEY = "world";
/**
* A 'global' or default contexts instance.
*
* Simply passes an empty context set, with all accumulation settings set to true.
*/
private static final Contexts GLOBAL = new Contexts(
ContextSet.empty(),
true,
true,
true,
true,
true,
false
);
/**
* Gets the {@link FullySatisfiedContexts} instance.
*
* @return a context that will satisfy all contextual requirements.
*/
@Nonnull
public static Contexts allowAll() {
return ALLOW_ALL;
return FullySatisfiedContexts.getInstance();
}
public static Contexts of(ContextSet context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
/**
* A contexts instance with no defined context.
*
* @return the global contexts
* @since 3.3
*/
@Nonnull
public static Contexts global() {
return GLOBAL;
}
public static Contexts of(@Nonnull ContextSet 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;
private final ImmutableContextSet context;
/**
* The mode to parse defaults on Bukkit
* If the target subject is OP. This is used to parse defaults on Bukkit,
* and is ignored on all other platforms.
*
* @since 2.12
*/
@@ -87,18 +129,18 @@ public class Contexts {
*/
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");
}
// cache hashcode - this class is immutable, and is used as an index in the permission cache.
private final int hashCode;
this.context = context.makeImmutable();
public Contexts(@Nonnull ContextSet context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups, boolean op) {
this.context = Preconditions.checkNotNull(context, "context").makeImmutable();
this.includeGlobal = includeGlobal;
this.includeGlobalWorld = includeGlobalWorld;
this.applyGroups = applyGroups;
this.applyGlobalGroups = applyGlobalGroups;
this.applyGlobalWorldGroups = applyGlobalWorldGroups;
this.op = op;
this.hashCode = calculateHashCode();
}
/**
@@ -107,12 +149,14 @@ public class Contexts {
* @return an immutable set of context key value pairs
* @since 2.13
*/
@Nonnull
public ContextSet getContexts() {
return this.context;
}
/**
* Gets if OP defaults should be included
* Gets if the target subject is OP. This is used to parse defaults on Bukkit,
* and is ignored on all other platforms.
*
* @return true if op defaults should be included
*/
@@ -165,6 +209,8 @@ public class Contexts {
return this.applyGlobalWorldGroups;
}
@Override
@Nonnull
public String toString() {
return "Contexts(" +
"context=" + this.getContexts() + ", " +
@@ -177,44 +223,27 @@ public class Contexts {
")";
}
/*
* Ugly auto-generated lombok code
*/
/**
* Check for equality
*
* @param o the other
* @return true if equal
* @since 2.12
*/
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o == allowAll()) return false;
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;
return this.getContexts().equals(other.getContexts()) &&
this.isOp() == other.isOp() &&
this.isIncludeGlobal() == other.isIncludeGlobal() &&
this.isIncludeGlobalWorld() == other.isIncludeGlobalWorld() &&
this.isApplyGroups() == other.isApplyGroups() &&
this.isApplyGlobalGroups() == other.isApplyGlobalGroups() &&
this.isApplyGlobalWorldGroups() == other.isApplyGlobalWorldGroups();
}
/**
* Gets the hashcode
*
* @return the hashcode
* @since 2.12
*/
public int hashCode() {
private int calculateHashCode() {
final int PRIME = 59;
int result = 1;
final Object $context = this.getContexts();
result = result * PRIME + ($context == null ? 43 : $context.hashCode());
final Object contexts = this.getContexts();
result = result * PRIME + contexts.hashCode();
result = result * PRIME + (this.isOp() ? 79 : 97);
result = result * PRIME + (this.isIncludeGlobal() ? 79 : 97);
result = result * PRIME + (this.isIncludeGlobalWorld() ? 79 : 97);
@@ -224,4 +253,8 @@ public class Contexts {
return result;
}
@Override
public int hashCode() {
return hashCode;
}
}
@@ -0,0 +1,88 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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;
/**
* Represents the result of a mutation call.
*/
public enum DataMutateResult {
/**
* Indicates the mutation was a success
*/
SUCCESS(true),
/**
* Indicates the mutation failed because the subject already has something
*/
ALREADY_HAS(false),
/**
* Indicates the mutation failed because the subject lacks something
*/
LACKS(false),
/**
* Indicates the mutation failed
*/
FAIL(false);
private final boolean value;
DataMutateResult(boolean value) {
this.value = value;
}
/**
* Gets a boolean representation of the result.
*
* @return a boolean representation
*/
public boolean asBoolean() {
return value;
}
/**
* Gets if the result indicates a success
*
* @return if the result indicates a success
* @since 3.4
*/
public boolean wasSuccess() {
return value;
}
/**
* Gets if the result indicates a failure
*
* @return if the result indicates a failure
* @since 3.4
*/
public boolean wasFailure() {
return !value;
}
}
@@ -0,0 +1,81 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.caching.MetaContexts;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.api.context.ContextSet;
import javax.annotation.Nonnull;
/**
* A special instance of {@link Contexts}, which when passed to:
*
* <p></p>
* <ul>
* <li>{@link UserData#getPermissionData(Contexts)}</li>
* <li>{@link UserData#getMetaData(Contexts)}</li>
* <li>{@link UserData#getMetaData(MetaContexts)}</li>
* </ul>
*
* <p>... will always satisfy all contextual requirements.</p>
*
* <p>This effectively allows you to do lookups which ignore context.</p>
*
* @since 3.3
*/
public final class FullySatisfiedContexts extends Contexts {
// singleton
private static final FullySatisfiedContexts INSTANCE = new FullySatisfiedContexts();
@Nonnull
public static Contexts getInstance() {
return INSTANCE;
}
private FullySatisfiedContexts() {
super(ContextSet.empty(), true, true, true, true, true, false);
}
@Nonnull
@Override
public String toString() {
return "FullySatisfiedContexts()";
}
@Override
public boolean equals(Object o) {
// this class is a singleton, so we can use object comparison to check equality.
return o == this;
}
@Override
public int hashCode() {
// just use the system hashcode - we need to override the hashcode impl in super
return System.identityHashCode(this);
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,14 +25,14 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.List;
import java.util.OptionalInt;
import javax.annotation.Nonnull;
/**
* A group which holds permission data.
* An inheritable holder of permission data.
*/
public interface Group extends PermissionHolder {
@@ -38,225 +41,38 @@ public interface Group extends PermissionHolder {
*
* @return the name of the group
*/
@Nonnull
String getName();
/**
* Check to see if a group inherits a group
* Check to see if a group inherits another group directly
*
* @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 IllegalStateException if the group instance was not obtained from LuckPerms.
*/
boolean inheritsGroup(Group group);
boolean inheritsGroup(@Nonnull Group group);
/**
* Check to see if the group inherits a group on a specific server
* Check to see if a group inherits another group directly
*
* @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 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 server The server 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 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.
*/
void setInheritGroup(Group group) throws ObjectAlreadyHasException;
/**
* Make this group inherit another group on a specific server
*
* @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
*/
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 server The server 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
*/
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 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
*/
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 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
*/
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 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
*/
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
* @param group The group to check membership of
* @param contextSet the context set to filter by
* @return true if the group inherits the other group
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @since 3.2
*/
void unsetInheritGroup(Group group) throws ObjectLacksException;
boolean inheritsGroup(@Nonnull Group group, @Nonnull ContextSet contextSet);
/**
* Remove a previously set inheritance rule
*
* @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 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 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 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 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.
* @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 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 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 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 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;
/**
* 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
* @return a {@link List} of group names
* @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);
/**
* Gets the weight of this group, is present.
* Gets the weight of this group, if present.
*
* @return the group weight
* @since 2.17
*/
@Nonnull
OptionalInt getWeight();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,13 +25,15 @@
package me.lucko.luckperms.api;
import com.google.common.collect.Multimap;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.Optional;
import java.util.OptionalLong;
import javax.annotation.Nonnull;
/**
* A relationship between a Holder and a permission
* A relationship between a PermissionHolder and a permission
*
* @param <T> the identifier type of the holder
* @since 2.17
@@ -40,6 +45,7 @@ public interface HeldPermission<T> {
*
* @return the holder
*/
@Nonnull
T getHolder();
/**
@@ -47,6 +53,7 @@ public interface HeldPermission<T> {
*
* @return the permission
*/
@Nonnull
String getPermission();
/**
@@ -61,6 +68,7 @@ public interface HeldPermission<T> {
*
* @return the server
*/
@Nonnull
Optional<String> getServer();
/**
@@ -68,6 +76,7 @@ public interface HeldPermission<T> {
*
* @return the world
*/
@Nonnull
Optional<String> getWorld();
/**
@@ -78,17 +87,18 @@ public interface HeldPermission<T> {
OptionalLong getExpiry();
/**
* Gets the context for the permission.
* Gets the extra context for the permission.
*
* @return the context
* @return the extra context
*/
Multimap<String, String> getContext();
ContextSet getContexts();
/**
* Converts this permission into a Node
*
* @return a Node copy of this permission
*/
@Nonnull
Node asNode();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,145 +25,98 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.data.DatastoreConfiguration;
import java.util.Map;
import javax.annotation.Nonnull;
/**
* Read-only access to the LuckPerms configuration settings
* Wrapper around parts of the LuckPerms configuration file
*/
public interface LPConfiguration {
/**
* Returns the name of this server
* Gets the name of this server
*
* @return the name of this server
*/
@Nonnull
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 if the users on this server will have their global permissions applied
* Gets 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
* Gets 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
* Gets 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
* Gets if the platform is applying global world groups
*
* @return true if the platform is applying global world groups
* @since 2.9
*/
boolean getApplyGlobalWorldGroups();
/**
* 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 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 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
* Gets the storage method string from the configuration
*
* @return the storage method string from the configuration
*/
@Nonnull
String getStorageMethod();
/**
* Returns true if split storage is enabled
* Gets true if split storage is enabled
*
* @return true if split storage is enabled
* @since 2.7
*/
boolean getSplitStorage();
/**
* Returns a map of split storage options
* Gets 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
*/
@Nonnull
Map<String, String> getSplitStorageOptions();
@Nonnull
Unsafe unsafe();
interface Unsafe {
/**
* Gets an Object from the config.
*
* <p>This method is nested under {@link Unsafe} because the keys
* and return types may change between versions without warning.</p>
*
* @param key the key, as defined as a field name in
* the "ConfigKeys" class.
* @return the corresponding object, if one is present
* @throws IllegalArgumentException if the key isn't known
*/
@Nonnull
Object getObject(String key);
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +25,8 @@
package me.lucko.luckperms.api;
import javax.annotation.Nonnull;
/**
* A node with a traceable origin
*
@@ -34,6 +39,7 @@ public interface LocalizedNode extends Node {
*
* @return the node this instance is representing
*/
@Nonnull
Node getNode();
/**
@@ -42,6 +48,7 @@ public interface LocalizedNode extends Node {
* @return where the node was inherited from. Will not return null.
* @see PermissionHolder#getObjectName()
*/
@Nonnull
String getLocation();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,22 +29,34 @@ import java.util.SortedMap;
import java.util.SortedSet;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Represents the internal LuckPerms log.
*
* <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.
* <p>The returned instance provides a copy of the data at the time of retrieval.</p>
*
* <p>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.</p>
*
* <p>All methods are thread safe, and return immutable and thread safe collections.</p>
*/
public interface Log {
/**
* Get the content of the log
*
* @return a {@link SortedSet} of all of the {@link LogEntry} objects in this {@link Log}
*/
@Nonnull
SortedSet<LogEntry> getContent();
/**
* Get the content of the log
*
* @return all content in this log
*/
@Nonnull
SortedSet<LogEntry> getRecent();
/**
@@ -50,120 +65,150 @@ public interface Log {
* @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()}}
* @throws IllegalStateException if the log doesn't contain enough entries to populate the page. See {@link #getRecentMaxPages()}
*/
@Nonnull
SortedMap<Integer, LogEntry> getRecent(int pageNo);
/**
* Gets the max page number for the recent pages.
*
* @return the max page number allowed in the {@link #getRecent(int)} method
*/
int getRecentMaxPages();
/**
* Gets the recent content for a given actor
*
* @param actor the uuid of the actor to filter by
* @return all content in this log where is actor = uuid
*/
SortedSet<LogEntry> getRecent(UUID actor);
@Nonnull
SortedSet<LogEntry> getRecent(@Nonnull UUID actor);
/**
* Gets the recent content for the uuid, separated into pages
* Gets the recent content for a given actor, separated by page
*
* @param pageNo the page number
* @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)}}
* @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);
@Nonnull
SortedMap<Integer, LogEntry> getRecent(int pageNo, @Nonnull UUID actor);
/**
* Gets the max page number for the recent pages.
*
* @param actor the actor to filter by
* @return the max page number allowed in the {@link #getRecent(int, UUID)} method
*/
int getRecentMaxPages(UUID actor);
int getRecentMaxPages(@Nonnull UUID actor);
/**
* Gets the log content for a given user
*
* @param uuid the uuid to filter by
* @return all content in this log where the user = uuid
*/
SortedSet<LogEntry> getUserHistory(UUID uuid);
@Nonnull
SortedSet<LogEntry> getUserHistory(@Nonnull UUID uuid);
/**
* Gets the user history content, separated by pages
* Gets the log content for a given user, separated by page
*
* @param pageNo the page number
* @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)}}
* @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);
@Nonnull
SortedMap<Integer, LogEntry> getUserHistory(int pageNo, @Nonnull UUID uuid);
/**
* Gets the max page number for the user history pages.
*
* @param uuid the uuid to filter by
* @return the max page number allowed in the {@link #getUserHistory(int, UUID)} method
*/
int getUserHistoryMaxPages(UUID uuid);
int getUserHistoryMaxPages(@Nonnull UUID uuid);
/**
* Gets the log content for a given group
*
* @param name the name to filter by
* @return all content in this log where the group = name
*/
SortedSet<LogEntry> getGroupHistory(String name);
@Nonnull
SortedSet<LogEntry> getGroupHistory(@Nonnull String name);
/**
* Gets the group history content, separated by pages
* Gets the log content for a given group, separated by page
*
* @param pageNo the page number
* @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)}}
* @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);
@Nonnull
SortedMap<Integer, LogEntry> getGroupHistory(int pageNo, @Nonnull String name);
/**
* Gets the max page number for the group history pages.
*
* @param name the name to filter by
* @return the max page number allowed in the {@link #getGroupHistory(int, String)} method
*/
int getGroupHistoryMaxPages(String name);
int getGroupHistoryMaxPages(@Nonnull String name);
/**
* Gets the log content for a given track
*
* @param name the name to filter by
* @return all content in this log where the track = name
*/
SortedSet<LogEntry> getTrackHistory(String name);
@Nonnull
SortedSet<LogEntry> getTrackHistory(@Nonnull String name);
/**
* Gets the track history content, separated by pages
* Gets the log content for a given track, separated by page
*
* @param pageNo the page number
* @param name the name of the acted track 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 #getTrackHistoryMaxPages(String)}
*/
SortedMap<Integer, LogEntry> getTrackHistory(int pageNo, String name);
@Nonnull
SortedMap<Integer, LogEntry> getTrackHistory(int pageNo, @Nonnull String name);
/**
* Gets the max page number for the track history pages.
*
* @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);
int getTrackHistoryMaxPages(@Nonnull String name);
/**
* Gets the log content for a given search query
*
* @param query the query to filter by
* @return all content in this log where the content matches query
*/
SortedSet<LogEntry> getSearch(String query);
@Nonnull
SortedSet<LogEntry> getSearch(@Nonnull String query);
/**
* Gets the search content, separated by pages
* Gets the log content for a given search query, separated by page
*
* @param pageNo the page number
* @param query the query to filter by
@@ -171,11 +216,15 @@ public interface Log {
* @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);
@Nonnull
SortedMap<Integer, LogEntry> getSearch(int pageNo, @Nonnull String query);
/**
* Gets the max page number for the search query pages.
*
* @param query the query to filter by
* @return the max page number allowed in the {@link #getSearch(int, String)} method
*/
int getSearchMaxPages(String query);
int getSearchMaxPages(@Nonnull String query);
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,297 +25,139 @@
package me.lucko.luckperms.api;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* A single entry in the log
*
* <p> Implements {@link Comparable} ordering based upon the timestamp of the entry.
* Represents a logged action.
*/
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 interface LogEntry extends Comparable<LogEntry> {
public static LogEntryBuilder builder() {
return new LogEntryBuilder();
}
/**
* Gets the time in unix seconds when the action occurred.
*
* @return the timestamp
*/
long getTimestamp();
private long timestamp;
private UUID actor;
private String actorName;
private char type;
private UUID acted;
private String actedName;
private String action;
/**
* Gets the id of the object which performed the action.
*
* <p>This is the players uuid in most cases.</p>
*
* @return the actor id
*/
UUID getActor();
public LogEntry(long timestamp, UUID actor, String actorName, char type, UUID acted, String actedName, String action) {
if (actor == null) {
throw new NullPointerException("actor");
}
if (actorName == null) {
throw new NullPointerException("actorName");
}
if (actedName == null) {
throw new NullPointerException("actedName");
}
if (action == null) {
throw new NullPointerException("action");
/**
* Gets the name describing the actor.
*
* @return the name of the actor
*/
String getActorName();
/**
* Gets the type of action.
*
* @return the action type
*/
Type getType();
/**
* Gets the uuid of the object which was acted upon.
*
* <p>Will only return a value for {@link Type#USER} entries.</p>
*
* @return the uuid of acted object
*/
Optional<UUID> getActed();
/**
* Gets the name describing the object which was acted upon
*
* @return the name of the acted object
*/
String getActedName();
/**
* Returns a string describing the action which took place.
*
* <p>In most instances, this returns a variation of the command string which caused
* the change.</p>
*
* @return the action
*/
String getAction();
/**
* Represents the type of a {@link LogEntry}.
*
* @since 3.3
*/
enum Type {
USER('U'),
GROUP('G'),
TRACK('T');
private final char code;
Type(char code) {
this.code = code;
}
this.timestamp = timestamp;
this.actor = actor;
this.actorName = actorName;
this.type = type;
this.acted = acted;
this.actedName = actedName;
this.action = action;
}
protected LogEntry() {
this.timestamp = 0L;
this.actor = null;
this.actorName = null;
this.type = Character.MIN_VALUE;
this.acted = null;
this.actedName = null;
this.action = null;
}
@Override
public int compareTo(LogEntry o) {
return equals(o) ? 0 : (Long.compare(timestamp, o.getTimestamp()) == 0 ? 1 : Long.compare(timestamp, o.getTimestamp()));
}
public boolean matchesSearch(String query) {
query = query.toLowerCase();
return actorName.toLowerCase().contains(query) || actedName.toLowerCase().contains(query)
|| action.toLowerCase().contains(query);
}
public String getFormatted() {
return String.format(FORMAT,
actorName,
Character.toString(type),
actedName,
action
);
}
public long getTimestamp() {
return timestamp;
}
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() +
")";
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof LogEntry)) return false;
final LogEntry other = (LogEntry) o;
if (this.getTimestamp() != other.getTimestamp()) 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;
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;
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();
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;
}
public static class LogEntryBuilder extends AbstractLogEntryBuilder<LogEntry, LogEntry.LogEntryBuilder> {
@Override
protected LogEntry createObj() {
return new LogEntry();
public char getCode() {
return this.code;
}
@Override
protected LogEntryBuilder getThis() {
return this;
}
}
public static abstract class AbstractLogEntryBuilder<T extends LogEntry, B extends AbstractLogEntryBuilder<T, B>> {
private T obj;
private B thisObj;
public AbstractLogEntryBuilder() {
obj = createObj();
thisObj = getThis();
}
protected abstract T createObj();
protected abstract B getThis();
public long getTimestamp() {
return obj.getTimestamp();
}
public UUID getActor() {
return obj.getActor();
}
public String getActorName() {
return obj.getActorName();
}
public char getType() {
return obj.getType();
}
public UUID getActed() {
return obj.getActed();
}
public String getActedName() {
return obj.getActedName();
}
public String getAction() {
return obj.getAction();
}
public B timestamp(long timestamp) {
obj.setTimestamp(timestamp);
return thisObj;
}
public B actor(UUID actor) {
obj.setActor(actor);
return thisObj;
}
public B actorName(String actorName) {
obj.setActorName(actorName);
return thisObj;
}
public B type(char type) {
obj.setType(type);
return thisObj;
}
public B acted(UUID acted) {
obj.setActed(acted);
return thisObj;
}
public B actedName(String actedName) {
obj.setActedName(actedName);
return thisObj;
}
public B action(String action) {
obj.setAction(action);
return thisObj;
}
public T build() {
if (getActor() == null) {
throw new NullPointerException("actor");
public static Type valueOf(char code) {
switch (code) {
case 'U':
case 'u':
return USER;
case 'G':
case 'g':
return GROUP;
case 'T':
case 't':
return TRACK;
default:
throw new IllegalArgumentException("Unknown code: " + code);
}
if (getActorName() == null) {
throw new NullPointerException("actorName");
}
if (getActedName() == null) {
throw new NullPointerException("actedName");
}
if (getAction() == null) {
throw new NullPointerException("action");
}
return obj;
}
}
/**
* Builds a LogEntry instance
*/
interface Builder {
@Nonnull
Builder setTimestamp(long timestamp);
@Nonnull
Builder setActor(@Nonnull UUID actor);
@Nonnull
Builder setActorName(@Nonnull String actorName);
@Nonnull
Builder setType(@Nonnull Type type);
@Nonnull
Builder setActed(@Nullable UUID acted);
@Nonnull
Builder setActedName(@Nonnull String actedName);
@Nonnull
Builder setAction(@Nonnull String action);
@Nonnull
LogEntry build();
@Override
public String toString() {
return "LogEntry.LogEntryBuilder(" +
"timestamp=" + this.getTimestamp() + ", " +
"actor=" + this.getActor() + ", " +
"actorName=" + this.getActorName() + ", " +
"type=" + this.getType() + ", " +
"acted=" + this.getActed() + ", " +
"actedName=" + this.getActedName() + ", " +
"action=" + this.getAction() +
")";
}
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,87 +25,150 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.LuckPerms;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.ContextManager;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.event.EventBus;
import me.lucko.luckperms.api.manager.GroupManager;
import me.lucko.luckperms.api.manager.TrackManager;
import me.lucko.luckperms.api.manager.UserManager;
import me.lucko.luckperms.api.metastacking.MetaStackFactory;
import me.lucko.luckperms.api.platform.PlatformInfo;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* The root API interface for LuckPerms
* The LuckPerms API.
*
* <p>This interface is the base of the entire API package. All API functions
* are accessed via this interface.</p>
*
* <p>An instance can be obtained via {@link LuckPerms#getApi()}, or the platforms
* Services Manager.</p>
*/
public interface LuckPermsApi {
/**
* Gets information about the platform LuckPerms is running on.
*
* @return the platform info
* @since 4.0
*/
@Nonnull
PlatformInfo getPlatformInfo();
/**
* Gets the user manager
*
* @return the user manager
* @since 4.0
*/
@Nonnull
UserManager getUserManager();
/**
* Gets the group manager
*
* @return the group manager
* @since 4.0
*/
@Nonnull
GroupManager getGroupManager();
/**
* Gets the track manager
*
* @return the track manager
* @since 4.0
*/
@Nonnull
TrackManager getTrackManager();
/**
* Schedules an update task to run
*
* @since 4.0
*/
void runUpdateTask();
/**
* Gets the API version
* @return the version of the API running on the platform
* @since 2.6
*/
double getApiVersion();
/**
* Gets the plugin version
* @return the version of the plugin running on the platform
*/
String getVersion();
/**
* Gets the platform LuckPerms is running on
* @return the platform LuckPerms is running on
* @since 2.7
*/
PlatformType getPlatformType();
@Nonnull
CompletableFuture<Void> runUpdateTask();
/**
* Gets the event bus, used for subscribing to events
*
* @return the event bus
* @since 3.0
*/
@Nonnull
EventBus getEventBus();
/**
* Gets a wrapped {@link LPConfiguration} instance, with read only access
* Gets the configuration
*
* @return a configuration instance
*/
@Nonnull
LPConfiguration getConfiguration();
/**
* Gets a wrapped {@link Storage} instance.
* Gets the backend storage dao
*
* @return a storage instance
* @since 2.14
*/
@Nonnull
Storage getStorage();
/**
* Gets the messaging service in use on the platform, if present.
* Gets the messaging service
*
* @return an optional that may contain a messaging service instance.
*/
@Nonnull
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
* Gets a {@link UuidCache} instance, providing read access to the LuckPerms
* internal uuid caching system
*
* @return a uuid cache instance
*/
@Nonnull
UuidCache getUuidCache();
/**
* Gets the context manager
*
* @return the context manager
* @since 4.0
*/
ContextManager getContextManager();
/**
* Gets the node factory
*
* @return the node factory
*/
@Nonnull
NodeFactory getNodeFactory();
/**
* Gets the MetaStackFactory
*
* @return the meta stack factory
* @since 3.2
*/
@Nonnull
MetaStackFactory getMetaStackFactory();
// convenience methods
/**
* Gets a wrapped user object from the user storage
*
@@ -110,16 +176,22 @@ public interface LuckPermsApi {
* @return a {@link User} object, if one matching the uuid is loaded, or null if not
* @throws NullPointerException if the uuid is null
*/
User getUser(UUID uuid);
@Nullable
default User getUser(@Nonnull UUID uuid) {
return getUserManager().getUser(uuid);
}
/**
* Gets a wrapped user object from the user storage. This method does not return null, unlike {@link #getUser(UUID)}
* Gets a wrapped user object from the user storage.
*
* @param uuid the uuid of the user to get
* @return an optional {@link User} object
* @throws NullPointerException if the uuid is null
*/
Optional<User> getUserSafe(UUID uuid);
@Nonnull
default Optional<User> getUserSafe(@Nonnull UUID uuid) {
return getUserManager().getUserOpt(uuid);
}
/**
* Gets a wrapped user object from the user storage
@@ -128,24 +200,32 @@ public interface LuckPermsApi {
* @return a {@link User} object, if one matching the uuid is loaded, or null if not
* @throws NullPointerException if the name is null
*/
User getUser(String name);
@Nullable
default User getUser(@Nonnull String name) {
return getUserManager().getUser(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.
*
* @param name the username of the user to get
* @return an optional {@link User} object
* @throws NullPointerException if the name is null
*/
Optional<User> getUserSafe(String name);
@Nonnull
default Optional<User> getUserSafe(@Nonnull String name) {
return getUserManager().getUserOpt(name);
}
/**
* Gets a set of all loaded users.
*
* @return a {@link Set} of {@link User} objects
*/
Set<User> getUsers();
@Nonnull
default Set<User> getUsers() {
return getUserManager().getLoadedUsers();
}
/**
* Check if a user is loaded in memory
@@ -154,16 +234,19 @@ public interface LuckPermsApi {
* @return true if the user is loaded
* @throws NullPointerException if the uuid is null
*/
boolean isUserLoaded(UUID uuid);
default boolean isUserLoaded(@Nonnull UUID uuid) {
return getUserManager().isLoaded(uuid);
}
/**
* 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
*/
void cleanupUser(User user);
default void cleanupUser(@Nonnull User user) {
getUserManager().cleanupUser(user);
}
/**
* Gets a wrapped group object from the group storage
@@ -172,23 +255,34 @@ public interface LuckPermsApi {
* @return a {@link Group} object, if one matching the name exists, or null if not
* @throws NullPointerException if the name is null
*/
Group getGroup(String name);
@Nullable
default Group getGroup(@Nonnull String name) {
return getGroupManager().getGroup(name);
}
/**
* Gets a wrapped group object from the group storage. This method does not return null, unlike {@link #getGroup}
* Gets a wrapped group object from the group storage.
*
* <p>This method does not return null, unlike {@link #getGroup}</p>
*
* @param name the name of the group to get
* @return an optional {@link Group} object
* @throws NullPointerException if the name is null
*/
Optional<Group> getGroupSafe(String name);
@Nonnull
default Optional<Group> getGroupSafe(@Nonnull String name) {
return getGroupManager().getGroupOpt(name);
}
/**
* Gets a set of all loaded groups.
*
* @return a {@link Set} of {@link Group} objects
*/
Set<Group> getGroups();
@Nonnull
default Set<Group> getGroups() {
return getGroupManager().getLoadedGroups();
}
/**
* Check if a group is loaded in memory
@@ -197,32 +291,46 @@ public interface LuckPermsApi {
* @return true if the group is loaded
* @throws NullPointerException if the name is null
*/
boolean isGroupLoaded(String name);
default boolean isGroupLoaded(@Nonnull String name) {
return getGroupManager().isLoaded(name);
}
/**
* 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
* @return a {@link Track} object, if one matching the name exists, or null
* if not
* @throws NullPointerException if the name is null
*/
Track getTrack(String name);
@Nullable
default Track getTrack(@Nonnull String name) {
return getTrackManager().getTrack(name);
}
/**
* Gets a wrapped track object from the track storage. This method does not return null, unlike {@link #getTrack}
* Gets a wrapped track object from the track storage.
*
* <p>This method does not return null, unlike {@link #getTrack}</p>
*
* @param name the name of the track to get
* @return an optional {@link Track} object
* @throws NullPointerException if the name is null
*/
Optional<Track> getTrackSafe(String name);
@Nonnull
default Optional<Track> getTrackSafe(@Nonnull String name) {
return getTrackManager().getTrackOpt(name);
}
/**
* Gets a set of all loaded tracks.
*
* @return a {@link Set} of {@link Track} objects
*/
Set<Track> getTracks();
@Nonnull
default Set<Track> getTracks() {
return getTrackManager().getLoadedTracks();
}
/**
* Check if a track is loaded in memory
@@ -231,14 +339,17 @@ public interface LuckPermsApi {
* @return true if the track is loaded
* @throws NullPointerException if the name is null
*/
boolean isTrackLoaded(String name);
default boolean isTrackLoaded(@Nonnull String name) {
return getTrackManager().isLoaded(name);
}
/**
* Gets the node factory instance for the platform
* Returns a new LogEntry Builder instance
*
* @return the node factory
* @return a new builder
* @since 4.0
*/
NodeFactory getNodeFactory();
LogEntry.Builder newLogEntryBuilder();
/**
* Returns a permission builder instance
@@ -249,15 +360,20 @@ public interface LuckPermsApi {
* @throws NullPointerException if the permission is null
* @since 2.6
*/
Node.Builder buildNode(String permission) throws IllegalArgumentException;
@Nonnull
default Node.Builder buildNode(@Nonnull String permission) throws IllegalArgumentException {
return getNodeFactory().newBuilder(permission);
}
/**
* 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.
* @param calculator 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(ContextCalculator<?> contextCalculator);
default void registerContextCalculator(@Nonnull ContextCalculator<?> calculator) {
getContextManager().registerCalculator(calculator);
}
/**
* Gets a calculated context instance for the user using the rules of the platform.
@@ -267,7 +383,10 @@ public interface LuckPermsApi {
* @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);
@Nonnull
default Optional<Contexts> getContextForUser(@Nonnull User user) {
return getContextManager().lookupApplicableContexts(user);
}
/**
* Gets set of contexts applicable to a player using the platforms {@link ContextCalculator}s.
@@ -276,6 +395,21 @@ public interface LuckPermsApi {
* @return a set of contexts.
* @since 2.17
*/
ContextSet getContextForPlayer(Object player);
@Nonnull
default ContextSet getContextForPlayer(@Nonnull Object player) {
return getContextManager().getApplicableContext(player);
}
/**
* Gets a Contexts instance for the player using the platforms {@link ContextCalculator}s.
*
* @param player the player to calculate for. Must be the player instance for the platform.
* @return a set of contexts.
* @since 3.3
*/
@Nonnull
default Contexts getContextsForPlayer(@Nonnull Object player) {
return getContextManager().getApplicableContexts(player);
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,8 +35,8 @@ 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.
* <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.</p>
*/
void pushUpdate();
@@ -1,340 +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;
import me.lucko.luckperms.LuckPerms;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A collection of utilities to help retrieve meta values for {@link PermissionHolder}s
*
* @since 2.7
*/
public class MetaUtils {
private static String escapeDelimiters(String s, String... delims) {
for (String delim : delims) {
s = s.replace(delim, "\\" + delim);
}
return s;
}
private static String unescapeDelimiters(String s, String... delims) {
for (String delim : delims) {
s = s.replace("\\" + delim, delim);
}
return s;
}
/**
* 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
*/
public static String escapeCharacters(String s) {
if (s == null) {
throw new NullPointerException();
}
return escapeDelimiters(s, ".", "/", "-", "$");
}
/**
* 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
*/
public static String unescapeCharacters(String s) {
if (s == null) {
throw new NullPointerException();
}
s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$");
s = unescapeDelimiters(s, ".", "/", "-", "$");
return s;
}
/**
* 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
* @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");
}
if (value == null) {
throw new NullPointerException("value");
}
if (node.equals("")) {
throw new IllegalArgumentException("node is empty");
}
if (value.equals("")) {
throw new IllegalArgumentException("value is empty");
}
if (server == null || server.equals("")) {
server = "global";
}
node = escapeCharacters(node);
value = escapeCharacters(value);
Set<Node> toRemove = new HashSet<>();
for (Node n : holder.getEnduringPermissions()) {
if (n.isMeta() && n.getMeta().getKey().equals(node)) {
toRemove.add(n);
}
}
for (Node n : toRemove) {
try {
holder.unsetPermission(n);
} catch (ObjectLacksException ignored) {
}
}
Node.Builder metaNode = LuckPerms.getApi().buildNode("meta." + node + "." + value).setValue(true);
if (!server.equalsIgnoreCase("global")) {
metaNode.setServer(server);
}
if (world != null && !world.equals("")) {
metaNode.setServer(server).setWorld(world);
}
try {
holder.setPermission(metaNode.build());
} 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 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 IllegalArgumentException if the node is empty
*/
public static String getMeta(PermissionHolder holder, String server, String world, String node, String defaultValue, boolean includeGlobal) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (server == null || server.equals("")) {
server = "global";
}
if (node == null) {
throw new NullPointerException("node");
}
if (node.equals("")) {
throw new IllegalArgumentException("node is empty");
}
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
if (!n.getValue()) {
continue;
}
if (!n.isMeta()) {
continue;
}
if (!server.equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(server, includeGlobal, false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
return unescapeCharacters(meta.getValue());
}
}
return defaultValue;
}
private static void setChatMeta(boolean prefix, PermissionHolder holder, String value, int priority, String server, String world) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (value == null || value.equals("")) {
throw new IllegalArgumentException("value is null/empty");
}
Node.Builder node = LuckPerms.getApi().buildNode(prefix ? "prefix" : "suffix" + "." + priority + "." + escapeCharacters(value));
node.setValue(true);
if (!server.equalsIgnoreCase("global")) {
node.setServer(server);
}
if (world != null && !world.equals("")) {
node.setServer(server).setWorld(world);
}
try {
holder.setPermission(node.build());
} 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 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
* @throws IllegalArgumentException if the prefix is null or empty
*/
public static void setPrefix(PermissionHolder holder, String prefix, int priority, String server, String world) {
setChatMeta(true, holder, prefix, priority, server, world);
}
/**
* 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 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
* @throws IllegalArgumentException if the suffix is null or empty
*/
public static void setSuffix(PermissionHolder holder, String suffix, int priority, String server, String world) {
setChatMeta(false, holder, suffix, priority, server, world);
}
private static String getChatMeta(boolean prefix, PermissionHolder holder, String server, String world, boolean includeGlobal) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (server == null) {
server = "global";
}
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes(Contexts.allowAll())) {
if (!n.getValue()) {
continue;
}
if (!server.equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(server, includeGlobal, false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
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);
}
/**
* 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 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
*/
public static String getPrefix(PermissionHolder holder, String server, String world, boolean includeGlobal) {
return getChatMeta(true, holder, server, world, includeGlobal);
}
/**
* 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 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
*/
public static String getSuffix(PermissionHolder holder, String server, String world, boolean includeGlobal) {
return getChatMeta(false, holder, server, world, includeGlobal);
}
private MetaUtils() {
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -30,41 +33,72 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
/**
* An immutable permission node
* Represents a permission node.
*
* <p> Use {@link LuckPermsApi#buildNode(String)} to get an instance.
* <p>All implementations of this interface must be immutable.</p>
*
* <p>Use the {@link NodeFactory} to obtain and construct instances.</p>
*
* @since 2.6
*/
public interface Node extends Map.Entry<String, Boolean> {
/**
* Gets the permission string
*
* @return the actual permission node
*/
@Nonnull
String getPermission();
/**
* Get what value the permission is set to. A negated node would return <code>false</code>.
* Gets the value.
*
* <p>A negated node would return a value of <code>false</code>.</p>
*
* @return the permission's value
*/
@Override
Boolean getValue();
@Nonnull
default Boolean getValue() {
return getValuePrimitive();
}
/**
* Gets the value.
*
* <p>A negated node would return a value of <code>false</code>.</p>
*
* @return the permission's value
*/
boolean getValuePrimitive();
/**
* Gets the value of this node as a {@link Tristate}
*
* @return the value of this node as a Tristate
*/
Tristate getTristate();
@Nonnull
default Tristate getTristate() {
return Tristate.fromBoolean(getValuePrimitive());
}
/**
* Gets if the node is negated
*
* @return true if the node is negated
*/
boolean isNegated();
default boolean isNegated() {
return !getValuePrimitive();
}
/**
* If this node is set to override explicitly.
* This value does not persist across saves, and is therefore only useful for transient nodes
* Gets if this node is set to override explicitly.
*
* <p>This value does not persist across saves, and is therefore only useful for transient nodes</p>
*
* @return true if this node is set to override explicitly
*/
@@ -75,6 +109,7 @@ public interface Node extends Map.Entry<String, Boolean> {
*
* @return an {@link Optional} containing the server, if one is defined
*/
@Nonnull
Optional<String> getServer();
/**
@@ -82,152 +117,144 @@ public interface Node extends Map.Entry<String, Boolean> {
*
* @return an {@link Optional} containing the world, if one is defined
*/
@Nonnull
Optional<String> getWorld();
/**
* Gets if this node is server specific
*
* @return true if this node is server specific
*/
boolean isServerSpecific();
/**
* Gets if this node is server specific
*
* @return true if this node is server specific
*/
boolean isWorldSpecific();
/**
* If this node should apply on a specific server
* Gets if this node applies globally, and therefore has no specific context
*
* @param server the name of the server
* @param includeGlobal if global permissions should apply
* @param applyRegex if regex should be applied
* @return true if the node should apply
* @return true if this node applies globally, and has no specific context
* @since 3.1
*/
boolean shouldApplyOnServer(String server, boolean includeGlobal, boolean applyRegex);
boolean appliesGlobally();
/**
* If this node should apply on a specific world
* Gets if this node has any specific context in order for it to apply
*
* @param world the name of the world
* @param includeGlobal if global permissions should apply
* @param applyRegex if regex should be applied
* @return true if the node should apply
* @return true if this node has specific context
* @since 3.1
*/
boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex);
boolean hasSpecificContext();
/**
* 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
* Gets 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(ContextSet context);
/**
* Similar to {@link #shouldApplyOnServer(String, boolean, boolean)}, except this method accepts a List
*
* @param servers the list of servers
* @param includeGlobal if global permissions should apply
* @return true if the node should apply
*/
boolean shouldApplyOnAnyServers(List<String> servers, boolean includeGlobal);
/**
* Similar to {@link #shouldApplyOnWorld(String, boolean, boolean)}, except this method accepts a List
*
* @param worlds the list of world
* @param includeGlobal if global permissions should apply
* @return true if the node should apply
*/
boolean shouldApplyOnAnyWorlds(List<String> worlds, boolean includeGlobal);
/**
* 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
*/
List<String> resolveWildcard(List<String> possibleNodes);
boolean shouldApplyWithContext(@Nonnull ContextSet context);
/**
* Resolves any shorthand parts of this node and returns the full list
*
* @return a list of full nodes
*/
@Nonnull
List<String> resolveShorthand();
/**
* Gets if this node will expire in the future
*
* @return true if this node will expire in the future
*/
boolean isTemporary();
/**
* Gets if this node will not expire
*
* @return true if this node will not expire
*/
boolean isPermanent();
default boolean isPermanent() {
return !isTemporary();
}
/**
* Gets a unix timestamp in seconds when this node will expire
*
* @return the time in Unix time when this node will expire
* @throws IllegalStateException if the node is not temporary
*/
long getExpiryUnixTime();
long getExpiryUnixTime() throws IllegalStateException;
/**
* Gets the date when this node will expire
*
* @return the {@link Date} when this node will expire
* @throws IllegalStateException if the node is not temporary
*/
Date getExpiry();
@Nonnull
Date getExpiry() throws IllegalStateException;
/**
* Gets the number of seconds until this permission will expire
*
* @return the number of seconds until this permission will expire
* @throws IllegalStateException if the node is not temporary
*/
long getSecondsTilExpiry();
long getSecondsTilExpiry() throws IllegalStateException;
/**
* Return true if the node has expired.
* This also returns false if the node is not temporary
* Gets if the node has expired.
*
* <p>This also returns false if the node is not temporary.</p>
*
* @return true if this node has expired
*/
boolean hasExpired();
/**
* Gets the extra contexts required for this node to apply
*
* @return the extra contexts required for this node to apply
* @since 2.13
*/
@Nonnull
ContextSet getContexts();
/**
* Converts this node into a serialized form
* The same as {@link #getContexts()}, but also includes values for "server" and "world" keys if present.
*
* @return a serialized node string
* @return the full contexts required for this node to apply
* @since 3.1
*/
String toSerializedNode();
@Nonnull
ContextSet getFullContexts();
/**
* Gets if this is a group node
*
* @return true if this is a group node
*/
boolean isGroupNode();
/**
* Gets the name of the group, if this is a group node.
*
* @return the name of the group
* @throws IllegalStateException if this is not a group node. See {@link #isGroupNode()}
*/
String getGroupName();
@Nonnull
String getGroupName() throws IllegalStateException;
/**
* @return true is this node is a wildcard node
* Gets if this node is a wildcard node
*
* @return true if this node is a wildcard node
*/
boolean isWildcard();
@@ -237,9 +264,11 @@ public interface Node extends Map.Entry<String, Boolean> {
* @return the wildcard level
* @throws IllegalStateException if this is not a wildcard
*/
int getWildcardLevel();
int getWildcardLevel() throws IllegalStateException;
/**
* Gets if this node is a meta node
*
* @return true if this node is a meta node
*/
boolean isMeta();
@@ -250,9 +279,12 @@ public interface Node extends Map.Entry<String, Boolean> {
* @return the meta value
* @throws IllegalStateException if this node is not a meta node
*/
Map.Entry<String, String> getMeta();
@Nonnull
Map.Entry<String, String> getMeta() throws IllegalStateException;
/**
* Gets if this node is a prefix node
*
* @return true if this node is a prefix node
*/
boolean isPrefix();
@@ -263,9 +295,12 @@ public interface Node extends Map.Entry<String, Boolean> {
* @return the prefix value
* @throws IllegalStateException if this node is a not a prefix node
*/
Map.Entry<Integer, String> getPrefix();
@Nonnull
Map.Entry<Integer, String> getPrefix() throws IllegalStateException;
/**
* Gets if this node is a suffix node
*
* @return true if this node is a suffix node
*/
boolean isSuffix();
@@ -276,7 +311,8 @@ public interface Node extends Map.Entry<String, Boolean> {
* @return the suffix value
* @throws IllegalStateException if this node is a not a suffix node
*/
Map.Entry<Integer, String> getSuffix();
@Nonnull
Map.Entry<Integer, String> getSuffix() throws IllegalStateException;
/**
* Checks if this Node is equal to another node
@@ -285,6 +321,7 @@ public interface Node extends Map.Entry<String, Boolean> {
* @return true if this node is equal to the other provided
* @see #equalsIgnoringValue(Node) for a less strict implementation of this method
*/
@Override
boolean equals(Object obj);
/**
@@ -293,7 +330,7 @@ public interface Node extends Map.Entry<String, Boolean> {
* @param other the other node
* @return true if the two nodes are almost equal
*/
boolean equalsIgnoringValue(Node other);
boolean equalsIgnoringValue(@Nonnull Node other);
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the expiry time or value
@@ -301,7 +338,7 @@ public interface Node extends Map.Entry<String, Boolean> {
* @param other the other node
* @return true if the two nodes are almost equal
*/
boolean almostEquals(Node other);
boolean almostEquals(@Nonnull Node other);
/**
* Similar to {@link Node#equals(Object)}, except doesn't take note of the value or if the node is temporary
@@ -310,38 +347,51 @@ public interface Node extends Map.Entry<String, Boolean> {
* @return true if the two nodes are almost equal
* @since 2.8
*/
boolean equalsIgnoringValueOrTemp(Node other);
boolean equalsIgnoringValueOrTemp(@Nonnull Node other);
/**
* Builds a Node instance
*/
interface Builder {
@Nonnull
Builder setNegated(boolean negated);
@Nonnull
Builder setValue(boolean value);
/**
* Warning: this value does not persist, and disappears when the holder is re-loaded.
* It is therefore only useful for transient nodes.
*/
@Nonnull
Builder setOverride(boolean override);
@Nonnull
Builder setExpiry(long expireAt);
Builder setWorld(String world);
@Nonnull
Builder setWorld(@Nonnull String world);
Builder setServer(String server) throws IllegalArgumentException;
@Nonnull
Builder setServer(@Nonnull String server) throws IllegalArgumentException;
Builder withExtraContext(String key, String value);
@Nonnull
Builder withExtraContext(@Nonnull String key, @Nonnull String value);
Builder withExtraContext(Map<String, String> map);
@Nonnull
Builder withExtraContext(@Nonnull Map<String, String> map);
Builder withExtraContext(Set<Map.Entry<String, String>> context);
@Nonnull
Builder withExtraContext(@Nonnull Set<Map.Entry<String, String>> context);
Builder withExtraContext(Map.Entry<String, String> entry);
@Nonnull
Builder withExtraContext(@Nonnull Map.Entry<String, String> entry);
Builder withExtraContext(ContextSet set);
@Nonnull
Builder withExtraContext(@Nonnull ContextSet set);
@Nonnull
Node build();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,24 +25,15 @@
package me.lucko.luckperms.api;
import javax.annotation.Nonnull;
/**
* Builds {@link Node} instances
* Assists with constructing {@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
*
@@ -47,7 +41,8 @@ public interface NodeFactory {
* @return a node builder instance
* @throws NullPointerException if the permission is null
*/
Node.Builder newBuilder(String permission);
@Nonnull
Node.Builder newBuilder(@Nonnull String permission);
/**
* Creates a node builder instance from an existing node
@@ -56,46 +51,76 @@ public interface NodeFactory {
* @return a node builder instance
* @throws NullPointerException if the other node is null
*/
Node.Builder newBuilderFromExisting(Node other);
@Nonnull
Node.Builder newBuilderFromExisting(@Nonnull Node other);
/**
* Creates a node builder from a serialised node string
* @param serialisedPermission the serialised permission string
* @param value the value of the node
* Creates a node builder from a group
*
* @param group the group
* @return a node builder instance
* @throws NullPointerException if the permission is null
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @since 3.1
*/
Node.Builder newBuilderFromSerialisedNode(String serialisedPermission, boolean value);
@Nonnull
Node.Builder makeGroupNode(@Nonnull Group group);
/**
* Creates a node builder from a group
*
* @param groupName the name of the group
* @return a node builder instance
* @throws NullPointerException if the groupName is null
* @since 4.0
*/
@Nonnull
Node.Builder makeGroupNode(@Nonnull String groupName);
/**
* Creates a node builder from a key value pair
*
* @param key the key
* @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);
@Nonnull
Node.Builder makeMetaNode(@Nonnull String key, @Nonnull String value);
/**
* Creates a node builder for the given chat meta type
*
* @param priority the priority
* @param value the value for the prefix/suffix
* @return a node builder instance
* @throws NullPointerException if the type or value is null
* @since 3.2
*/
@Nonnull
Node.Builder makeChatMetaNode(@Nonnull ChatMetaType type, int priority, @Nonnull String value);
/**
* Creates a node builder from a prefix string and priority
*
* @param priority the priority
* @param prefix the prefix string
* @param prefix the prefix string
* @return a node builder instance
* @throws NullPointerException if the prefix is null
*/
Node.Builder makePrefixNode(int priority, String prefix);
@Nonnull
Node.Builder makePrefixNode(int priority, @Nonnull String prefix);
/**
* Creates a node builder from a prefix string and priority
*
* @param priority the priority
* @param suffix the suffix string
* @param suffix the suffix string
* @return a node builder instance
* @throws NullPointerException if the suffix is null
*/
Node.Builder makeSuffixNode(int priority, String suffix);
@Nonnull
Node.Builder makeSuffixNode(int priority, @Nonnull String suffix);
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,88 +25,226 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
/**
* An object capable of holding permissions
* An object which holds permissions.
*
* <p> Any changes made will be lost unless the instance is saved back to the {@link Storage}.
* <p>Any changes made to permission holding objects will be lost unless the
* instance is saved back to the {@link Storage}.</p>
*/
public interface PermissionHolder {
/**
* Gets the objects name
* Gets the objects name.
*
* <p> {@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} should normally be used instead of
* this method.
* <p>{@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} should normally be used instead of
* this method.</p>
*
* @return the identifier for this object. Either a uuid string or name.
*/
@Nonnull
String getObjectName();
/**
* Gets a friendly name for this holder, to be displayed in command output, etc.
*
* <p>This will <strong>always</strong> return a value, eventually falling back to {@link #getObjectName()} if no
* other "friendlier" identifiers are present.</p>
*
* @return a friendly identifier for this holder
* @since 3.2
*/
@Nonnull
String getFriendlyName();
/**
* Gets the backing multimap containing every permission this holder has.
*
* <p>This method <b>does not</b> resolve inheritance rules, and returns a
* view of what's 'in the file'.</p>
*
* @return the holders own permissions
* @since 3.3
*/
@Nonnull
ImmutableSetMultimap<ImmutableContextSet, Node> getNodes();
/**
* Gets the backing multimap containing every transient permission this holder has.
*
* <p>This method <b>does not</b> resolve inheritance rules.</p>
*
* <p>Transient permissions only exist for the duration of the session.</p>
*
* @return the holders own permissions
* @since 3.3
*/
@Nonnull
ImmutableSetMultimap<ImmutableContextSet, Node> getTransientNodes();
/**
* Gets a flattened/squashed view of the holders permissions.
*
* <p>This list is constructed using the {@link Multimap#values()} method
* of both the transient and enduring backing multimaps.</p>
*
* <p>This means that it <b>may contain</b> duplicate entries.</p>
*
* <p>Use {@link #getPermissions()} for a view without duplicates.</p>
*
* @return a list of the holders own nodes.
* @since 3.3
*/
@Nonnull
List<Node> getOwnNodes();
/**
* Gets a sorted set of all held permissions.
*
* @return an immutable set of permissions in priority order
* @since 2.6
*/
@Nonnull
SortedSet<? extends Node> getPermissions();
/**
* Similar to {@link #getPermissions()}, except without transient permissions
* Similar to {@link #getPermissions()}, except without transient permissions.
*
* <p>Unlike transient permissions, enduring permissions will be saved to storage, and exist after the session.</p>
*
* @return a set of nodes
* @since 2.6
*/
@Nonnull
Set<? extends Node> getEnduringPermissions();
/**
* Gets an immutable set of all transiently held permissions.
* Similar to {@link #getPermissions()}, except without enduring permissions.
*
* <p> Transient permissions only exist for the duration of the session.
* <p>Transient permissions only exist for the duration of the session.</p>
*
* @return a set of nodes
* @since 2.6
*/
@Nonnull
Set<? extends Node> getTransientPermissions();
/**
* 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.
* Processes the nodes and returns the non-temporary ones.
*
* @param contexts the context for the lookup,
* @return a set of permanent nodes
* @since 2.6
*/
@Nonnull
Set<Node> getPermanentPermissionNodes();
/**
* Processes the nodes and returns the temporary ones.
*
* @return a set of temporary nodes
* @since 2.6
*/
@Nonnull
Set<Node> getTemporaryPermissionNodes();
/**
* Recursively resolves this holders permissions.
*
* <p>The returned list will contain every inherited
* node the holder has, in the order that they were inherited in.</p>
*
* <p>This means the list will contain duplicates.</p>
*
* @param contexts the contexts for the lookup
* @return a list of nodes
* @since 3.3
*/
@Nonnull
List<LocalizedNode> resolveInheritances(Contexts contexts);
/**
* Recursively resolves this holders permissions.
*
* <p>The returned list will contain every inherited
* node the holder has, in the order that they were inherited in.</p>
*
* <p>This means the list will contain duplicates.</p>
*
* <p>Unlike {@link #resolveInheritances(Contexts)}, this method does not
* filter by context, at all.</p>
*
* @return a list of nodes
* @since 3.3
*/
@Nonnull
List<LocalizedNode> resolveInheritances();
/**
* Gets a mutable sorted set of the nodes that this object has and inherits, filtered by context
*
* <p>Unlike {@link #getAllNodesFiltered(Contexts)}, this method will not filter individual nodes. The context is only
* used to determine which groups should apply.</p>
*
* <p>Nodes are sorted into priority order.</p>
*
* @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);
@Nonnull
SortedSet<LocalizedNode> getAllNodes(@Nonnull Contexts contexts);
/**
* Gets a mutable sorted set of the nodes that this object has and inherits.
*
* <p>Unlike {@link #getAllNodes(Contexts)}, this method does not filter by context, at all.</p>
*
* <p>Nodes are sorted into priority order.</p>
*
* @return a mutable sorted set of permissions
* @throws NullPointerException if the context is null
* @since 3.3
*/
@Nonnull
SortedSet<LocalizedNode> getAllNodes();
/**
* 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.
*
* <p>Unlike {@link #getAllNodes(Contexts)}, this method WILL filter individual nodes, and only return ones that fully
* meet the context provided.</p>
*
* @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);
@Nonnull
Set<LocalizedNode> getAllNodesFiltered(@Nonnull 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);
@Nonnull
Map<String, Boolean> exportNodes(@Nonnull Contexts contexts, boolean lowerCase);
/**
* Removes temporary permissions that have expired
@@ -118,7 +259,8 @@ public interface PermissionHolder {
* @throws NullPointerException if the node is null
* @since 2.6
*/
Tristate hasPermission(Node node);
@Nonnull
Tristate hasPermission(@Nonnull Node node);
/**
* Checks to see if the object has a certain permission
@@ -128,384 +270,88 @@ public interface PermissionHolder {
* @throws NullPointerException if the node is null
* @since 2.6
*/
Tristate hasTransientPermission(Node node);
@Nonnull
Tristate hasTransientPermission(@Nonnull Node node);
/**
* Checks to see if the object has a certain permission
*
* @param node The permission node
* @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 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 server The server
* @return true if the object has the permission
* @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 server The server
* @param world The world
* @return true if the object has the permission
* @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 temporary if the permission is temporary
* @return true if the object has the permission
* @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 temporary if the permission is temporary
* @return true if the object has the permission
* @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 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 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
* Checks 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
* @since 2.6
*/
Tristate inheritsPermission(Node node);
/**
* Checks to see if the object inherits a certain permission
*
* @param node The permission node
* @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 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 server The server
* @return true if the object inherits the permission
* @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 server The server
* @param world The world
* @return true if the object inherits the permission
* @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 temporary if the permission is temporary
* @return true if the object inherits the permission
* @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 temporary if the permission is temporary
* @return true if the object inherits the permission
* @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 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 IllegalArgumentException if the node, server or world if invalid
*/
boolean inheritsPermission(String node, boolean b, String server, String world, boolean temporary);
@Nonnull
Tristate inheritsPermission(@Nonnull Node node);
/**
* 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
* @since 2.6
* @return the result of the operation
* @throws NullPointerException if the node is null
* @since 4.0
*/
void setPermission(Node node) throws ObjectAlreadyHasException;
@Nonnull
DataMutateResult setPermission(@Nonnull Node node);
/**
* Sets a transient permission for the object
*
* <p> A transient node is a permission that does not persist.
* <p>A transient node is a permission that does not persist.
* Whenever a user logs out of the server, or the server restarts, this permission will disappear.
* It is never saved to the datastore, and therefore will not apply on other servers.
* It is never saved to the datastore, and therefore will not apply on other servers.</p>
*
* <p> This is useful if you want to temporarily set a permission for a user while they're online, but don't
* want it to persist, and have to worry about removing it when they log out.
* <p>This is useful if you want to temporarily set a permission for a user while they're online, but don't
* want it to persist, and have to worry about removing it when they log out.</p>
*
* <p> For unsetting a transient permission, see {@link #unsetTransientPermission(Node)}
* <p>For unsetting a transient permission, see {@link #unsetTransientPermission(Node)}</p>
*
* @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @since 2.6
* @return the result of the operation
* @throws NullPointerException if the node is null
* @since 4.0
*/
void setTransientPermission(Node node) throws ObjectAlreadyHasException;
/**
* Sets a permission for the object
*
* @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
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
void setPermission(String node, boolean value) throws ObjectAlreadyHasException;
/**
* 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 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
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException;
/**
* 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 server The server 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
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
void setPermission(String node, boolean value, String server, String world) throws ObjectAlreadyHasException;
/**
* 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 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
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException;
/**
* 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 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
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException;
/**
* 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 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
* @deprecated in favour of {@link #setPermission(Node)}
*/
@Deprecated
void setPermission(String node, boolean value, String server, String world, long expireAt) throws ObjectAlreadyHasException;
@Nonnull
DataMutateResult setTransientPermission(@Nonnull Node node);
/**
* Unsets a permission for the object
*
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @return the result of the operation
* @throws NullPointerException if the node is null
* @since 2.6
* @since 4.0
*/
void unsetPermission(Node node) throws ObjectLacksException;
@Nonnull
DataMutateResult unsetPermission(@Nonnull Node node);
/**
* Unsets a transient permission for the object
*
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @return the result of the operation
* @throws NullPointerException if the node is null
* @since 2.6
* @since 4.0
*/
void unsetTransientPermission(Node node) throws ObjectLacksException;
@Nonnull
DataMutateResult unsetTransientPermission(@Nonnull Node node);
/**
* Unsets a permission for the object
* Clears any nodes from the holder which pass the predicate
*
* @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 IllegalArgumentException if the node is invalid
* @deprecated in favour of {@link #unsetPermission(Node)}
* @param test the predicate to test for nodes which should be removed
* @since 3.2
*/
@Deprecated
void unsetPermission(String node, boolean temporary) throws ObjectLacksException;
void clearMatching(@Nonnull Predicate<Node> test);
/**
* Unsets a permission for the object
* Clears any transient nodes from the holder which pass the predicate
*
* @param node The node to be unset
* @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)}
* @param test the predicate to test for nodes which should be removed
* @since 3.2
*/
@Deprecated
void unsetPermission(String node) throws ObjectLacksException;
/**
* 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
* @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)}
*/
@Deprecated
void unsetPermission(String node, String server) throws ObjectLacksException;
/**
* 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
* @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) throws ObjectLacksException;
/**
* 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 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 IllegalArgumentException if the node or server is invalid
* @deprecated in favour of {@link #unsetPermission(Node)}
*/
@Deprecated
void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException;
/**
* 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 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 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;
void clearMatchingTransient(@Nonnull Predicate<Node> test);
/**
* Clears all nodes held by the object
@@ -515,21 +361,12 @@ public interface PermissionHolder {
void clearNodes();
/**
* Clears all nodes held by the object on a specific server
* Clears all nodes held by the object in a specific context
*
* @param server the server to filter by, can be null
* @since 2.17
* @param contextSet the contexts to filter by
* @since 3.2
*/
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);
void clearNodes(@Nonnull ContextSet contextSet);
/**
* Clears all parent groups
@@ -539,21 +376,12 @@ public interface PermissionHolder {
void clearParents();
/**
* Clears all parents on a specific server
* Clears all parent groups in a specific context
*
* @param server the server to filter by, can be null
* @since 2.17
* @param contextSet the contexts to filter by
* @since 3.2
*/
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);
void clearParents(@Nonnull ContextSet contextSet);
/**
* Clears all meta held by the object
@@ -563,31 +391,12 @@ public interface PermissionHolder {
void clearMeta();
/**
* Clears all meta held by the object on a specific server
* Clears all meta held by the object in a specific context
*
* @param server the server to filter by, can be null
* @since 2.17
* @param contextSet the contexts to filter by
* @since 3.2
*/
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);
void clearMeta(@Nonnull ContextSet contextSet);
/**
* Clears all transient permissions the holder has.
@@ -595,19 +404,63 @@ public interface PermissionHolder {
void clearTransientNodes();
/**
* Processes the nodes and returns the non-temporary ones.
* Sets a permission for the object
*
* @return a set of permanent nodes
* @since 2.6
* @param node The node to be set
* @return the result of the operation
* @throws NullPointerException if the node is null
* @since 3.1
* @deprecated now forwards to {@link #setPermission(Node)}.
*/
Set<Node> getPermanentPermissionNodes();
@Nonnull
@Deprecated
default DataMutateResult setPermissionUnchecked(@Nonnull Node node) {
return setPermission(node);
}
/**
* Processes the nodes and returns the temporary ones.
* Sets a transient permission for the object
*
* @return a set of temporary nodes
* @since 2.6
* @param node The node to be set
* @return the result of the operation
* @throws NullPointerException if the node is null
* @since 3.1
* @deprecated now forwards to {@link #setTransientPermission(Node)}
*/
Set<Node> getTemporaryPermissionNodes();
@Nonnull
@Deprecated
default DataMutateResult setTransientPermissionUnchecked(@Nonnull Node node) {
return setTransientPermission(node);
}
/**
* Unsets a permission for the object
*
* @param node The node to be unset
* @throws NullPointerException if the node is null
* @return the result of the operation
* @since 3.1
* @deprecated now forwards to {@link #unsetPermission(Node)}
*/
@Nonnull
@Deprecated
default DataMutateResult unsetPermissionUnchecked(@Nonnull Node node) {
return unsetPermission(node);
}
/**
* Unsets a transient permission for the object
*
* @param node The node to be unset
* @throws NullPointerException if the node is null
* @return the result of the operation
* @since 3.1
* @deprecated now forwards to {@link #unsetTransientPermission(Node)}
*/
@Nonnull
@Deprecated
default DataMutateResult unsetTransientPermissionUnchecked(@Nonnull Node node) {
return unsetTransientPermission(node);
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -29,16 +32,19 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* A means of loading and saving data to/from the Storage provider.
* A means of loading and saving permission data to/from the backend.
*
* <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>All blocking methods return {@link CompletableFuture}s, which will be populated with the result once the data has been
* loaded/saved asynchronously. Care should be taken when using such methods to ensure that the main server thread is not
* blocked.</p>
*
* <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()}.
* server thread. If you need to use the result of these operations on the main server thread, register a
* callback using {@link CompletableFuture#thenAcceptAsync(Consumer, Executor)} and {@link #getSyncExecutor()}.</p>
*
* @since 2.14
*/
@@ -49,10 +55,11 @@ public interface Storage {
*
* @return the name of the implementation
*/
@Nonnull
String getName();
/**
* Return whether the storage instance is allowing logins on the platform.
* Gets whether the storage instance is allowing logins on the platform.
*
* @return true if logins are enabled
*/
@@ -63,6 +70,7 @@ public interface Storage {
*
* @return an executor instance
*/
@Nonnull
Executor getSyncExecutor();
/**
@@ -71,6 +79,7 @@ public interface Storage {
*
* @return an executor instance
*/
@Nonnull
Executor getAsyncExecutor();
/**
@@ -80,25 +89,39 @@ public interface Storage {
* @return true if the operation completed successfully.
* @throws NullPointerException if entry is null
*/
CompletableFuture<Boolean> logAction(LogEntry entry);
@Nonnull
CompletableFuture<Boolean> logAction(@Nonnull LogEntry entry);
/**
* Loads and returns the entire log from storage
*
* @return a log instance, could be null if loading failed
*/
@Nonnull
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.)
* @param username the users username, or null if it is not known.
* @return if the operation completed successfully
* @throws NullPointerException if uuid or username is null
* @throws NullPointerException if uuid is null
*/
CompletableFuture<Boolean> loadUser(UUID uuid, String username);
@Nonnull
CompletableFuture<Boolean> loadUser(@Nonnull UUID uuid, @Nullable String username);
/**
* Loads a user's data from the main storage into the plugins local storage.
*
* @param uuid the uuid of the user to load
* @return if the operation completed successfully
* @throws NullPointerException if uuid is null
*/
@Nonnull
default CompletableFuture<Boolean> loadUser(@Nonnull UUID uuid) {
return loadUser(uuid, null);
}
/**
* Saves a user object back to storage. You should call this after you make any changes to a user.
@@ -108,21 +131,17 @@ public interface Storage {
* @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();
@Nonnull
CompletableFuture<Boolean> saveUser(@Nonnull User user);
/**
* Gets a set all "unique" user UUIDs.
* "Unique" meaning the user isn't just a member of the "default" group.
*
* <p>"Unique" meaning the user isn't just a member of the "default" group.</p>
*
* @return a set of uuids, or null if the operation failed.
*/
@Nonnull
CompletableFuture<Set<UUID>> getUniqueUsers();
/**
@@ -133,7 +152,8 @@ public interface Storage {
* @throws NullPointerException if the permission is null
* @since 2.17
*/
CompletableFuture<List<HeldPermission<UUID>>> getUsersWithPermission(String permission);
@Nonnull
CompletableFuture<List<HeldPermission<UUID>>> getUsersWithPermission(@Nonnull String permission);
/**
* Creates and loads a group into the plugins local storage
@@ -143,7 +163,8 @@ public interface Storage {
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
CompletableFuture<Boolean> createAndLoadGroup(String name);
@Nonnull
CompletableFuture<Boolean> createAndLoadGroup(@Nonnull String name);
/**
* Loads a group into the plugins local storage.
@@ -153,13 +174,15 @@ public interface Storage {
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
CompletableFuture<Boolean> loadGroup(String name);
@Nonnull
CompletableFuture<Boolean> loadGroup(@Nonnull String name);
/**
* Loads all groups from the storage into memory
*
* @return true if the operation completed successfully.
*/
@Nonnull
CompletableFuture<Boolean> loadAllGroups();
/**
@@ -170,7 +193,8 @@ public interface Storage {
* @throws NullPointerException if group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> saveGroup(Group group);
@Nonnull
CompletableFuture<Boolean> saveGroup(@Nonnull Group group);
/**
* Permanently deletes a group from storage.
@@ -180,7 +204,8 @@ public interface Storage {
* @throws NullPointerException if group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> deleteGroup(Group group);
@Nonnull
CompletableFuture<Boolean> deleteGroup(@Nonnull Group group);
/**
* Searches for a list of groups with a given permission.
@@ -190,7 +215,8 @@ public interface Storage {
* @throws NullPointerException if the permission is null
* @since 2.17
*/
CompletableFuture<List<HeldPermission<String>>> getGroupsWithPermission(String permission);
@Nonnull
CompletableFuture<List<HeldPermission<String>>> getGroupsWithPermission(@Nonnull String permission);
/**
* Creates and loads a track into the plugins local storage
@@ -200,7 +226,8 @@ public interface Storage {
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
CompletableFuture<Boolean> createAndLoadTrack(String name);
@Nonnull
CompletableFuture<Boolean> createAndLoadTrack(@Nonnull String name);
/**
* Loads a track into the plugins local storage.
@@ -210,13 +237,15 @@ public interface Storage {
* @throws NullPointerException if name is null
* @throws IllegalArgumentException if the name is invalid
*/
CompletableFuture<Boolean> loadTrack(String name);
@Nonnull
CompletableFuture<Boolean> loadTrack(@Nonnull String name);
/**
* Loads all tracks from the storage into memory
*
* @return true if the operation completed successfully.
*/
@Nonnull
CompletableFuture<Boolean> loadAllTracks();
/**
@@ -227,7 +256,8 @@ public interface Storage {
* @throws NullPointerException if track is null
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> saveTrack(Track track);
@Nonnull
CompletableFuture<Boolean> saveTrack(@Nonnull Track track);
/**
* Permanently deletes a track from storage
@@ -237,7 +267,8 @@ public interface Storage {
* @throws NullPointerException if track is null
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
*/
CompletableFuture<Boolean> deleteTrack(Track track);
@Nonnull
CompletableFuture<Boolean> deleteTrack(@Nonnull Track track);
/**
* Saves UUID caching data to the global cache
@@ -248,7 +279,8 @@ public interface Storage {
* @throws NullPointerException if either parameters are null
* @throws IllegalArgumentException if the username is invalid
*/
CompletableFuture<Boolean> saveUUIDData(String username, UUID uuid);
@Nonnull
CompletableFuture<Boolean> saveUUIDData(@Nonnull String username, @Nonnull UUID uuid);
/**
* Gets a UUID from a username
@@ -258,7 +290,8 @@ public interface Storage {
* @throws NullPointerException if either parameters are null
* @throws IllegalArgumentException if the username is invalid
*/
CompletableFuture<UUID> getUUID(String username);
@Nonnull
CompletableFuture<UUID> getUUID(@Nonnull String username);
/**
* Gets a username from a UUID
@@ -268,6 +301,7 @@ public interface Storage {
* @throws NullPointerException if either parameters are null
* @since 2.17
*/
CompletableFuture<String> getName(UUID uuid);
@Nonnull
CompletableFuture<String> getName(@Nonnull UUID uuid);
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,29 +25,34 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* An ordered collection of groups for easy promotions and demotions
* An ordered chain of {@link Group}s.
*/
public interface Track {
/**
* Gets the name of this track
*
* @return the name of this track
*/
@Nonnull
String getName();
/**
* Gets an ordered list of the groups on this track
* Gets a list of the groups on this track
*
* <p> 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.</p>
*
* <p>The returned collection is immutable, and cannot be modified.</p>
*
* @return an ordered {@link List} of the groups on this track
*/
@Nonnull
List<String> getGroups();
/**
@@ -57,65 +65,69 @@ public interface Track {
/**
* Gets the next group on the track, after the one provided
*
* <p>{@code null} is returned if the group is not on the track.</p>
*
* @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 IllegalStateException if the group instance was not obtained from LuckPerms.
*/
String getNext(Group current) throws ObjectLacksException;
@Nullable
String getNext(@Nonnull Group current);
/**
* Gets the previous group on the track, before the one provided
*
* <p>{@code null} is returned if the group is not on the track.</p>
*
* @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 IllegalStateException if the group instance was not obtained from LuckPerms.
*/
String getPrevious(Group current) throws ObjectLacksException;
@Nullable
String getPrevious(@Nonnull Group current);
/**
* 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
* @return the result of the operation
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void appendGroup(Group group) throws ObjectAlreadyHasException;
DataMutateResult appendGroup(@Nonnull Group group);
/**
* Inserts a group at a certain position on this track
*
* @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
* @return the result of the operation
* @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.
*/
void insertGroup(Group group, int position) throws ObjectAlreadyHasException, IndexOutOfBoundsException;
DataMutateResult insertGroup(@Nonnull Group group, int position) throws IndexOutOfBoundsException;
/**
* Removes a group from this track
*
* @param group the group to remove
* @throws ObjectLacksException if the group is not on this track
* @return the result of the operation
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void removeGroup(Group group) throws ObjectLacksException;
DataMutateResult removeGroup(@Nonnull Group group);
/**
* Removes a group from this track
*
* @param group the group to remove
* @throws ObjectLacksException if the group is not on this track
* @return the result of the operation
* @throws NullPointerException if the group is null
*/
void removeGroup(String group) throws ObjectLacksException;
DataMutateResult removeGroup(@Nonnull String group);
/**
* Checks if a group features on this track
@@ -125,7 +137,7 @@ public interface Track {
* @throws NullPointerException if the group is null
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
*/
boolean containsGroup(Group group);
boolean containsGroup(@Nonnull Group group);
/**
* Checks if a group features on this track
@@ -134,7 +146,7 @@ public interface Track {
* @return true if the group is on this track
* @throws NullPointerException if the group is null
*/
boolean containsGroup(String group);
boolean containsGroup(@Nonnull String group);
/**
* Clear all of the groups from this track
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,37 +25,45 @@
package me.lucko.luckperms.api;
import javax.annotation.Nonnull;
/**
* Represents a permission setting.
* Represents three different states of a 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.
* <p>Possible values:</p>
* <p></p>
* <ul>
* <li>{@link #TRUE} - a positive setting</li>
* <li>{@link #FALSE} - a negative (negated) setting</li>
* <li>{@link #UNDEFINED} - a non-existent setting</li>
* </ul>
*/
public enum Tristate {
/**
* A value indicating a holder has a permission set.
* A value indicating a positive setting
*/
TRUE(true),
/**
* A value indicating a holder has a negated value for a permission.
* A value indicating a negative (negated) setting
*/
FALSE(false),
/**
* A value indicating a holder doesn't have a value for a permission set.
* A value indicating a non-existent setting
*/
UNDEFINED(false);
/**
* Converts from {@link Boolean} a boolean
* Returns a {@link Tristate} from a boolean
*
* @param b the boolean
* @param val the boolean value
* @return {@link #TRUE} or {@link #FALSE}, depending on the value of the boolean.
*/
public static Tristate fromBoolean(boolean b) {
return b ? TRUE : FALSE;
@Nonnull
public static Tristate fromBoolean(boolean val) {
return val ? TRUE : FALSE;
}
private final boolean booleanValue;
@@ -63,7 +74,8 @@ public enum Tristate {
/**
* Returns the value of the Tristate as a boolean.
* <p> A value of {@link #UNDEFINED} converts to false.
*
* <p>A value of {@link #UNDEFINED} converts to false.</p>
*
* @return a boolean representation of the Tristate.
*/
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +26,16 @@
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 me.lucko.luckperms.api.context.ContextSet;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* A player holding permission data
* A player which holds permission data.
*/
public interface User extends PermissionHolder {
@@ -40,260 +44,100 @@ public interface User extends PermissionHolder {
*
* @return the users Mojang assigned unique id
*/
@Nonnull
UUID getUuid();
/**
* Gets the users username
*
* @return the Users Username
* <p>Returns null if no username is associated with this user.</p>
*
* @return the users username
*/
@Nullable
String getName();
/**
* Gets the users primary group
* Gets the users current primary group.
*
* <p>The result of this method depends on which method is configured for primary group calculation. It may not
* be the same as any value set through {@link #setPrimaryGroup(String)}.</p>
*
* @return the users primary group
*/
@Nonnull
String getPrimaryGroup();
/**
* Sets a users primary group
* Sets a users primary group. This will only take effect if platform is using stored primary groups.
*
* @param group the new primary group
* @throws ObjectAlreadyHasException if the user already has this set as their primary group
* @return if the change was applied successfully
* @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;
DataMutateResult setPrimaryGroup(@Nonnull String group);
/**
* Refresh and re-assign the users permissions.
* Gets the user's {@link UserData} cache.
*
* <p> This request is not buffered, and the refresh call will be ran directly. This should ideally be called on
* an asynchronous thread.
* @return the users cached data.
* @since 3.2
*/
void refreshPermissions();
@Nonnull
UserData getCachedData();
/**
* Gets the user's {@link UserData} cache, if they have one setup.
* Refreshes and applies any changes to the cached user data.
*
* @return an optional, possibly containing the user's cached lookup data.
* @since 2.13
* @return the task future
* @since 4.0
*/
Optional<UserData> getUserDataCache();
@Nonnull
CompletableFuture<Void> refreshCachedData();
/**
* 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
* Check to see if the user is a direct 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
*/
boolean isInGroup(Group group);
/**
* 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 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 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 server The server 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 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.
*/
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 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
*/
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 server The server 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
*/
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 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
*/
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 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
*/
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 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
*/
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 IllegalStateException if the group instance was not obtained from LuckPerms.
*/
void removeGroup(Group group) throws ObjectLacksException;
boolean isInGroup(@Nonnull Group group);
/**
* Remove the user from a group
* Check to see if the user is a direct member of a group in a specific context
*
* @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
* @param group the group to check membership of
* @param contextSet the context set to filter by
* @return true if the user is a member of the group
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
* @since 3.2
*/
void removeGroup(Group group, boolean temporary) throws ObjectLacksException;
boolean isInGroup(@Nonnull Group group, @Nonnull ContextSet contextSet);
/**
* Remove the user from a group on a specific server
* Refresh and re-assign the users permissions.
*
* @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 IllegalArgumentException if the server is invalid
* <p>This request is not buffered, and the refresh call will be ran directly. This should be called on an
* asynchronous thread.</p>
*
* @deprecated in favour of {@link #refreshCachedData()}.
*/
void removeGroup(Group group, String server) throws ObjectLacksException;
@Deprecated
void refreshPermissions();
/**
* Remove the user from a group on a specific server and world
* Pre-calculates some values in the user's data cache.
*
* @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.
* @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
* <p>Is it <b>not</b> necessary to call this method before
* using {@link #getCachedData()}.</p>
*
* @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 IllegalArgumentException if the expiry time is in the past or the server is invalid
* @since 2.17
* @deprecated because use of this method is no longer necessary.
*/
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 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 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;
/**
* 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
* @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 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 IllegalArgumentException if the server is invalid
*/
List<String> getLocalGroups(String server);
@Deprecated
void setupDataCache();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,35 +27,39 @@ package me.lucko.luckperms.api;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* 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 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>A user's internal LuckPerms UUID is always the same as their Mojang one,
* unless <code>use-server-uuids</code> is disabled.</p>
*
* <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>When this setting is disabled, this cache becomes active, and allows you to convert
* between 'internal' and 'server provided' uuids.</p>
*
* <p> THIS IS ONLY EFFECTIVE FOR ONLINE PLAYERS. USE THE DATASTORE METHODS FOR OFFLINE PLAYERS.
* <p><strong>This is only effective for online players. Use {@link Storage#getUUID(String)} for offline players.</strong></p>
*/
public interface UuidCache {
/**
* Gets a users internal "LuckPerms" UUID, from the one given by the server.
* 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
* <p>When <code>use-server-uuids</code> is true, this returns the same UUID instance.</p>
*
* @param mojangUuid the UUID assigned by the server, through <code>Player#getUniqueId</code> or <code>ProxiedPlayer#getUniqueId</code>
* @return the corresponding internal UUID
*/
UUID getUUID(UUID external);
@Nonnull
UUID getUUID(@Nonnull UUID mojangUuid);
/**
* Gets a users external, server assigned or Mojang assigned unique id, from the internal one used within LuckPerms.
* Gets a users "external", server assigned unique id, from the internal one used within LuckPerms.
*
* @param internal the UUID used within LuckPerms, through User#getUuid
* @param internalUuid the UUID used within LuckPerms, through <code>User#getUuid</code>
* @return the corresponding external UUID
*/
UUID getExternalUUID(UUID internal);
@Nonnull
UUID getExternalUUID(@Nonnull UUID internalUuid);
}
@@ -0,0 +1,126 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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 com.google.common.base.Preconditions;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.metastacking.MetaStackDefinition;
import javax.annotation.Nonnull;
/**
* Represents the context for a meta lookup.
*
* <p>Consisting of a standard {@link Contexts} element, plus options to define how
* the meta stack should be constructed.</p>
*
* @since 3.2
*/
public final class MetaContexts {
/**
* Creates a new meta contexts instance
*
* @param contexts the standard contexts for the query
* @param prefixStackDefinition the prefix stack definition to be used
* @param suffixStackDefinition the suffix stack definition to be used
* @return the new instance
*/
public static MetaContexts of(@Nonnull Contexts contexts, @Nonnull MetaStackDefinition prefixStackDefinition, @Nonnull MetaStackDefinition suffixStackDefinition) {
return new MetaContexts(contexts, prefixStackDefinition, suffixStackDefinition);
}
private final Contexts contexts;
private final MetaStackDefinition prefixStackDefinition;
private final MetaStackDefinition suffixStackDefinition;
// cache hashcode - this class is immutable, and is used as an index in the permission cache.
private final int hashCode;
/**
* Creates a new meta contexts instance
*
* @param contexts the standard contexts for the query
* @param prefixStackDefinition the prefix stack definition to be used
* @param suffixStackDefinition the suffix stack definition to be used
*/
public MetaContexts(@Nonnull Contexts contexts, @Nonnull MetaStackDefinition prefixStackDefinition, @Nonnull MetaStackDefinition suffixStackDefinition) {
this.contexts = Preconditions.checkNotNull(contexts, "contexts");
this.prefixStackDefinition = Preconditions.checkNotNull(prefixStackDefinition, "prefixStackDefinition");
this.suffixStackDefinition = Preconditions.checkNotNull(suffixStackDefinition, "suffixStackDefinition");
this.hashCode = calculateHashCode();
}
@Nonnull
public Contexts getContexts() {
return this.contexts;
}
@Nonnull
public MetaStackDefinition getPrefixStackDefinition() {
return this.prefixStackDefinition;
}
@Nonnull
public MetaStackDefinition getSuffixStackDefinition() {
return this.suffixStackDefinition;
}
@Override
@Nonnull
public String toString() {
return "MetaContexts(" +
"contexts=" + this.getContexts() + ", " +
"prefixStackDefinition=" + this.getPrefixStackDefinition() + ", " +
"suffixStackDefinition=" + this.getSuffixStackDefinition() +
")";
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof MetaContexts)) return false;
final MetaContexts other = (MetaContexts) o;
return this.getContexts().equals(other.getContexts()) &&
this.getPrefixStackDefinition().equals(other.getPrefixStackDefinition()) &&
this.getSuffixStackDefinition().equals(other.getSuffixStackDefinition());
}
private int calculateHashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.getContexts().hashCode();
result = result * PRIME + this.getPrefixStackDefinition().hashCode();
result = result * PRIME + this.getSuffixStackDefinition().hashCode();
return result;
}
@Override
public int hashCode() {
return hashCode;
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +25,16 @@
package me.lucko.luckperms.api.caching;
import com.google.common.collect.ListMultimap;
import me.lucko.luckperms.api.metastacking.MetaStackDefinition;
import java.util.Map;
import java.util.SortedMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Holds cached Meta lookup data for a specific set of contexts
*
@@ -33,10 +43,29 @@ import java.util.SortedMap;
public interface MetaData {
/**
* Gets an immutable copy of the meta this user has
* Gets an immutable copy of the meta this user has.
*
* <p>A list multimap is used because when inherited values are included, each key can be
* mapped to multiple values.</p>
*
* <p>The first value to be accumulated (and used to represent the key in {@link #getMeta()} is at index 0
* in the list. Any additional values are stored in order of accumulation.</p>
*
* @return an immutable multimap of meta
* @since 3.3
*/
@Nonnull
ListMultimap<String, String> getMetaMultimap();
/**
* Gets an immutable copy of the meta this user has.
*
* <p>This map is formed by taking the entries in {@link #getMetaMultimap()}, and mapping each key
* to the value at index 0 in the corresponding list.</p>
*
* @return an immutable map of meta
*/
@Nonnull
Map<String, String> getMeta();
/**
@@ -45,6 +74,7 @@ public interface MetaData {
*
* @return a sorted map of prefixes
*/
@Nonnull
SortedMap<Integer, String> getPrefixes();
/**
@@ -53,6 +83,7 @@ public interface MetaData {
*
* @return a sorted map of suffixes
*/
@Nonnull
SortedMap<Integer, String> getSuffixes();
/**
@@ -60,6 +91,7 @@ public interface MetaData {
*
* @return a prefix string, or null
*/
@Nullable
String getPrefix();
/**
@@ -67,6 +99,25 @@ public interface MetaData {
*
* @return a suffix string, or null
*/
@Nullable
String getSuffix();
/**
* Gets the definition used for the prefix stack
*
* @return the definition used for the prefix stack
* @since 3.2
*/
@Nonnull
MetaStackDefinition getPrefixStackDefinition();
/**
* Gets the definition used for the suffix stack
*
* @return the definition used for the suffix stack
* @since 3.2
*/
@Nonnull
MetaStackDefinition getSuffixStackDefinition();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +29,8 @@ import me.lucko.luckperms.api.Tristate;
import java.util.Map;
import javax.annotation.Nonnull;
/**
* Holds cached Permission lookup data for a specific set of contexts
*
@@ -40,12 +45,13 @@ public interface PermissionData {
* @return a tristate result
* @throws NullPointerException if permission is null
*/
Tristate getPermissionValue(String permission);
@Nonnull
Tristate getPermissionValue(@Nonnull String permission);
/**
* Invalidates the underlying permission calculator cache.
*
* <p> Can be called to allow for an update in defaults.
* <p>Can be called to allow for an update in defaults.</p>
*/
void invalidateCache();
@@ -54,6 +60,7 @@ public interface PermissionData {
*
* @return an immutable set of permissions
*/
@Nonnull
Map<String, Boolean> getImmutableBacking();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,11 +29,17 @@ import me.lucko.luckperms.api.Contexts;
import java.util.Set;
import javax.annotation.Nonnull;
/**
* 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.
* default data provided by the platform. This calls are heavily cached and are therefore fast.</p>
*
* <p>For meta, both methods accepting {@link Contexts} and {@link MetaContexts} are provided. The only difference is that
* the latter allows you to define how the meta stack should be structured internally. Where {@link Contexts} are passed, the
* values from the configuration are used.</p>
*
* @since 2.13
*/
@@ -39,24 +48,39 @@ 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.
* <p>If the data is not cached, it is calculated. Therefore, this call could be costly.</p>
*
* @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);
@Nonnull
PermissionData getPermissionData(@Nonnull 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.
* <p>If the data is not cached, it is calculated. Therefore, this call could be costly.</p>
*
* @param contexts the contexts to get the permission data in
* @return a meta data instance
* @throws NullPointerException if contexts is null
* @since 3.2
*/
@Nonnull
MetaData getMetaData(@Nonnull MetaContexts 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.</p>
*
* @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);
@Nonnull
MetaData getMetaData(@Nonnull Contexts contexts);
/**
* Calculates permission data, bypassing the cache.
@@ -65,7 +89,19 @@ public interface UserData {
* @return a permission data instance
* @throws NullPointerException if contexts is null
*/
PermissionData calculatePermissions(Contexts contexts);
@Nonnull
PermissionData calculatePermissions(@Nonnull 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
* @since 3.2
*/
@Nonnull
MetaData calculateMeta(@Nonnull MetaContexts contexts);
/**
* Calculates meta data, bypassing the cache.
@@ -74,29 +110,42 @@ public interface UserData {
* @return a meta data instance
* @throws NullPointerException if contexts is null
*/
MetaData calculateMeta(Contexts contexts);
@Nonnull
MetaData calculateMeta(@Nonnull 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.
* <p>If there is already data cached for the given contexts, and if the resultant output is different,
* the cached value is updated.</p>
*
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
*/
void recalculatePermissions(Contexts contexts);
void recalculatePermissions(@Nonnull 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.
* <p>If there is already data cached for the given contexts, and if the resultant output is different,
* the cached value is updated.</p>
*
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
* @since 3.2
*/
void recalculateMeta(@Nonnull MetaContexts 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.</p>
*
* @param contexts the contexts to recalculate in.
* @throws NullPointerException if contexts is null
*/
void recalculateMeta(Contexts contexts);
void recalculateMeta(@Nonnull Contexts contexts);
/**
* Calls {@link #recalculatePermissions(Contexts)} for all current loaded contexts
@@ -114,21 +163,22 @@ public interface UserData {
* @param contexts a set of contexts
* @throws NullPointerException if contexts is null
*/
void preCalculate(Set<Contexts> contexts);
void preCalculate(@Nonnull 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.
* <p>If the cache does not contain any data for the context, it will be calculated and saved.</p>
*
* @param contexts the contexts to pre-calculate for
* @throws NullPointerException if contexts is null
*/
void preCalculate(Contexts contexts);
void preCalculate(@Nonnull Contexts contexts);
/**
* Invalidates all of the underlying Permission calculators.
* Can be called to allow for an update in defaults.
*
* <p>Can be called to allow for an update in defaults.</p>
*/
void invalidatePermissionCalculators();
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,15 +25,15 @@
package me.lucko.luckperms.api.context;
import java.util.Map;
import javax.annotation.Nonnull;
/**
* 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.
* @since 2.13
*/
@FunctionalInterface
public interface ContextCalculator<T> {
/**
@@ -41,15 +44,7 @@ public interface ContextCalculator<T> {
* @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);
@Nonnull
MutableContextSet giveApplicableContext(@Nonnull T subject, @Nonnull MutableContextSet accumulator);
}
@@ -0,0 +1,172 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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 me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.User;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Manages {@link ContextCalculator}s, and calculates applicable contexts for a
* given type.
*
* This interface accepts {@link Object} types as a parameter to avoid having to depend
* on specific server implementations. In all cases, the "player" or "subject" type for
* the platform must be used.
*
* Specifically, {@code org.bukkit.entity.Player},
* {@code net.md_5.bungee.api.connection.ProxiedPlayer} and
* {@code org.spongepowered.api.service.permission.Subject}.
*
* @since 4.0
*/
public interface ContextManager {
/**
* Queries the ContextManager for current context values for the subject.
*
* @param subject the subject
* @return the applicable context for the subject
*/
@Nonnull
ImmutableContextSet getApplicableContext(@Nonnull Object subject);
/**
* Queries the ContextManager for current context values for the subject.
*
* @param subject the subject
* @return the applicable context for the subject
*/
@Nonnull
Contexts getApplicableContexts(@Nonnull Object subject);
/**
* Queries the ContextManager for current context values for the given User.
*
* <p>This will only return a value if the player corresponding to the
* {@link User} is online.</p>
*
* <p>If you need to be a {@link Contexts} instance regardless, you should
* initially try this method, and then fallback on {@link #getStaticContext()}.</p>
*
* @param user the user
* @return the applicable context for the subject
*/
@Nonnull
Optional<ImmutableContextSet> lookupApplicableContext(@Nonnull User user);
/**
* Queries the ContextManager for current context values for the given User.
*
* <p>This will only return a value if the player corresponding to the
* {@link User} is online.</p>
*
* <p>If you need to be a {@link Contexts} instance regardless, you should
* initially try this method, and then fallback on {@link #getStaticContexts()}.</p>
*
* @param user the user
* @return the applicable context for the subject
*/
@Nonnull
Optional<Contexts> lookupApplicableContexts(@Nonnull User user);
/**
* Gets the contexts from the static calculators in this manager.
*
* <p>Static calculators provide the same context for all subjects, and are
* marked as such when registered.</p>
*
* @return the current active static contexts
*/
@Nonnull
ImmutableContextSet getStaticContext();
/**
* Gets the contexts from the static calculators in this manager.
*
* <p>Static calculators provide the same context for all subjects, and are
* marked as such when registered.</p>
*
* @return the current active static contexts
*/
@Nonnull
Contexts getStaticContexts();
/**
* Forms a {@link Contexts} instance from an {@link ImmutableContextSet}.
*
* <p>This method relies on the plugins configuration to form the
* {@link Contexts} instance.</p>
*
* @param subject the reference subject
* @param contextSet the context set
* @return a contexts instance
*/
@Nonnull
Contexts formContexts(@Nonnull Object subject, @Nonnull ImmutableContextSet contextSet);
/**
* Forms a {@link Contexts} instance from an {@link ImmutableContextSet}.
*
* <p>This method relies on the plugins configuration to form the
* {@link Contexts} instance.</p>
*
* @param contextSet the context set
* @return a contexts instance
*/
@Nonnull
Contexts formContexts(@Nonnull ImmutableContextSet contextSet);
/**
* Registers a context calculator with the manager.
*
* @param calculator the calculator
*/
@Nonnull
void registerCalculator(@Nonnull ContextCalculator<?> calculator);
/**
* Registers a static context calculator with the manager.
*
* <p>Static calculators provide the same context for all subjects.</p>
*
* @param calculator the calculator
*/
@Nonnull
void registerStaticCalculator(@Nonnull StaticContextCalculator calculator);
/**
* Invalidates the lookup cache for a given subject
*
* @param subject the subject
*/
@Nonnull
void invalidateCache(@Nonnull Object subject);
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,74 +25,106 @@
package me.lucko.luckperms.api.context;
import com.google.common.base.Preconditions;
import com.google.common.collect.Multimap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
/**
* Holds contexts.
* Implementations may be either mutable or immutable.
* A set of context pairs.
*
* <p>You can think of ContextSets as a wrapped <code>Multimap&lt;String, String&gt;</code>.
* Each key can be mapped to multiple values.</p>
*
* <p>Keys are automatically converted to lowercase when added, and are therefore
* case-insensitive. Values however are not.</p>
*
* <p>Implementations may be either mutable or immutable.</p>
*
* @since 2.13
*/
public interface ContextSet {
/**
* Make a singleton ImmutableContextSet from a context pair
* Creates an 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) {
@Nonnull
static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
return ImmutableContextSet.singleton(key, value);
}
/**
* Creates a ImmutableContextSet from an existing map
* Creates an ImmutableContextSet from two context pairs
*
* @param map the map to copy from
* @return a new ImmutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
* @param key1 the first key
* @param value1 the first value
* @param key2 the second key
* @param value2 the second value
* @return a new ImmutableContextSet containing the two pairs
* @throws NullPointerException if any of the keys or values are null
* @since 3.1
*/
static ImmutableContextSet fromMap(Map<String, String> map) {
return ImmutableContextSet.fromMap(map);
@Nonnull
static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) {
return ImmutableContextSet.of(key1, value1, key2, value2);
}
/**
* Creates a ImmutableContextSet from an existing iterable of Map Entries
* Creates an 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) {
@Nonnull
static ImmutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
return ImmutableContextSet.fromEntries(iterable);
}
/**
* Creates a ImmutableContextSet from an existing multimap
* Creates an 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
*/
@Nonnull
static ImmutableContextSet fromMap(@Nonnull Map<String, String> map) {
return ImmutableContextSet.fromMap(map);
}
/**
* Creates an 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) {
@Nonnull
static ImmutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) {
return ImmutableContextSet.fromMultimap(multimap);
}
/**
* Creates a new ImmutableContextSet from an existing set.
* Creates an 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) {
@Nonnull
static ImmutableContextSet fromSet(@Nonnull ContextSet contextSet) {
return ImmutableContextSet.fromSet(contextSet);
}
@@ -98,12 +133,13 @@ public interface ContextSet {
*
* @return a new ImmutableContextSet
*/
@Nonnull
static ImmutableContextSet empty() {
return ImmutableContextSet.empty();
}
/**
* Check to see if this set is in an immutable form
* Gets if this set is in an immutable form
*
* @return true if the set is immutable
*/
@@ -114,6 +150,7 @@ public interface ContextSet {
*
* @return an immutable ContextSet
*/
@Nonnull
ImmutableContextSet makeImmutable();
/**
@@ -122,6 +159,7 @@ public interface ContextSet {
* @return a mutable ContextSet
* @since 2.16
*/
@Nonnull
MutableContextSet mutableCopy();
/**
@@ -129,16 +167,20 @@ public interface ContextSet {
*
* @return an immutable set
*/
@Nonnull
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
* <b>IMPORTANT: 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
* @deprecated because the resultant map may not contain all data in the ContextSet
*/
@Nonnull
@Deprecated
Map<String, String> toMap();
/**
@@ -147,6 +189,7 @@ public interface ContextSet {
* @return a multimap
* @since 2.16
*/
@Nonnull
Multimap<String, String> toMultimap();
/**
@@ -156,7 +199,7 @@ public interface ContextSet {
* @return true if the set contains a value for the key
* @throws NullPointerException if the key is null
*/
boolean containsKey(String key);
boolean containsKey(@Nonnull String key);
/**
* Gets a set of all of the values mapped to the given key
@@ -165,7 +208,20 @@ public interface ContextSet {
* @return a set of values
* @throws NullPointerException if the key is null
*/
Set<String> getValues(String key);
@Nonnull
Set<String> getValues(@Nonnull String key);
/**
* Returns any value from this set matching the key, if present.
*
* @param key the key to find values for
* @return an optional containing any match
* @since 3.1
*/
@Nonnull
default Optional<String> getAnyValue(@Nonnull String key) {
return getValues(key).stream().findAny();
}
/**
* Check if thr set contains a given key mapped to a given value
@@ -175,7 +231,20 @@ public interface ContextSet {
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
*/
boolean has(String key, String value);
boolean has(@Nonnull String key, @Nonnull String value);
/**
* Check if thr set contains a given key mapped to a given value
*
* @param entry the entry to look for
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
* @since 3.4
*/
default boolean has(@Nonnull Map.Entry<String, String> entry) {
Preconditions.checkNotNull(entry, "entry");
return has(entry.getKey(), entry.getValue());
}
/**
* Same as {@link #has(String, String)}, except ignores the case of the value.
@@ -185,7 +254,68 @@ public interface ContextSet {
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
*/
boolean hasIgnoreCase(String key, String value);
boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value);
/**
* Same as {@link #has(Map.Entry)}, except ignores the case of the value.
*
* @param entry the entry to look for
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
* @since 3.4
*/
default boolean hasIgnoreCase(@Nonnull Map.Entry<String, String> entry) {
Preconditions.checkNotNull(entry, "entry");
return hasIgnoreCase(entry.getKey(), entry.getValue());
}
/**
* Checks to see if all entries in this context set are also included in another set.
*
* @param other the other set to check
* @return true if all entries in this set are also in the other set
* @since 3.1
*/
default boolean isSatisfiedBy(@Nonnull ContextSet other) {
return isSatisfiedBy(other, true);
}
/**
* Checks to see if all entries in this context set are also included in another set.
*
* @param other the other set to check
* @param caseSensitive if the lookup should be case sensitive. see {@link #has(Map.Entry)} and {@link #hasIgnoreCase(Map.Entry)}.
* @return true if all entries in this set are also in the other set
* @since 3.4
*/
default boolean isSatisfiedBy(@Nonnull ContextSet other, boolean caseSensitive) {
Preconditions.checkNotNull(other, "other");
if (this.isEmpty()) {
// this is empty, so is therefore always satisfied.
return true;
} else if (other.isEmpty()) {
// this set isn't empty, but the other one is
return false;
} else if (this.size() > other.size()) {
// this set has more unique entries than the other set, so there's no way this can be satisfied.
return false;
} else {
// neither are empty, we need to compare the individual entries
for (Map.Entry<String, String> pair : toSet()) {
if (caseSensitive) {
if (!other.has(pair)) {
return false;
}
} else {
if (!other.hasIgnoreCase(pair)) {
return false;
}
}
}
return true;
}
}
/**
* Check if the set is empty
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -23,89 +26,107 @@
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.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An immutable implementation of {@link ContextSet}.
*
* <p>On construction, all keys/values are {@link String#intern()}ed, in order to increase
* comparison speed.</p>
*
* @since 2.16
*/
public final class ImmutableContextSet implements ContextSet {
private static final ImmutableContextSet EMPTY = new ImmutableContextSet(ImmutableMultimap.of());
private static final ImmutableContextSet EMPTY = new ImmutableContextSet(ImmutableSetMultimap.of());
/**
* Make a singleton ImmutableContextSet from a context pair
* Creates an 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));
@Nonnull
public static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
return new ImmutableContextSet(ImmutableSetMultimap.of(
checkNotNull(key, "key").toLowerCase().intern(),
checkNotNull(value, "value").intern()
));
}
/**
* Creates a ImmutableContextSet from an existing map
* Creates an ImmutableContextSet from two context pairs
*
* @param map the map to copy from
* @return a new ImmutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
* @param key1 the first key
* @param value1 the first value
* @param key2 the second key
* @param value2 the second value
* @return a new ImmutableContextSet containing the two pairs
* @throws NullPointerException if any of the keys or values are null
* @since 3.1
*/
public static ImmutableContextSet fromMap(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("map");
}
@Nonnull
public static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) {
return new ImmutableContextSet(ImmutableSetMultimap.of(
checkNotNull(key1, "key1").toLowerCase().intern(),
checkNotNull(value1, "value1").intern(),
checkNotNull(key2, "key2").toLowerCase().intern(),
checkNotNull(value2, "value2").intern()
));
}
ImmutableMultimap.Builder<String, String> b = ImmutableMultimap.builder();
for (Map.Entry<String, String> e : map.entrySet()) {
b.put(e.getKey(), e.getValue());
/**
* Creates an 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
*/
@Nonnull
public static ImmutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
checkNotNull(iterable, "iterable");
ImmutableSetMultimap.Builder<String, String> b = ImmutableSetMultimap.builder();
for (Map.Entry<String, String> e : iterable) {
b.put(checkNotNull(e.getKey()).toLowerCase().intern(), checkNotNull(e.getValue()).intern());
}
return new ImmutableContextSet(b.build());
}
/**
* Creates a ImmutableContextSet from an existing iterable of Map Entries
* Creates an ImmutableContextSet from an existing map
*
* @param iterable the iterable to copy from
* @return a new ImmutableContextSet representing the pairs in the iterable
* @throws NullPointerException if the iterable is null
* @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 fromEntries(Iterable<? extends Map.Entry<String, String>> iterable) {
if (iterable == null) {
throw new NullPointerException("iterable");
}
return MutableContextSet.fromEntries(iterable).makeImmutable();
@Nonnull
public static ImmutableContextSet fromMap(@Nonnull Map<String, String> map) {
return fromEntries(checkNotNull(map, "map").entrySet());
}
/**
* Creates a ImmutableContextSet from an existing multimap
* Creates an 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();
@Nonnull
public static ImmutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) {
return fromEntries(checkNotNull(multimap, "multimap").entries());
}
/**
@@ -116,23 +137,25 @@ public final class ImmutableContextSet implements ContextSet {
* @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();
@Nonnull
public static ImmutableContextSet fromSet(@Nonnull ContextSet contextSet) {
return checkNotNull(contextSet, "contextSet").makeImmutable();
}
/**
* Creates a new empty ContextSet.
* Creates an new empty ContextSet.
*
* @return a new ContextSet
*/
@Nonnull
public static ImmutableContextSet empty() {
return EMPTY;
}
private final Multimap<String, String> map;
private final ImmutableSetMultimap<String, String> map;
ImmutableContextSet(Multimap<String, String> contexts) {
this.map = ImmutableMultimap.copyOf(contexts);
ImmutableContextSet(ImmutableSetMultimap<String, String> contexts) {
this.map = contexts;
}
@Override
@@ -142,21 +165,25 @@ public final class ImmutableContextSet implements ContextSet {
@Override
@Deprecated // This set is already immutable!
@Nonnull
public ImmutableContextSet makeImmutable() {
return this;
}
@Override
@Nonnull
public MutableContextSet mutableCopy() {
return MutableContextSet.fromSet(this);
}
@Override
@Nonnull
public Set<Map.Entry<String, String>> toSet() {
return ImmutableSet.copyOf(map.entries());
return map.entries();
}
@Override
@Nonnull
public Map<String, String> toMap() {
ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
for (Map.Entry<String, String> e : map.entries()) {
@@ -167,56 +194,41 @@ public final class ImmutableContextSet implements ContextSet {
}
@Override
@Nonnull
public Multimap<String, String> toMultimap() {
return map;
}
@Override
public boolean containsKey(String key) {
if (key == null) {
throw new NullPointerException("key");
}
return map.containsKey(key);
@Nonnull
public boolean containsKey(@Nonnull String key) {
return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
}
@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();
@Nonnull
public Set<String> getValues(@Nonnull String key) {
Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
return values != null ? ImmutableSet.copyOf(values) : 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);
@Nonnull
public boolean has(@Nonnull String key, @Nonnull String value) {
return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
}
@Override
public boolean hasIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
@Nonnull
public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
value = checkNotNull(value, "value").intern();
Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
Collection<String> c = map.get(key);
if (c == null || c.isEmpty()) {
if (values == null || values.isEmpty()) {
return false;
}
for (String val : c) {
for (String val : values) {
if (val.equalsIgnoreCase(value)) {
return true;
}
@@ -240,14 +252,19 @@ public final class ImmutableContextSet implements ContextSet {
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
// saves on copying the multimap
if (other instanceof MutableContextSet) {
return other.equals(this);
}
final Multimap<String, String> thisContexts = this.toMultimap();
final Multimap<String, String> otherContexts = other.toMultimap();
return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts);
return thisContexts.equals(otherContexts);
}
@Override
public int hashCode() {
return 59 + (this.map == null ? 43 : this.map.hashCode());
return map.hashCode();
}
@Override
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,19 +25,29 @@
package me.lucko.luckperms.api.context;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A mutable implementation of {@link ContextSet}.
*
* <p>On construction, all keys/values are {@link String#intern()}ed, in order to increase
* comparison speed.</p>
*
* @since 2.16
*/
public final class MutableContextSet implements ContextSet {
@@ -47,33 +60,35 @@ public final class MutableContextSet implements ContextSet {
* @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();
@Nonnull
public static MutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
checkNotNull(key, "key");
checkNotNull(value, "value");
MutableContextSet set = MutableContextSet.create();
set.add(key, value);
return set;
}
/**
* Creates a MutableContextSet from an existing map
* Makes a MutableContextSet from two context pairs
*
* @param map the map to copy from
* @return a new MutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
* @param key1 the first key
* @param value1 the first value
* @param key2 the second key
* @param value2 the second value
* @return a new MutableContextSet containing the two pairs
* @throws NullPointerException if any of the keys or values are null
* @since 3.1
*/
public static MutableContextSet fromMap(Map<String, String> map) {
if (map == null) {
throw new NullPointerException("map");
}
MutableContextSet set = new MutableContextSet();
set.addAll(map);
@Nonnull
public static MutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) {
checkNotNull(key1, "key1");
checkNotNull(value1, "value1");
checkNotNull(key2, "key2");
checkNotNull(value2, "value2");
MutableContextSet set = MutableContextSet.create();
set.add(key1, value1);
set.add(key2, value2);
return set;
}
@@ -84,16 +99,29 @@ public final class MutableContextSet implements ContextSet {
* @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();
@Nonnull
public static MutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
checkNotNull(iterable, "iterable");
MutableContextSet set = MutableContextSet.create();
set.addAll(iterable);
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
*/
@Nonnull
public static MutableContextSet fromMap(@Nonnull Map<String, String> map) {
checkNotNull(map, "map");
MutableContextSet set = MutableContextSet.create();
set.addAll(map);
return set;
}
/**
* Creates a MutableContextSet from an existing multimap
*
@@ -101,12 +129,12 @@ public final class MutableContextSet implements ContextSet {
* @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());
@Nonnull
public static MutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) {
checkNotNull(multimap, "multimap");
MutableContextSet set = MutableContextSet.create();
set.addAll(multimap);
return set;
}
/**
@@ -117,11 +145,9 @@ public final class MutableContextSet implements ContextSet {
* @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");
}
@Nonnull
public static MutableContextSet fromSet(@Nonnull ContextSet contextSet) {
Preconditions.checkNotNull(contextSet, "contextSet");
MutableContextSet set = new MutableContextSet();
set.addAll(contextSet.toSet());
return set;
@@ -132,18 +158,19 @@ public final class MutableContextSet implements ContextSet {
*
* @return a new MutableContextSet
*/
@Nonnull
public static MutableContextSet create() {
return new MutableContextSet();
}
private final Multimap<String, String> map;
private final SetMultimap<String, String> map;
public MutableContextSet() {
this.map = Multimaps.synchronizedMultimap(HashMultimap.create());
this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create());
}
private MutableContextSet(Multimap<String, String> contexts) {
this.map = Multimaps.synchronizedMultimap(HashMultimap.create(contexts));
private MutableContextSet(MutableContextSet other) {
this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other.map));
}
@Override
@@ -151,21 +178,25 @@ public final class MutableContextSet implements ContextSet {
return false;
}
@Nonnull
@Override
public ImmutableContextSet makeImmutable() {
return new ImmutableContextSet(map);
return new ImmutableContextSet(ImmutableSetMultimap.copyOf(map));
}
@Nonnull
@Override
public MutableContextSet mutableCopy() {
return new MutableContextSet(map);
return new MutableContextSet(this);
}
@Nonnull
@Override
public Set<Map.Entry<String, String>> toSet() {
return ImmutableSet.copyOf(map.entries());
}
@Nonnull
@Override
public Map<String, String> toMap() {
ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
@@ -176,57 +207,42 @@ public final class MutableContextSet implements ContextSet {
return m.build();
}
@Nonnull
@Override
public Multimap<String, String> toMultimap() {
return map;
return ImmutableSetMultimap.copyOf(map);
}
@Nonnull
@Override
public boolean containsKey(String key) {
if (key == null) {
throw new NullPointerException("key");
}
return map.containsKey(key);
public boolean containsKey(@Nonnull String key) {
return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
}
@Nonnull
@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();
public Set<String> getValues(@Nonnull String key) {
Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of();
}
@Nonnull
@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);
public boolean has(@Nonnull String key, @Nonnull String value) {
return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
}
@Nonnull
@Override
public boolean hasIgnoreCase(String key, String value) {
if (key == null) {
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
value = checkNotNull(value, "value").intern();
Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
Collection<String> c = map.get(key);
if (c == null || c.isEmpty()) {
if (values == null || values.isEmpty()) {
return false;
}
for (String val : c) {
for (String val : values) {
if (val.equalsIgnoreCase(value)) {
return true;
}
@@ -251,15 +267,8 @@ public final class MutableContextSet implements ContextSet {
* @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);
public void add(@Nonnull String key, @Nonnull String value) {
map.put(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
}
/**
@@ -268,12 +277,9 @@ public final class MutableContextSet implements ContextSet {
* @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());
public void add(@Nonnull Map.Entry<String, String> entry) {
checkNotNull(entry, "entry");
add(entry.getKey(), entry.getValue());
}
/**
@@ -282,13 +288,9 @@ public final class MutableContextSet implements ContextSet {
* @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());
public void addAll(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
for (Map.Entry<String, String> e : checkNotNull(iterable, "iterable")) {
add(e);
}
}
@@ -298,11 +300,19 @@ public final class MutableContextSet implements ContextSet {
* @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());
public void addAll(@Nonnull Map<String, String> map) {
addAll(checkNotNull(map, "map").entrySet());
}
/**
* Adds the entries of a multimap to the set
*
* @param multimap the multimap to add from
* @throws NullPointerException if the map is null
* @since 3.4
*/
public void addAll(@Nonnull Multimap<String, String> multimap) {
addAll(checkNotNull(multimap, "multimap").entries());
}
/**
@@ -311,12 +321,14 @@ public final class MutableContextSet implements ContextSet {
* @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");
public void addAll(@Nonnull ContextSet contextSet) {
checkNotNull(contextSet, "contextSet");
if (contextSet instanceof MutableContextSet) {
MutableContextSet other = ((MutableContextSet) contextSet);
this.map.putAll(other.map);
} else {
addAll(contextSet.toMultimap());
}
this.map.putAll(contextSet.toMultimap());
}
/**
@@ -326,15 +338,12 @@ public final class MutableContextSet implements ContextSet {
* @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");
}
public void remove(@Nonnull String key, @Nonnull String value) {
String k = checkNotNull(key, "key").toLowerCase().intern();
String v = checkNotNull(value, "value").intern();
map.entries().removeIf(entry -> entry.getKey().equalsIgnoreCase(key) && entry.getValue().equals(value));
//noinspection StringEquality
map.entries().removeIf(entry -> entry.getKey() == k && entry.getValue() == v);
}
/**
@@ -344,15 +353,12 @@ public final class MutableContextSet implements ContextSet {
* @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");
}
public void removeIgnoreCase(@Nonnull String key, @Nonnull String value) {
String k = checkNotNull(key, "key").toLowerCase().intern();
String v = checkNotNull(value, "value").intern();
map.entries().removeIf(e -> e.getKey().equalsIgnoreCase(key) && e.getValue().equalsIgnoreCase(value));
//noinspection StringEquality
map.entries().removeIf(e -> e.getKey() == k && e.getValue().equalsIgnoreCase(v));
}
/**
@@ -361,12 +367,8 @@ public final class MutableContextSet implements ContextSet {
* @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());
public void removeAll(@Nonnull String key) {
map.removeAll(checkNotNull(key, "key").toLowerCase());
}
/**
@@ -382,14 +384,20 @@ public final class MutableContextSet implements ContextSet {
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);
final Multimap<String, String> otherContexts;
if (other instanceof MutableContextSet) {
otherContexts = ((MutableContextSet) other).map;
} else {
otherContexts = other.toMultimap();
}
return this.map.equals(otherContexts);
}
@Override
public int hashCode() {
return 59 + (this.map == null ? 43 : this.map.hashCode());
return map.hashCode();
}
@Override
@@ -0,0 +1,63 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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 javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Extension of {@link ContextCalculator} which provides the same context
* regardless of the subject.
*
* @since 4.0
*/
@FunctionalInterface
public interface StaticContextCalculator extends ContextCalculator<Object> {
/**
* Adds this calculators context to the given accumulator.
*
* @param accumulator a map of contexts to add to
* @return the map
*/
@Nonnull
MutableContextSet giveApplicableContext(@Nonnull MutableContextSet accumulator);
/**
* 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
*/
@Nonnull
@Override
@Deprecated
default MutableContextSet giveApplicableContext(@Nullable Object subject, @Nonnull MutableContextSet accumulator) {
return giveApplicableContext(accumulator);
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +27,8 @@ package me.lucko.luckperms.api.event;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
/**
* Represents an event that can be cancelled
*/
@@ -34,6 +39,7 @@ public interface Cancellable {
*
* @return the cancellation
*/
@Nonnull
AtomicBoolean getCancellationState();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +28,8 @@ package me.lucko.luckperms.api.event;
import java.util.Set;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
/**
* The LuckPerms event bus. Used for subscribing (or registering listeners) to events.
*
@@ -40,7 +45,8 @@ public interface EventBus {
* @param <T> the event class
* @return an event handler instance representing this subscription
*/
<T extends LuckPermsEvent> EventHandler<T> subscribe(Class<T> eventClass, Consumer<T> handler);
@Nonnull
<T extends LuckPermsEvent> EventHandler<T> subscribe(@Nonnull Class<T> eventClass, @Nonnull Consumer<T> handler);
/**
* Gets a set of all registered handlers for a given event
@@ -49,6 +55,7 @@ public interface EventBus {
* @param <T> the event class
* @return an immutable set of event handlers
*/
<T extends LuckPermsEvent> Set<EventHandler<T>> getHandlers(Class<T> eventClass);
@Nonnull
<T extends LuckPermsEvent> Set<EventHandler<T>> getHandlers(@Nonnull Class<T> eventClass);
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +27,8 @@ package me.lucko.luckperms.api.event;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
/**
* Represents a handler for a LuckPerms event
*
@@ -36,6 +41,7 @@ public interface EventHandler<T extends LuckPermsEvent> {
*
* @return the event class
*/
@Nonnull
Class<T> getEventClass();
/**
@@ -57,6 +63,7 @@ public interface EventHandler<T extends LuckPermsEvent> {
*
* @return the event consumer
*/
@Nonnull
Consumer<T> getConsumer();
/**
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +27,8 @@ package me.lucko.luckperms.api.event;
import me.lucko.luckperms.api.LuckPermsApi;
import javax.annotation.Nonnull;
/**
* The base event interface
*
@@ -36,6 +41,7 @@ public interface LuckPermsEvent {
*
* @return the api instance
*/
@Nonnull
LuckPermsApi getApi();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -40,6 +43,6 @@ public enum CreationCause {
/**
* The creation was caused by a LuckPerms internal
*/
INTERNAL;
INTERNAL
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -40,6 +43,6 @@ public enum DeletionCause {
/**
* The deletion was caused by a LuckPerms internal
*/
INTERNAL;
INTERNAL
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +29,8 @@ import me.lucko.luckperms.api.Group;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import me.lucko.luckperms.api.event.cause.CreationCause;
import javax.annotation.Nonnull;
/**
* Called when a group is created
*/
@@ -36,6 +41,7 @@ public interface GroupCreateEvent extends LuckPermsEvent {
*
* @return the new group
*/
@Nonnull
Group getGroup();
/**
@@ -43,6 +49,7 @@ public interface GroupCreateEvent extends LuckPermsEvent {
*
* @return the cause of the creation
*/
@Nonnull
CreationCause getCause();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,6 +31,8 @@ import me.lucko.luckperms.api.event.cause.DeletionCause;
import java.util.Set;
import javax.annotation.Nonnull;
/**
* Called when a group is deleted
*/
@@ -38,6 +43,7 @@ public interface GroupDeleteEvent extends LuckPermsEvent {
*
* @return the name of the deleted group
*/
@Nonnull
String getGroupName();
/**
@@ -45,6 +51,7 @@ public interface GroupDeleteEvent extends LuckPermsEvent {
*
* @return a copy of the groups existing data
*/
@Nonnull
Set<Node> getExistingData();
/**
@@ -52,6 +59,7 @@ public interface GroupDeleteEvent extends LuckPermsEvent {
*
* @return the cause of the deletion
*/
@Nonnull
DeletionCause getCause();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +28,8 @@ package me.lucko.luckperms.api.event.group;
import me.lucko.luckperms.api.Group;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import javax.annotation.Nonnull;
/**
* Called when a group is loaded into memory from the storage.
*
@@ -37,6 +42,7 @@ public interface GroupLoadEvent extends LuckPermsEvent {
*
* @return the group that was loaded
*/
@Nonnull
Group getGroup();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +29,8 @@ import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.api.event.Cancellable;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import javax.annotation.Nonnull;
/**
* Called when a log entry is about to be sent to notifiable players on the platform
*/
@@ -36,6 +41,34 @@ public interface LogBroadcastEvent extends LuckPermsEvent, Cancellable {
*
* @return the log entry to be broadcasted
*/
@Nonnull
LogEntry getEntry();
/**
* Gets where the log entry originated from.
*
* @return the origin of the log
* @since 3.3
*/
@Nonnull
Origin getOrigin();
/**
* Represents where a log entry is from
*
* @since 3.3
*/
enum Origin {
/**
* Represents a log entry which originated from the current server instance
*/
LOCAL,
/**
* Represents a log entry which was sent to this server via the messaging service
*/
REMOTE
}
}
@@ -0,0 +1,59 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.event.log;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.api.event.Cancellable;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Called when a log is about to be published to the network via the MessagingService
*
* @since 3.3
*/
public interface LogNetworkPublishEvent extends LuckPermsEvent, Cancellable {
/**
* Gets the ID of the log entry being published
*
* @return the id of the log entry being published
*/
@Nonnull
UUID getLogId();
/**
* Gets the log entry to be published
*
* @return the log entry to be published
*/
@Nonnull
LogEntry getEntry();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +29,8 @@ import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.api.event.Cancellable;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import javax.annotation.Nonnull;
/**
* Called when a log is about to be published to the storage file/table
*/
@@ -36,6 +41,7 @@ public interface LogPublishEvent extends LuckPermsEvent, Cancellable {
*
* @return the log entry to be published
*/
@Nonnull
LogEntry getEntry();
}
@@ -0,0 +1,58 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.event.log;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Called when a log entry is received via the MessagingService
*
* @since 3.3
*/
public interface LogReceiveEvent extends LuckPermsEvent {
/**
* Gets the ID of the log entry being received
*
* @return the id of the log entry being received
*/
@Nonnull
UUID getLogId();
/**
* Gets the log entry being received
*
* @return the log entry being received
*/
@Nonnull
LogEntry getEntry();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +27,8 @@ package me.lucko.luckperms.api.event.node;
import me.lucko.luckperms.api.Node;
import javax.annotation.Nonnull;
/**
* Called when a node is added to a holder
*/
@@ -34,6 +39,7 @@ public interface NodeAddEvent extends NodeMutateEvent {
*
* @return the node that was added
*/
@Nonnull
Node getNode();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,6 +31,8 @@ import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.Set;
import javax.annotation.Nonnull;
/**
* Called when a node is added to/removed from a user/group
*/
@@ -38,6 +43,7 @@ public interface NodeMutateEvent extends LuckPermsEvent {
*
* @return the event target
*/
@Nonnull
PermissionHolder getTarget();
/**
@@ -45,6 +51,7 @@ public interface NodeMutateEvent extends LuckPermsEvent {
*
* @return the data before the change
*/
@Nonnull
Set<Node> getDataBefore();
/**
@@ -52,6 +59,7 @@ public interface NodeMutateEvent extends LuckPermsEvent {
*
* @return the data after the change
*/
@Nonnull
Set<Node> getDataAfter();
/**
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +27,8 @@ package me.lucko.luckperms.api.event.node;
import me.lucko.luckperms.api.Node;
import javax.annotation.Nonnull;
/**
* Called when a node is removed from a holder
*/
@@ -34,6 +39,7 @@ public interface NodeRemoveEvent extends NodeMutateEvent {
*
* @return the node that was removed
*/
@Nonnull
Node getNode();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -27,8 +30,10 @@ import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Called before a network sync task runs
* Called before a received network sync task runs
*/
public interface PreNetworkSyncEvent extends LuckPermsEvent, Cancellable {
@@ -37,6 +42,7 @@ public interface PreNetworkSyncEvent extends LuckPermsEvent, Cancellable {
*
* @return the id of the sync request
*/
@Nonnull
UUID getSyncId();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +29,8 @@ import me.lucko.luckperms.api.Track;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import me.lucko.luckperms.api.event.cause.CreationCause;
import javax.annotation.Nonnull;
/**
* Called when a track is created
*/
@@ -36,6 +41,7 @@ public interface TrackCreateEvent extends LuckPermsEvent {
*
* @return the new track
*/
@Nonnull
Track getTrack();
/**
@@ -43,6 +49,7 @@ public interface TrackCreateEvent extends LuckPermsEvent {
*
* @return the cause of the creation
*/
@Nonnull
CreationCause getCause();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -27,6 +30,8 @@ import me.lucko.luckperms.api.event.cause.DeletionCause;
import java.util.List;
import javax.annotation.Nonnull;
/**
* Called when a track is deleted
*/
@@ -37,6 +42,7 @@ public interface TrackDeleteEvent extends LuckPermsEvent {
*
* @return the name of the deleted track
*/
@Nonnull
String getTrackName();
/**
@@ -44,6 +50,7 @@ public interface TrackDeleteEvent extends LuckPermsEvent {
*
* @return a copy of the tracks existing data
*/
@Nonnull
List<String> getExistingData();
/**
@@ -51,6 +58,7 @@ public interface TrackDeleteEvent extends LuckPermsEvent {
*
* @return the cause of the deletion
*/
@Nonnull
DeletionCause getCause();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +28,8 @@ package me.lucko.luckperms.api.event.track;
import me.lucko.luckperms.api.Track;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import javax.annotation.Nonnull;
/**
* Called when a track is loaded into memory from the storage.
*
@@ -37,6 +42,7 @@ public interface TrackLoadEvent extends LuckPermsEvent {
*
* @return the track that was loaded
*/
@Nonnull
Track getTrack();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +25,8 @@
package me.lucko.luckperms.api.event.track.mutate;
import javax.annotation.Nonnull;
/**
* Called when a group is added to a track
*/
@@ -32,6 +37,7 @@ public interface TrackAddGroupEvent extends TrackMutateEvent {
*
* @return the group that was added
*/
@Nonnull
String getGroup();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -27,6 +30,8 @@ import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.List;
import javax.annotation.Nonnull;
/**
* Called when a track is changed
*/
@@ -37,6 +42,7 @@ public interface TrackMutateEvent extends LuckPermsEvent {
*
* @return the track that was mutated
*/
@Nonnull
Track getTrack();
/**
@@ -44,6 +50,7 @@ public interface TrackMutateEvent extends LuckPermsEvent {
*
* @return the data before the change
*/
@Nonnull
List<String> getDataBefore();
/**
@@ -51,6 +58,7 @@ public interface TrackMutateEvent extends LuckPermsEvent {
*
* @return the data after the change
*/
@Nonnull
List<String> getDataAfter();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +25,8 @@
package me.lucko.luckperms.api.event.track.mutate;
import javax.annotation.Nonnull;
/**
* Called when a group is removed from a track
*/
@@ -32,6 +37,7 @@ public interface TrackRemoveGroupEvent extends TrackMutateEvent {
*
* @return the group that was removed
*/
@Nonnull
String getGroup();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +29,8 @@ import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import javax.annotation.Nonnull;
/**
* Called when a users {@link me.lucko.luckperms.api.caching.UserData} is loaded.
*/
@@ -36,6 +41,7 @@ public interface UserCacheLoadEvent extends LuckPermsEvent {
*
* @return the user
*/
@Nonnull
User getUser();
/**
@@ -43,6 +49,7 @@ public interface UserCacheLoadEvent extends LuckPermsEvent {
*
* @return the loaded data
*/
@Nonnull
UserData getLoadedData();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +29,8 @@ import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import javax.annotation.Nonnull;
/**
* Called when a users cached data is refreshed
*/
@@ -36,6 +41,7 @@ public interface UserDataRecalculateEvent extends LuckPermsEvent {
*
* @return the user
*/
@Nonnull
User getUser();
/**
@@ -43,6 +49,7 @@ public interface UserDataRecalculateEvent extends LuckPermsEvent {
*
* @return the data
*/
@Nonnull
UserData getData();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +29,8 @@ import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Called when the user logs into the network for the first time.
*
@@ -44,6 +49,7 @@ public interface UserFirstLoginEvent extends LuckPermsEvent {
*
* @return the uuid of the user
*/
@Nonnull
UUID getUuid();
/**
@@ -51,6 +57,7 @@ public interface UserFirstLoginEvent extends LuckPermsEvent {
*
* @return the username of the user
*/
@Nonnull
String getUsername();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,6 +28,8 @@ package me.lucko.luckperms.api.event.user;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import javax.annotation.Nonnull;
/**
* Called when a user is loaded into memory from the storage.
*/
@@ -35,6 +40,7 @@ public interface UserLoadEvent extends LuckPermsEvent {
*
* @return the user that was loaded
*/
@Nonnull
User getUser();
}
@@ -0,0 +1,72 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.event.user;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Called when LuckPerms has finished processing a certain Player's connection.
*
* <p>This event will always execute during the platforms async login/auth event.
* All handlers will be called instantly.</p>
*
* <p>This, among other things, allows you to wait until permission data is loaded
* for a User during the BungeeCord 'LoginEvent', as event priorities are ignored
* by the current implementation.</p>
*
* @since 3.4
*/
public interface UserLoginProcessEvent extends LuckPermsEvent {
/**
* Gets the UUID of the connection which was processed
*
* @return the uuid of the connection which was processed
*/
@Nonnull
UUID getUuid();
/**
* Gets the username of the connection which was processed
*
* @return the username of the connection which was processed
*/
@Nonnull
String getUsername();
/**
* Gets the resultant User instance which was loaded.
*
* @return the user instance
*/
User getUser();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,6 +38,6 @@ public enum TrackAction {
/**
* The user was demoted down a track
*/
DEMOTION;
DEMOTION
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,6 +31,8 @@ import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Called when a user interacts with a track through a promotion or demotion
*/
@@ -38,6 +43,7 @@ public interface UserTrackEvent extends LuckPermsEvent {
*
* @return the track involved in the event
*/
@Nonnull
Track getTrack();
/**
@@ -45,6 +51,7 @@ public interface UserTrackEvent extends LuckPermsEvent {
*
* @return the user involved in the event
*/
@Nonnull
User getUser();
/**
@@ -52,6 +59,7 @@ public interface UserTrackEvent extends LuckPermsEvent {
*
* @return the action performed
*/
@Nonnull
TrackAction getAction();
/**
@@ -61,6 +69,7 @@ public interface UserTrackEvent extends LuckPermsEvent {
*
* @return the group the user was promoted/demoted from
*/
@Nonnull
Optional<String> getGroupFrom();
/**
@@ -68,6 +77,7 @@ public interface UserTrackEvent extends LuckPermsEvent {
*
* @return the group the user was promoted/demoted to
*/
@Nonnull
Optional<String> getGroupTo();
}
@@ -0,0 +1,84 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.manager;
import me.lucko.luckperms.api.Group;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Represents the object responsible for managing {@link Group} instances.
*
* @since 4.0
*/
public interface GroupManager {
/**
* 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
*/
@Nullable
Group getGroup(@Nonnull String name);
/**
* Gets a wrapped group object from the group storage.
*
* <p>This method does not return null, unlike {@link #getGroup}</p>
*
* @param name the name of the group to get
* @return an optional {@link Group} object
* @throws NullPointerException if the name is null
*/
@Nonnull
default Optional<Group> getGroupOpt(@Nonnull String name) {
return Optional.ofNullable(getGroup(name));
}
/**
* Gets a set of all loaded groups.
*
* @return a {@link Set} of {@link Group} objects
*/
@Nonnull
Set<Group> getLoadedGroups();
/**
* 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
*/
boolean isLoaded(@Nonnull String name);
}
@@ -0,0 +1,84 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.manager;
import me.lucko.luckperms.api.Track;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Represents the object responsible for managing {@link Track} instances.
*
* @since 4.0
*/
public interface TrackManager {
/**
* 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
*/
@Nullable
Track getTrack(@Nonnull String name);
/**
* Gets a wrapped track object from the track storage.
*
* <p>This method does not return null, unlike {@link #getTrack}</p>
*
* @param name the name of the track to get
* @return an optional {@link Track} object
* @throws NullPointerException if the name is null
*/
@Nonnull
default Optional<Track> getTrackOpt(@Nonnull String name) {
return Optional.ofNullable(getTrack(name));
}
/**
* Gets a set of all loaded tracks.
*
* @return a {@link Set} of {@link Track} objects
*/
@Nonnull
Set<Track> getLoadedTracks();
/**
* 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 isLoaded(@Nonnull String name);
}
@@ -0,0 +1,113 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.manager;
import me.lucko.luckperms.api.User;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Represents the object responsible for managing {@link User} instances.
*
* @since 4.0
*/
public interface UserManager {
/**
* 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
*/
@Nullable
User getUser(@Nonnull UUID uuid);
/**
* Gets a wrapped user object from the user storage.
*
* @param uuid the uuid of the user to get
* @return an optional {@link User} object
* @throws NullPointerException if the uuid is null
*/
@Nonnull
default Optional<User> getUserOpt(@Nonnull UUID uuid) {
return Optional.ofNullable(getUser(uuid));
}
/**
* 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
*/
@Nullable
User getUser(@Nonnull String name);
/**
* Gets a wrapped user object from the user storage.
*
* @param name the username of the user to get
* @return an optional {@link User} object
* @throws NullPointerException if the name is null
*/
@Nonnull
default Optional<User> getUserOpt(@Nonnull String name) {
return Optional.ofNullable(getUser(name));
}
/**
* Gets a set of all loaded users.
*
* @return a {@link Set} of {@link User} objects
*/
@Nonnull
Set<User> getLoadedUsers();
/**
* 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
*/
boolean isLoaded(@Nonnull UUID uuid);
/**
* 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
*/
void cleanupUser(@Nonnull User user);
}
@@ -0,0 +1,77 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.metastacking;
import java.util.List;
import javax.annotation.Nonnull;
/**
* Represents a meta stack model, consisting of a chain of elements, separated by spacers.
*
* <p>The resultant string is constructed as:
* [start spacer] [element] [middle spacer] [element] [middle spacer] [element] [end spacer]</p>
*
* <p>Definitions can be passed to a users UserData instance using MetaContexts, and the result of this stack can be
* retrieved from the returned MetaData instance.</p>
*
* @since 2.3
*/
public interface MetaStackDefinition {
/**
* Gets an immutable list of the elements in this stack definition
*
* @return the elements in this stack
*/
@Nonnull
List<MetaStackElement> getElements();
/**
* Gets the spacer string added before any stack elements
*
* @return the start spacer
*/
@Nonnull
String getStartSpacer();
/**
* Gets the spacer added between stack elements
*
* @return the middle spacer
*/
@Nonnull
String getMiddleSpacer();
/**
* Gets the spacer added after any stack elements
*
* @return the end spacer
*/
@Nonnull
String getEndSpacer();
}
@@ -0,0 +1,56 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.metastacking;
import me.lucko.luckperms.api.ChatMetaType;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Represents an element within a {@link MetaStackDefinition}.
*
* @since 3.2
*/
public interface MetaStackElement {
/**
* Returns if the given node should be accumulated onto the stack.
*
* <p>The element being considered can be retrieved using {@link ChatMetaType#getEntry(Node)}.</p>
*
* @param node the node being considered
* @param type the type of entry being accumulated
* @param current the current value being used. If this returns true, the current value will be replaced by this entry
* @return true if the node should be accumulated into this element, replacing the current value
*/
boolean shouldAccumulate(@Nonnull LocalizedNode node, @Nonnull ChatMetaType type, @Nullable Map.Entry<Integer, String> current);
}
@@ -0,0 +1,72 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.metastacking;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
/**
* Factory to create meta stack elements and definitions.
*
* @since 3.2
*/
public interface MetaStackFactory {
/**
* Parses a standard {@link MetaStackElement} from string, using the pre-defined elements in the plugin.
*
* @param definition the definition
* @return the parsed element, if present
*/
@Nonnull
Optional<MetaStackElement> fromString(@Nonnull String definition);
/**
* Parses a list of {@link MetaStackElement}s from string, using the pre-defined elements in the plugin.
*
* <p>If an element cannot be parsed, it will not be included in the resultant list.</p>
*
* @param definitions the definition strings
* @return a list of parsed elements
*/
@Nonnull
List<MetaStackElement> fromStrings(@Nonnull List<String> definitions);
/**
* Creates a new {@link MetaStackDefinition} with the given properties.
*
* @param elements the elements to be included in the stack.
* @param startSpacer the spacer to be included at the start of the stacks output
* @param middleSpacer the spacer to be included between stack elements
* @param endSpacer the spacer to be included at the end of the stacks output
* @return the new stack definition instance
*/
@Nonnull
MetaStackDefinition createDefinition(@Nonnull List<MetaStackElement> elements, @Nonnull String startSpacer, @Nonnull String middleSpacer, @Nonnull String endSpacer);
}
@@ -0,0 +1,78 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.platform;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Provides information about the platform LuckPerms is running on.
*
* @since 4.0
*/
public interface PlatformInfo {
/**
* Gets the plugin version
*
* @return the version of the plugin running on the platform
*/
@Nonnull
String getVersion();
/**
* Gets the API version
*
* @return the version of the API running on the platform
*/
double getApiVersion();
/**
* Gets the type of platform LuckPerms is running on
*
* @return the type of platform LuckPerms is running on
*/
@Nonnull
PlatformType getType();
/**
* Gets the unique players which have connected to the server since it started.
*
* @return the unique connections
*/
@Nonnull
Set<UUID> getUniqueConnections();
/**
* Gets the time when the plugin first started in milliseconds.
*
* @return the enable time
*/
long getStartTime();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,10 +23,12 @@
* SOFTWARE.
*/
package me.lucko.luckperms.api;
package me.lucko.luckperms.api.platform;
import javax.annotation.Nonnull;
/**
* The platforms which LuckPerms can run on
* Represents a type of platform which LuckPerms can run on.
*
* @since 2.7
*/
@@ -39,6 +44,12 @@ public enum PlatformType {
this.friendlyName = friendlyName;
}
/**
* Gets a readable name for the platform type.
*
* @return a readable name
*/
@Nonnull
public String getFriendlyName() {
return friendlyName;
}
@@ -1,32 +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.exceptions;
/**
* 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 {
}
@@ -1,30 +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.exceptions;
/**
* Thrown when a permission holding object already has a permission, is already a member of a group, or when a track
* already contains a group.
*/
public class ObjectAlreadyHasException extends MembershipException {
}
@@ -1,30 +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.exceptions;
/**
* Thrown when a permission holding object does not already have a permission, is not already a member of a group,
* or when a track doesn't contain a group.
*/
public class ObjectLacksException extends MembershipException {
}
+41 -19
View File
@@ -5,32 +5,20 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>3.0-SNAPSHOT</version>
<version>4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms-bukkit-legacy</artifactId>
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPerms-Bukkit-Legacy-${release.version}.${patch.version}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>../</directory>
<filtering>false</filtering>
<includes>
<include>LICENSE.txt</include>
</includes>
</resource>
</resources>
<finalName>LuckPerms-Bukkit-Legacy-${full.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>${compiler.version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -39,7 +27,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<version>${shade.version}</version>
<executions>
<execution>
<phase>package</phase>
@@ -59,8 +47,8 @@
<shadedPattern>me.lucko.luckperms.lib.gson</shadedPattern>
</relocation>
<relocation>
<pattern>io.github.mkremins.fanciful</pattern>
<shadedPattern>me.lucko.luckperms.lib.fanciful</shadedPattern>
<pattern>net.kyori.text</pattern>
<shadedPattern>me.lucko.luckperms.lib.text</shadedPattern>
</relocation>
</relocations>
</configuration>
@@ -70,6 +58,30 @@
</plugins>
</build>
<profiles>
<profile>
<id>sign</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<!-- LuckPerms Bukkit -->
<dependency>
@@ -77,6 +89,16 @@
<artifactId>luckperms-bukkit</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-common</artifactId>
</exclusion>
<exclusion>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Shade gson and guava, as they are not included in earlier Bukkit releases -->
@@ -1,220 +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.placeholders;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.util.TimeUtil;
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>
* - inherits_group_<name>
* - on_track_<track>
* - expiry_time_<node>
* - group_expiry_time_<group>
* - prefix
* - suffix
* - meta_<node>
*/
public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
private static final String IDENTIFIER = "luckperms";
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
public boolean canRegister() {
return Bukkit.getServicesManager().isProvidedFor(LuckPermsApi.class);
}
@Override
public boolean register() {
if (!canRegister()) {
return false;
}
api = Bukkit.getServicesManager().getRegistration(LuckPermsApi.class).getProvider();
return super.register();
}
@Override
public String getVersion() {
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
public String onPlaceholderRequest(Player player, String identifier) {
if (player == null || api == null) {
return "";
}
Optional<User> u = api.getUserSafe(api.getUuidCache().getUUID(player.getUniqueId()));
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();
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));
}
if (identifier.startsWith("inherits_permission_") && identifier.length() > "inherits_permission_".length()) {
String node = identifier.substring("inherits_permission_".length());
return formatBoolean(data.getPermissionData(makeContexts(player)).getPermissionValue(node).asBoolean());
}
if (identifier.startsWith("in_group_") && identifier.length() > "in_group_".length()) {
String groupName = identifier.substring("in_group_".length());
return formatBoolean(user.getGroupNames().contains(groupName));
}
if (identifier.startsWith("inherits_group_") && identifier.length() > "inherits_group_".length()) {
String groupName = identifier.substring("inherits_group_".length());
return formatBoolean(data.getPermissionData(makeContexts(player)).getPermissionValue("group." + groupName).asBoolean());
}
if (identifier.startsWith("on_track_") && identifier.length() > "on_track_".length()) {
String trackName = identifier.substring("on_track_".length());
Optional<Track> track = api.getTrackSafe(trackName);
if (!track.isPresent()) return "";
return formatBoolean(track.get().containsGroup(user.getPrimaryGroup()));
}
if (identifier.startsWith("expiry_time_") && identifier.length() > "expiry_time_".length()) {
String node = identifier.substring("expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.toSerializedNode().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
}
return "";
}
if (identifier.startsWith("group_expiry_time_") && identifier.length() > "group_expiry_time_".length()) {
String node = "group." + identifier.substring("group_expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.getPermission().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
}
return "";
}
if (identifier.equalsIgnoreCase("prefix")) {
String prefix = data.calculateMeta(makeContexts(player)).getPrefix();
if (prefix == null) {
prefix = "";
}
return prefix;
}
if (identifier.equalsIgnoreCase("suffix")) {
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());
String meta = data.getMetaData(makeContexts(player)).getMeta().get(node);
if (meta == null) {
meta = "";
}
return meta;
}
return null;
}
@Override
public String getIdentifier() {
return IDENTIFIER;
}
@Override
public String getPlugin() {
return PLUGIN_NAME;
}
@Override
public String getAuthor() {
return AUTHOR;
}
}
+72 -34
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>3.0-SNAPSHOT</version>
<version>4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -14,26 +14,18 @@
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPerms-Bukkit-${release.version}.${patch.version}</finalName>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<finalName>LuckPerms-Bukkit-${full.version}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>../</directory>
<filtering>false</filtering>
<includes>
<include>LICENSE.txt</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>${compiler.version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -42,7 +34,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<version>${shade.version}</version>
<executions>
<execution>
<phase>package</phase>
@@ -54,8 +46,8 @@
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>io.github.mkremins.fanciful</pattern>
<shadedPattern>me.lucko.luckperms.lib.fanciful</shadedPattern>
<pattern>net.kyori.text</pattern>
<shadedPattern>me.lucko.luckperms.lib.text</shadedPattern>
</relocation>
</relocations>
</configuration>
@@ -65,12 +57,36 @@
</plugins>
</build>
<profiles>
<profile>
<id>sign</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${gpg.version}</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<!-- BukkitAPI -->
<!-- Spigot API -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<version>1.12-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Vault -->
@@ -79,6 +95,12 @@
<artifactId>VaultAPI</artifactId>
<version>1.6</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- LuckPerms Common -->
<dependency>
@@ -94,38 +116,33 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<!-- LilyPad - used as a messaging service -->
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- LilyPad -->
<dependency>
<groupId>lilypad.client.connect</groupId>
<artifactId>api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>provided</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>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
<scope>provided</scope>
</dependency>
<!-- **************Migration Plugins************** -->
<!-- ************** Migration Plugins ************** -->
<!-- zPermissions -->
<dependency>
<groupId>org.tyrannyofheaven.bukkit</groupId>
<artifactId>zPermissions</artifactId>
<version>1.3</version>
<scope>provided</scope>
</dependency>
<!-- PermissionsEx -->
<dependency>
<groupId>ru.tehkode</groupId>
<artifactId>PermissionsEx</artifactId>
<version>1.23</version>
<version>1.23.5</version>
<scope>provided</scope>
<exclusions>
<exclusion>
@@ -136,20 +153,41 @@
<groupId>net.gravitydevelopment.updater</groupId>
<artifactId>updater</artifactId>
</exclusion>
<exclusion>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</exclusion>
<exclusion>
<groupId>com.mojang</groupId>
<artifactId>AccountsClient</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- PowerfulPerms -->
<dependency>
<groupId>com.github.cheesesoftware</groupId>
<artifactId>PowerfulPermsAPI</artifactId>
<version>1.0.1</version>
<version>1.4.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- GroupManager -->
<dependency>
<groupId>org.anjocaido</groupId>
<artifactId>GroupManager</artifactId>
<version>1.4</version>
<scope>provided</scope>
</dependency>
<!-- bPermissions -->
<dependency>
<groupId>de.bananaco</groupId>
<artifactId>bpermissions-api</artifactId>
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,37 +29,41 @@ import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.common.commands.CommandManager;
import me.lucko.luckperms.common.commands.sender.Sender;
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;
import org.bukkit.command.TabExecutor;
import java.util.Arrays;
import java.util.List;
public class BukkitCommand extends CommandManager implements CommandExecutor, TabExecutor {
public class BukkitCommandExecutor extends CommandManager implements CommandExecutor, TabExecutor {
private static final Splitter ARGUMENT_SPLITTER = Splitter.on(COMMAND_SEPARATOR_PATTERN).omitEmptyStrings();
private static final Joiner ARGUMENT_JOINER = Joiner.on(' ');
private final LPBukkitPlugin plugin;
BukkitCommand(LPBukkitPlugin plugin) {
BukkitCommandExecutor(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
onCommand(
plugin.getSenderFactory().wrap(sender),
label,
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args)))
);
Sender lpSender = plugin.getSenderFactory().wrap(sender);
List<String> arguments = Util.stripQuotes(ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
onCommand(lpSender, label, arguments);
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
return onTabComplete(plugin.getSenderFactory().wrap(sender), Arrays.asList(args));
Sender lpSender = plugin.getSenderFactory().wrap(sender);
List<String> arguments = Util.stripQuotes(ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
return onTabComplete(lpSender, arguments);
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,20 +28,20 @@ package me.lucko.luckperms.bukkit;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.common.config.AbstractConfiguration;
import me.lucko.luckperms.common.config.ConfigurationAdapter;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.Set;
@RequiredArgsConstructor
public class BukkitConfig extends AbstractConfiguration {
public class BukkitConfigAdapter implements ConfigurationAdapter {
@Getter
private final LPBukkitPlugin plugin;
@@ -57,6 +60,11 @@ public class BukkitConfig extends AbstractConfiguration {
configuration = YamlConfiguration.loadConfiguration(configFile);
}
@Override
public boolean contains(String path) {
return configuration.contains(path);
}
@Override
public String getString(String path, String def) {
return configuration.getString(path, def);
@@ -74,7 +82,8 @@ public class BukkitConfig extends AbstractConfiguration {
@Override
public List<String> getList(String path, List<String> def) {
return Optional.ofNullable(configuration.getStringList(path)).orElse(def);
List<String> ret = configuration.getStringList(path);
return ret == null ? def : ret;
}
@Override
@@ -84,7 +93,8 @@ public class BukkitConfig extends AbstractConfiguration {
return def;
}
return Optional.ofNullable(section.getKeys(false).stream().collect(Collectors.toList())).orElse(def);
Set<String> keys = section.getKeys(false);
return keys == null ? def : new ArrayList<>(keys);
}
@Override
@@ -1,190 +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.bukkit;
import me.lucko.luckperms.bukkit.model.Injector;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import me.lucko.luckperms.common.config.ConfigKeys;
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.PlayerChangedWorldEvent;
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.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
class BukkitListener extends AbstractListener implements Listener {
private final LPBukkitPlugin plugin;
private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>());
private final Set<UUID> deniedLogin = new HashSet<>();
BukkitListener(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) {
if (e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
deniedAsyncLogin.add(e.getUniqueId());
return;
}
if (!plugin.isStarted() || !plugin.getStorage().isAcceptingLogins()) {
deniedAsyncLogin.add(e.getUniqueId());
// 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 they were denied before/at LOW, then don't bother handling here.
if (deniedAsyncLogin.remove(e.getUniqueId())) {
return;
}
if (plugin.isStarted() && plugin.getStorage().isAcceptingLogins() && e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
// Login event was cancelled by another plugin
onLeave(e.getUniqueId());
}
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLogin(PlayerLoginEvent e) {
if (e.getResult() != PlayerLoginEvent.Result.ALLOWED) {
deniedLogin.add(e.getPlayer().getUniqueId());
return;
}
final Player player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) {
deniedLogin.add(e.getPlayer().getUniqueId());
// 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();
}
plugin.refreshAutoOp(player);
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) {
// If they were denied before/at LOW, then don't bother handling here.
if (deniedLogin.remove(e.getPlayer().getUniqueId())) {
return;
}
// The player got denied on sync login.
plugin.getServer().getScheduler().runTaskLater(plugin, () -> onLeave(e.getPlayer().getUniqueId()), 20L);
} else {
plugin.refreshAutoOp(e.getPlayer());
}
}
@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();
// Remove the custom permissible
Injector.unInject(player, true, true);
// Handle auto op
if (plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) {
player.setOp(false);
}
// Call internal leave handling
onLeave(player.getUniqueId());
}
@EventHandler
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
if (plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
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);
}
}
@EventHandler
public void onWorldChange(PlayerChangedWorldEvent e) {
plugin.refreshAutoOp(e.getPlayer());
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,8 +27,9 @@ package me.lucko.luckperms.bukkit;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import me.lucko.luckperms.common.plugin.LuckPermsScheduler;
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
import org.bukkit.scheduler.BukkitTask;
@@ -36,78 +40,79 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class LPBukkitScheduler implements LuckPermsScheduler {
public class BukkitSchedulerAdapter implements SchedulerAdapter {
private final LPBukkitPlugin plugin;
@Getter
private ExecutorService asyncLpExecutor;
@Accessors(fluent = true)
private ExecutorService asyncLp;
@Getter
private Executor asyncBukkitExecutor;
private Executor syncExecutor;
@Accessors(fluent = true)
private Executor asyncBukkit;
@Getter
@Accessors(fluent = true)
private Executor sync;
@Getter
@Accessors(fluent = true)
private Executor async;
@Getter
@Setter
private boolean useBukkitAsync = false;
private Set<BukkitTask> tasks = ConcurrentHashMap.newKeySet();
private final Set<BukkitTask> tasks = ConcurrentHashMap.newKeySet();
public LPBukkitScheduler(LPBukkitPlugin plugin) {
public BukkitSchedulerAdapter(LPBukkitPlugin plugin) {
this.plugin = plugin;
this.asyncLpExecutor = Executors.newCachedThreadPool();
this.asyncBukkitExecutor = r -> plugin.getServer().getScheduler().runTaskAsynchronously(plugin, r);
this.syncExecutor = r -> plugin.getServer().getScheduler().runTask(plugin, r);
this.asyncLp = Executors.newCachedThreadPool();
this.asyncBukkit = r -> plugin.getServer().getScheduler().runTaskAsynchronously(plugin, r);
this.sync = r -> plugin.getServer().getScheduler().runTask(plugin, r);
this.async = r -> (useBukkitAsync ? asyncBukkit : asyncLp).execute(r);
}
@Override
public Executor getAsyncExecutor() {
return useBukkitAsync ? asyncBukkitExecutor : asyncLpExecutor;
public void doAsync(Runnable runnable) {
async().execute(runnable);
}
@Override
public Executor getSyncExecutor() {
return syncExecutor;
public void doSync(Runnable runnable) {
sync().execute(runnable);
}
@Override
public void doAsync(Runnable r) {
getAsyncExecutor().execute(r);
}
@Override
public void doSync(Runnable r) {
getSyncExecutor().execute(r);
}
@Override
public void doAsyncRepeating(Runnable r, long interval) {
BukkitTask task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, r, interval, interval);
public void asyncRepeating(Runnable runnable, long intervalTicks) {
BukkitTask task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, runnable, intervalTicks, intervalTicks);
tasks.add(task);
}
@Override
public void doSyncRepeating(Runnable r, long interval) {
BukkitTask task = plugin.getServer().getScheduler().runTaskTimer(plugin, r, interval, interval);
public void syncRepeating(Runnable runnable, long intervalTicks) {
BukkitTask task = plugin.getServer().getScheduler().runTaskTimer(plugin, runnable, intervalTicks, intervalTicks);
tasks.add(task);
}
@Override
public void doAsyncLater(Runnable r, long delay) {
plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, r, delay);
public void asyncLater(Runnable runnable, long delayTicks) {
plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, runnable, delayTicks);
}
@Override
public void doSyncLater(Runnable r, long delay) {
plugin.getServer().getScheduler().runTaskLater(plugin, r, delay);
public void syncLater(Runnable runnable, long delayTicks) {
plugin.getServer().getScheduler().runTaskLater(plugin, runnable, delayTicks);
}
@Override
public void shutdown() {
tasks.forEach(BukkitTask::cancel);
// wait for executor
asyncLpExecutor.shutdown();
asyncLp.shutdown();
try {
asyncLpExecutor.awaitTermination(30, TimeUnit.SECONDS);
asyncLp.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -22,16 +25,20 @@
package me.lucko.luckperms.bukkit;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.compat.MessageHandler;
import me.lucko.luckperms.common.commands.sender.SenderFactory;
import me.lucko.luckperms.common.constants.Constants;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import net.kyori.text.Component;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import io.github.mkremins.fanciful.FancyMessage;
import java.util.UUID;
public class BukkitSenderFactory extends SenderFactory<CommandSender> {
@@ -60,16 +67,43 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
@Override
protected void sendMessage(CommandSender sender, String s) {
// send sync if command block
if (sender instanceof BlockCommandSender) {
getPlugin().getScheduler().doSync(new BlockMessageAgent(((BlockCommandSender) sender), s));
return;
}
sender.sendMessage(s);
}
@Override
protected void sendMessage(CommandSender sender, FancyMessage message) {
protected void sendMessage(CommandSender sender, Component message) {
messageHandler.sendJsonMessage(sender, message);
}
@Override
protected Tristate getPermissionValue(CommandSender sender, String node) {
boolean isSet = sender.isPermissionSet(node);
boolean val = sender.hasPermission(node);
return !isSet ? val ? Tristate.TRUE : Tristate.UNDEFINED : Tristate.fromBoolean(val);
}
@Override
protected boolean hasPermission(CommandSender sender, String node) {
return sender.hasPermission(node);
}
@AllArgsConstructor
private static final class BlockMessageAgent implements Runnable {
private final BlockCommandSender block;
private final String message;
@Override
public void run() {
block.sendMessage(message);
}
}
}
@@ -1,5 +1,8 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,56 +28,63 @@ package me.lucko.luckperms.bukkit;
import lombok.Getter;
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.messaging.BungeeMessagingService;
import me.lucko.luckperms.bukkit.messaging.LilyPadMessagingService;
import me.lucko.luckperms.bukkit.model.ChildPermissionProvider;
import me.lucko.luckperms.bukkit.model.DefaultsProvider;
import me.lucko.luckperms.api.platform.PlatformType;
import me.lucko.luckperms.bukkit.calculators.BukkitCalculatorFactory;
import me.lucko.luckperms.bukkit.contexts.BukkitContextManager;
import me.lucko.luckperms.bukkit.contexts.WorldCalculator;
import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener;
import me.lucko.luckperms.bukkit.listeners.BukkitPlatformListener;
import me.lucko.luckperms.bukkit.messaging.BukkitMessagingFactory;
import me.lucko.luckperms.bukkit.model.Injector;
import me.lucko.luckperms.bukkit.model.LPPermissible;
import me.lucko.luckperms.bukkit.vault.VaultHook;
import me.lucko.luckperms.common.api.ApiHandler;
import me.lucko.luckperms.bukkit.model.SubscriptionMapInjector;
import me.lucko.luckperms.bukkit.processors.BukkitProcessorsSetupTask;
import me.lucko.luckperms.bukkit.processors.ChildPermissionProvider;
import me.lucko.luckperms.bukkit.processors.DefaultsProvider;
import me.lucko.luckperms.bukkit.vault.VaultHookManager;
import me.lucko.luckperms.common.actionlog.LogDispatcher;
import me.lucko.luckperms.common.api.ApiProvider;
import me.lucko.luckperms.common.api.ApiSingletonUtils;
import me.lucko.luckperms.common.buffers.BufferedRequest;
import me.lucko.luckperms.common.buffers.UpdateTaskBuffer;
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.AbstractConfiguration;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.config.LuckPermsConfiguration;
import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.constants.CommandPermission;
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.debug.DebugHandler;
import me.lucko.luckperms.common.contexts.LuckPermsCalculator;
import me.lucko.luckperms.common.dependencies.Dependency;
import me.lucko.luckperms.common.dependencies.DependencyManager;
import me.lucko.luckperms.common.locale.LocaleManager;
import me.lucko.luckperms.common.locale.NoopLocaleManager;
import me.lucko.luckperms.common.locale.SimpleLocaleManager;
import me.lucko.luckperms.common.logging.Logger;
import me.lucko.luckperms.common.logging.SenderLogger;
import me.lucko.luckperms.common.managers.GenericGroupManager;
import me.lucko.luckperms.common.managers.GenericTrackManager;
import me.lucko.luckperms.common.managers.GenericUserManager;
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.InternalMessagingService;
import me.lucko.luckperms.common.messaging.RedisMessaging;
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
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.storage.dao.file.FileWatcher;
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.treeview.PermissionVault;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.common.utils.LoggerImpl;
import me.lucko.luckperms.common.utils.LoginHelper;
import me.lucko.luckperms.common.utils.UuidCache;
import me.lucko.luckperms.common.verbose.VerboseHandler;
import org.bukkit.World;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
@@ -86,160 +96,140 @@ 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.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Stream;
/**
* LuckPerms implementation for the Bukkit API.
*/
@Getter
public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private Set<UUID> ignoringLogs;
private LPBukkitScheduler scheduler;
private BukkitCommand commandManager;
private VaultHook vaultHook = null;
private long startTime;
private BukkitSchedulerAdapter scheduler;
private BukkitCommandExecutor commandManager;
private VaultHookManager vaultHookManager = null;
private LuckPermsConfiguration configuration;
private UserManager userManager;
private GroupManager groupManager;
private TrackManager trackManager;
private Storage storage;
private InternalMessagingService messagingService = null;
private FileWatcher fileWatcher = null;
private ExtendedMessagingService messagingService = 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 CountDownLatch enableLatch = new CountDownLatch(1);
private VerboseHandler verboseHandler;
private BukkitSenderFactory senderFactory;
private PermissionVault permissionVault;
private LogDispatcher logDispatcher;
private Set<UUID> uniqueConnections = ConcurrentHashMap.newKeySet();
@Override
public void onLoad() {
if (checkInvalidVersion()) {
return;
}
// setup minimal functionality in order to load initial dependencies
scheduler = new BukkitSchedulerAdapter(this);
localeManager = new NoopLocaleManager();
senderFactory = new BukkitSenderFactory(this);
log = new SenderLogger(this, getConsoleSender());
DependencyManager.loadDependencies(this, Collections.singleton(Dependency.CAFFEINE));
}
@Override
public void onEnable() {
scheduler = new LPBukkitScheduler(this);
if (checkInvalidVersion()) {
getLogger().severe("----------------------------------------------------------------------");
getLogger().severe("Your server version is not compatible with this build of LuckPerms. :(");
getLogger().severe("");
getLogger().severe("If your server is running 1.8, please update to 1.8.8 or higher.");
getLogger().severe("If your server is running 1.7.10, please download the Bukkit-Legacy version of LuckPerms from here:");
getLogger().severe("==> https://ci.lucko.me/job/LuckPerms/");
getLogger().severe("----------------------------------------------------------------------");
getServer().getPluginManager().disablePlugin(this);
return;
}
localeManager = new NoopLocaleManager();
senderFactory = new BukkitSenderFactory(this);
log = new LoggerImpl(getConsoleSender());
try {
enable();
started = true;
} finally {
// count down the latch when onEnable has been called
// we don't care about the result here
enableLatch.countDown();
}
}
private void enable() {
startTime = System.currentTimeMillis();
LuckPermsPlugin.sendStartupBanner(getConsoleSender(), this);
ignoringLogs = ConcurrentHashMap.newKeySet();
debugHandler = new DebugHandler(scheduler.getAsyncBukkitExecutor(), getVersion());
permissionVault = new PermissionVault(scheduler.getAsyncBukkitExecutor());
verboseHandler = new VerboseHandler(scheduler.asyncBukkit(), getVersion());
permissionVault = new PermissionVault(scheduler.asyncBukkit());
logDispatcher = new LogDispatcher(this);
getLog().info("Loading configuration...");
configuration = new BukkitConfig(this);
configuration = new AbstractConfiguration(this, new BukkitConfigAdapter(this));
configuration.init();
configuration.loadAll();
Set<StorageType> storageTypes = StorageFactory.getRequiredTypes(this, StorageType.H2);
DependencyManager.loadDependencies(this, storageTypes);
DependencyManager.loadStorageDependencies(this, storageTypes);
// setup the Bukkit defaults hook
defaultsProvider = new DefaultsProvider();
childPermissionProvider = new ChildPermissionProvider();
// give all plugins a chance to load their permissions, then refresh.
scheduler.doSyncLater(() -> {
defaultsProvider.refresh();
childPermissionProvider.setup();
Set<String> perms = new HashSet<>();
getServer().getPluginManager().getPermissions().forEach(p -> {
perms.add(p.getName());
perms.addAll(p.getChildren().keySet());
});
perms.forEach(p -> permissionVault.offer(p));
}, 1L);
scheduler.syncLater(new BukkitProcessorsSetupTask(this), 1L);
// register events
PluginManager pm = getServer().getPluginManager();
listener = new BukkitListener(this);
pm.registerEvents(listener, this);
getServer().getPluginManager().registerEvents(new BukkitConnectionListener(this), this);
getServer().getPluginManager().registerEvents(new BukkitPlatformListener(this), this);
if (getConfiguration().get(ConfigKeys.WATCH_FILES)) {
fileWatcher = new FileWatcher(this);
getScheduler().asyncRepeating(fileWatcher, 30L);
}
// initialise datastore
storage = StorageFactory.getInstance(this, StorageType.H2);
// initialise messaging
String messagingType = getConfiguration().get(ConfigKeys.MESSAGING_SERVICE).toLowerCase();
if (messagingType.equals("none") && getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
messagingType = "redis";
}
if (messagingType.equals("redis")) {
getLog().info("Loading redis...");
if (getConfiguration().get(ConfigKeys.REDIS_ENABLED)) {
RedisMessaging redis = new RedisMessaging(this);
try {
redis.init(getConfiguration().get(ConfigKeys.REDIS_ADDRESS), getConfiguration().get(ConfigKeys.REDIS_PASSWORD));
getLog().info("Loaded redis successfully...");
messagingService = redis;
} catch (Exception e) {
getLog().warn("Couldn't load redis...");
e.printStackTrace();
}
} else {
getLog().warn("Messaging Service was set to redis, but redis is not enabled!");
}
} else if (messagingType.equals("bungee")) {
getLog().info("Loading bungee messaging service...");
BungeeMessagingService bungeeMessaging = new BungeeMessagingService(this);
bungeeMessaging.init();
messagingService = bungeeMessaging;
} else if (messagingType.equals("lilypad")) {
getLog().info("Loading LilyPad messaging service...");
if (getServer().getPluginManager().getPlugin("LilyPad-Connect") == null) {
getLog().warn("LilyPad-Connect plugin not present.");
} else {
LilyPadMessagingService lilyPadMessaging = new LilyPadMessagingService(this);
lilyPadMessaging.init();
messagingService = lilyPadMessaging;
}
} else if (!messagingType.equals("none")) {
getLog().warn("Messaging service '" + messagingType + "' not recognised.");
}
messagingService = new BukkitMessagingFactory(this).getInstance();
// setup the update task buffer
updateTaskBuffer = new BufferedRequest<Void>(1000L, this::doAsync) {
@Override
protected Void perform() {
new UpdateTask(LPBukkitPlugin.this).run();
return null;
}
};
updateTaskBuffer = new UpdateTaskBuffer(this);
// load locale
localeManager = new SimpleLocaleManager();
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();
}
}
localeManager.tryLoad(this, new File(getDataFolder(), "lang.yml"));
// register commands
getLog().info("Registering commands...");
commandManager = new BukkitCommand(this);
commandManager = new BukkitCommandExecutor(this);
PluginCommand main = getServer().getPluginCommand("luckperms");
main.setExecutor(commandManager);
main.setTabCompleter(commandManager);
main.setAliases(Arrays.asList("perms", "lp", "permissions", "perm"));
main.setDescription("Manage permissions");
main.setAliases(Arrays.asList("lp", "perm", "perms", "permission", "permissions"));
// load internal managers
getLog().info("Loading internal permission managers...");
@@ -247,22 +237,27 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
userManager = new GenericUserManager(this);
groupManager = new GenericGroupManager(this);
trackManager = new GenericTrackManager(this);
importer = new Importer(commandManager);
calculatorFactory = new BukkitCalculatorFactory(this);
cachedStateManager = new CachedStateManager(this);
cachedStateManager = new CachedStateManager();
contextManager = new ContextManager<>();
worldCalculator = new WorldCalculator(this);
contextManager.registerCalculator(worldCalculator);
contextManager.registerCalculator(new ServerCalculator<>(getConfiguration()));
// setup context manager
contextManager = new BukkitContextManager(this);
contextManager.registerCalculator(new WorldCalculator(this));
contextManager.registerStaticCalculator(new LuckPermsCalculator(getConfiguration()));
// inject our own subscription map
new SubscriptionMapInjector(this).run();
// schedule another injection after all plugins have loaded - the entire pluginmanager instance
// is replaced by some plugins :(
scheduler.asyncLater(new SubscriptionMapInjector(this), 2L);
// Provide vault support
tryVaultHook(false);
// register with the LP API
getLog().info("Registering API...");
apiProvider = new ApiProvider(this);
ApiHandler.registerProvider(apiProvider);
ApiSingletonUtils.registerProvider(apiProvider);
getServer().getServicesManager().register(LuckPermsApi.class, apiProvider, this, ServicePriority.Normal);
@@ -270,33 +265,42 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
int mins = getConfiguration().get(ConfigKeys.SYNC_TIME);
if (mins > 0) {
long ticks = mins * 60 * 20;
scheduler.doAsyncRepeating(() -> updateTaskBuffer.request(), ticks);
scheduler.asyncRepeating(() -> updateTaskBuffer.request(), ticks);
}
scheduler.doAsyncLater(() -> updateTaskBuffer.request(), 40L);
scheduler.asyncLater(() -> updateTaskBuffer.request(), 40L);
// run an update instantly.
updateTaskBuffer.requestDirectly();
getLog().info("Performing initial data load...");
new UpdateTask(this, true).run();
// register tasks
scheduler.doAsyncRepeating(new ExpireTemporaryTask(this), 60L);
scheduler.doAsyncRepeating(new CacheHousekeepingTask(this), 2400L);
scheduler.asyncRepeating(new ExpireTemporaryTask(this), 60L);
scheduler.asyncRepeating(new CacheHousekeepingTask(this), 2400L);
// register permissions
registerPermissions(getConfiguration().get(ConfigKeys.COMMANDS_ALLOW_OP) ? PermissionDefault.OP : PermissionDefault.FALSE);
try {
PluginManager pm = getServer().getPluginManager();
PermissionDefault permDefault = getConfiguration().get(ConfigKeys.COMMANDS_ALLOW_OP) ? PermissionDefault.OP : PermissionDefault.FALSE;
for (CommandPermission p : CommandPermission.values()) {
pm.addPermission(new org.bukkit.permissions.Permission(p.getPermission(), permDefault));
}
} catch (Exception e) {
// this throws an exception if the plugin is /reloaded, grr
}
if (!getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
scheduler.doSync(() -> getServer().getOperators().forEach(o -> o.setOp(false)));
}
// replace the temporary executor when the Bukkit one starts
getServer().getScheduler().runTaskAsynchronously(this, () -> {
scheduler.setUseBukkitAsync(true);
});
getServer().getScheduler().runTaskAsynchronously(this, () -> scheduler.setUseBukkitAsync(true));
// Load any online users (in the case of a reload)
for (Player player : getServer().getOnlinePlayers()) {
scheduler.doAsync(() -> {
listener.onAsyncLogin(player.getUniqueId(), player.getName());
User user = getUserManager().get(getUuidCache().getUUID(player.getUniqueId()));
LoginHelper.loadUser(this, player.getUniqueId(), player.getName(), false);
User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) {
scheduler.doSync(() -> {
try {
@@ -310,125 +314,111 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
});
}
started = true;
getLog().info("Successfully loaded.");
getLog().info("Successfully enabled. (took " + (System.currentTimeMillis() - startTime) + "ms)");
}
@Override
public void onDisable() {
if (checkInvalidVersion()) {
return;
}
// Switch back to the LP executor, the bukkit one won't allow new tasks
scheduler.setUseBukkitAsync(false);
started = false;
defaultsProvider.close();
permissionVault.setShutdown(true);
debugHandler.setShutdown(true);
permissionVault.shutdown();
verboseHandler.shutdown();
// uninject from players
for (Player player : getServer().getOnlinePlayers()) {
Injector.unInject(player, false, false);
try {
Injector.unInject(player, false);
} catch (Exception e) {
e.printStackTrace();
}
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
player.setOp(false);
}
final User user = getUserManager().get(getUuidCache().getUUID(player.getUniqueId()));
final User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
if (user != null) {
user.unregisterData();
user.getUserData().invalidateCaches();
getUserManager().unload(user);
}
}
getLog().info("Closing datastore...");
// uninject subscription map
SubscriptionMapInjector.uninject();
getLog().info("Closing storage...");
storage.shutdown();
if (fileWatcher != null) {
fileWatcher.close();
}
if (messagingService != null) {
getLog().info("Closing messaging service...");
messagingService.close();
}
getLog().info("Unregistering API...");
ApiHandler.unregisterProvider();
ApiSingletonUtils.unregisterProvider();
getServer().getServicesManager().unregisterAll(this);
if (vaultHook != null) {
vaultHook.unhook(this);
if (vaultHookManager != null) {
vaultHookManager.unhook(this);
}
getLog().info("Shutting down internal scheduler...");
scheduler.shutdown();
// 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;
vaultHook = null;
configuration = null;
userManager = null;
groupManager = null;
trackManager = null;
storage = null;
messagingService = 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;
permissionVault = null;
getLog().info("Goodbye!");
}
public void tryVaultHook(boolean force) {
if (vaultHook != null) {
return;
if (vaultHookManager != null) {
return; // already hooked
}
getLog().info("Attempting to hook with Vault...");
try {
if (force || getServer().getPluginManager().isPluginEnabled("Vault")) {
vaultHook = new VaultHook();
vaultHook.hook(this);
vaultHookManager = new VaultHookManager();
vaultHookManager.hook(this);
getLog().info("Registered Vault permission & chat hook.");
} else {
getLog().info("Vault not found.");
}
} catch (Exception e) {
vaultHook = null;
vaultHookManager = 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();
public void refreshAutoOp(User user, Player player) {
if (user == null) {
return;
}
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
Map<String, Boolean> backing = user.getUserData().getPermissionData(contextManager.getApplicableContexts(player)).getImmutableBacking();
boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false);
player.setOp(op);
}
}
public void refreshAutoOp(Player player) {
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
try {
LPPermissible permissible = Injector.getPermissible(player.getUniqueId());
if (permissible == null) {
return;
}
@Override
public Optional<ExtendedMessagingService> getMessagingService() {
return Optional.ofNullable(messagingService);
}
Map<String, Boolean> backing = permissible.getUser().getUserData().getPermissionData(permissible.calculateContexts()).getImmutableBacking();
boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false);
player.setOp(op);
} catch (Exception ignored) {}
}
public Optional<FileWatcher> getFileWatcher() {
return Optional.ofNullable(fileWatcher);
}
@Override
@@ -448,7 +438,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
@Override
public String getServerVersion() {
return getServer().getVersion();
return getServer().getVersion() + " - " + getServer().getBukkitVersion();
}
@Override
@@ -466,21 +456,24 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
return getServer().getPlayer(uuidCache.getExternalUUID(user.getUuid()));
}
@Override
public Optional<UUID> lookupUuid(String username) {
try {
//noinspection deprecation
return Optional.ofNullable(getServer().getOfflinePlayer(username)).flatMap(p -> Optional.ofNullable(p.getUniqueId()));
} catch (Exception e) {
e.printStackTrace();
return Optional.empty();
}
}
@Override
public Contexts getContextForUser(User user) {
Player player = getPlayer(user);
if (player == null) {
return null;
}
return new Contexts(
getContextManager().getApplicableContext(player),
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
true,
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
player.isOp()
);
return contextManager.getApplicableContexts(player);
}
@Override
@@ -489,25 +482,27 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public List<String> getPlayerList() {
return getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
public Stream<String> getPlayerList() {
return getServer().getOnlinePlayers().stream().map(Player::getName);
}
@Override
public Set<UUID> getOnlinePlayers() {
return getServer().getOnlinePlayers().stream().map(Player::getUniqueId).collect(Collectors.toSet());
public Stream<UUID> getOnlinePlayers() {
return getServer().getOnlinePlayers().stream().map(Player::getUniqueId);
}
@Override
public boolean isPlayerOnline(UUID external) {
return getServer().getPlayer(external) != null;
Player player = getServer().getPlayer(external);
return player != null && player.isOnline();
}
@Override
public List<Sender> getOnlineSenders() {
return getServer().getOnlinePlayers().stream()
.map(p -> getSenderFactory().wrap(p))
.collect(Collectors.toList());
public Stream<Sender> getOnlineSenders() {
return Stream.concat(
Stream.of(getConsoleSender()),
getServer().getOnlinePlayers().stream().map(p -> getSenderFactory().wrap(p))
);
}
@Override
@@ -516,104 +511,20 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public Set<Contexts> getPreProcessContexts(boolean op) {
Set<ContextSet> c = new HashSet<>();
c.add(ContextSet.empty());
c.add(ContextSet.singleton("server", getConfiguration().get(ConfigKeys.SERVER)));
// Pre process all worlds
c.addAll(getServer().getWorlds().stream()
.map(World::getName)
.map(s -> {
MutableContextSet set = MutableContextSet.create();
set.add("server", getConfiguration().get(ConfigKeys.SERVER));
set.add("world", s);
return set.makeImmutable();
})
.collect(Collectors.toList())
);
// Pre process the separate Vault server, if any
if (!getConfiguration().get(ConfigKeys.SERVER).equals(getConfiguration().get(ConfigKeys.VAULT_SERVER))) {
c.add(ContextSet.singleton("server", getConfiguration().get(ConfigKeys.VAULT_SERVER)));
c.addAll(getServer().getWorlds().stream()
.map(World::getName)
.map(s -> {
MutableContextSet set = MutableContextSet.create();
set.add("server", getConfiguration().get(ConfigKeys.VAULT_SERVER));
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().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
true,
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
op
))
.collect(Collectors.toSet())
);
// Check for and include varying Vault config options
boolean vaultDiff = getConfiguration().get(ConfigKeys.VAULT_INCLUDING_GLOBAL) != getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS) ||
!getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS) ||
!getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS) ||
!getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS);
if (vaultDiff) {
contexts.addAll(c.stream()
.map(map -> new Contexts(map, getConfiguration().get(ConfigKeys.VAULT_INCLUDING_GLOBAL), 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.get(ConfigKeys.VAULT_SERVER));
public Map<String, Object> getExtraInfo() {
Map<String, Object> map = new LinkedHashMap<>();
map.put("Vault Enabled", vaultHookManager != null);
map.put("Bukkit Defaults count", defaultsProvider.size());
map.put("Bukkit Child Permissions count", childPermissionProvider.getPermissions().size());
map.put("Vault Including Global", configuration.get(ConfigKeys.VAULT_INCLUDING_GLOBAL));
map.put("Vault Ignoring World", configuration.get(ConfigKeys.VAULT_IGNORE_WORLD));
map.put("Vault Primary Group Overrides", configuration.get(ConfigKeys.VAULT_PRIMARY_GROUP_OVERRIDES));
map.put("Vault Debug", configuration.get(ConfigKeys.VAULT_DEBUG));
map.put("OPs Enabled", configuration.get(ConfigKeys.OPS_ENABLED));
map.put("Auto OP", configuration.get(ConfigKeys.AUTO_OP));
map.put("Commands Allow OPs", configuration.get(ConfigKeys.COMMANDS_ALLOW_OP));
return map;
}
@SuppressWarnings("deprecation")
@Override
public UUID getUuidFromUsername(String playerName) {
private static boolean checkInvalidVersion() {
try {
return getServer().getOfflinePlayer(playerName).getUniqueId();
} catch (Exception e) {
return null;
}
}
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));
}
Class.forName("com.google.gson.JsonElement");
return false;
} catch (ClassNotFoundException e) {
return true;
}
}
}
Loaded 100 of 652 files, more files were not shown because too many files have changed in this diff. Show more