Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
168e712324 | ||
|
|
2889c28815 | ||
|
|
966bf8bf51 | ||
|
|
63ff3c34b3 | ||
|
|
736105cc12 | ||
|
|
b1fa4263ed | ||
|
|
31d435dc2b | ||
|
|
da9977a30f | ||
|
|
9c92522564 | ||
|
|
31e436868d | ||
|
|
794455d728 | ||
|
|
0adf85746d | ||
|
|
f1047df98e | ||
|
|
765f9d2545 | ||
|
|
0b5588a7dd | ||
|
|
4fda89d5c8 | ||
|
|
b8c5c60ece | ||
|
|
a3730aceeb | ||
|
|
df5240a67c | ||
|
|
620b0c898c | ||
|
|
02ac4bc48b | ||
|
|
d14310768c | ||
|
|
63f8e8849f | ||
|
|
7e7268bb5a | ||
|
|
173286d404 | ||
|
|
fd937e3209 | ||
|
|
31df29194b | ||
|
|
a1a2e189b6 | ||
|
|
5799a26cb7 | ||
|
|
fe200ca1ec | ||
|
|
7f7116e1c7 | ||
|
|
0cc1c4e5a7 | ||
|
|
2bc56de1c3 | ||
|
|
e28d15456c | ||
|
|
54dbede130 | ||
|
|
982254ff5e | ||
|
|
e1c60fa70f | ||
|
|
6923c4e247 | ||
|
|
0eba5f1cbc | ||
|
|
4858e59b70 | ||
|
|
1f70ad978f | ||
|
|
40294b10f5 | ||
|
|
8d045be0b0 | ||
|
|
e24a482deb | ||
|
|
f18d230ff6 | ||
|
|
72d4e5cf7a | ||
|
|
4c3e28ba85 | ||
|
|
821dc4ef56 | ||
|
|
612712f015 | ||
|
|
03a93b1186 | ||
|
|
04ffd00c0e | ||
|
|
a74475d4cd | ||
|
|
05ac7e6041 | ||
|
|
5ae90f2a4b | ||
|
|
b800db03fc | ||
|
|
662b958a7f | ||
|
|
7cd5eea6d3 | ||
|
|
ed223f0e4e | ||
|
|
8a374aed04 | ||
|
|
9ceacdb739 | ||
|
|
39daed23c4 | ||
|
|
19e6d4e4c8 | ||
|
|
22006617d0 | ||
|
|
3201d10bdd | ||
|
|
ea94bd8696 | ||
|
|
f646c04d09 | ||
|
|
17ff9ac328 | ||
|
|
283516614b | ||
|
|
6c8f04f829 | ||
|
|
505a10a8b1 | ||
|
|
4fd89e6a34 | ||
|
|
7bf666b062 | ||
|
|
764665cf15 | ||
|
|
b9dfd7db54 | ||
|
|
e09c5622f7 | ||
|
|
717347273d | ||
|
|
9bb1ba12b2 | ||
|
|
bff9715e7f | ||
|
|
cfcd896c59 | ||
|
|
aaa50cacda | ||
|
|
3413adf16f | ||
|
|
3d6aa69ca1 | ||
|
|
ab115c4a6b | ||
|
|
e1e7bc8e0d | ||
|
|
6602b23f09 | ||
|
|
862fe6e2e3 | ||
|
|
5a34fa182a | ||
|
|
0b72507e2d | ||
|
|
cc3ddd51fd | ||
|
|
f793fd5010 | ||
|
|
fb92fd9357 | ||
|
|
a415a2705f | ||
|
|
765f140121 | ||
|
|
cb47f9f94b | ||
|
|
d0fb9f6aee | ||
|
|
9d424f5122 | ||
|
|
f71237ea95 | ||
|
|
fb5925e1e8 | ||
|
|
f86bdb7619 | ||
|
|
ad5299d0cd | ||
|
|
904bb90385 | ||
|
|
2e7a08c006 | ||
|
|
38e75da92f | ||
|
|
97708c2c80 | ||
|
|
e5ce64266f | ||
|
|
a765be0a6f | ||
|
|
0bec93ab1c | ||
|
|
dfb1b8f841 | ||
|
|
322b522a52 | ||
|
|
9dd4f71526 | ||
|
|
94b4e3d366 | ||
|
|
bbb2897d20 | ||
|
|
a5e41e479a | ||
|
|
2baea0ce13 | ||
|
|
90cbff885f | ||
|
|
0d9034504e | ||
|
|
800749c5ed | ||
|
|
30f27f29b4 | ||
|
|
2985cf7237 | ||
|
|
003ddc51de | ||
|
|
c369ac6b90 | ||
|
|
e2b9e37867 | ||
|
|
1e105b4135 | ||
|
|
c5c253af4e | ||
|
|
717835e5c1 | ||
|
|
e9ba62dbd9 | ||
|
|
ad5c22ecdb | ||
|
|
2e92b97f01 | ||
|
|
ae8fe0fe1e | ||
|
|
d992d7f77c | ||
|
|
a7cb7ec1af | ||
|
|
3afeb30795 | ||
|
|
26feeb125e | ||
|
|
211fb219a7 | ||
|
|
e71ef834c0 | ||
|
|
29eba93ee3 | ||
|
|
52ebfe9309 | ||
|
|
b3e78c6dc1 | ||
|
|
cd0184970a | ||
|
|
6401480874 | ||
|
|
6ca9eb13dd | ||
|
|
987ea51264 | ||
|
|
df37491199 | ||
|
|
9a420403de | ||
|
|
17da2807cf | ||
|
|
22fba0c172 | ||
|
|
14005563a3 | ||
|
|
a115ff8ce2 | ||
|
|
d75b29f51d | ||
|
|
3e8f7911af |
No files matched your search
@@ -1,728 +0,0 @@
|
||||
# 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"
|
||||
@@ -1,338 +0,0 @@
|
||||
# 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<------------------------------------------>"
|
||||
@@ -1,728 +0,0 @@
|
||||
# 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"
|
||||
@@ -1,290 +0,0 @@
|
||||
# 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<------------------------------------------>"
|
||||
@@ -1,303 +0,0 @@
|
||||
# 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<------------------------------------------>"
|
||||
@@ -1,728 +0,0 @@
|
||||
# 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表的名称"
|
||||
@@ -0,0 +1,26 @@
|
||||
### :speech_balloon: Looking for support?
|
||||
|
||||
Details about support for the project can be found [here, on the Wiki Homepage](https://github.com/lucko/LuckPerms/wiki#support).
|
||||
|
||||
### :bug: Reporting bugs?
|
||||
|
||||
Before reporting a bug or issue, please make sure that the issue is actually being caused by or related to LuckPerms. We get a lot of reports which are caused by other software - please double check!
|
||||
|
||||
If you're unsure, feel free to ask using the above resources BEFORE making a report.
|
||||
|
||||
Bugs or issues should be reported using the [GitHub Issues tab](https://github.com/lucko/LuckPerms/issues).
|
||||
|
||||
### :pencil: Want to contribute code?
|
||||
#### Pull Requests
|
||||
If you make any changes or improvements to the plugin which you think would be beneficial to others, 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). Generally, try to copy the style of code found in the class you're editing.
|
||||
|
||||
If you're considering submitting a substantial pull request, please open an issue so we can discuss the change before starting work on the contribution. Most pull requests are happily accepted, but larger changes may have an inpact on the maintainability of the project, and require more consideration.
|
||||
|
||||
#### Project Layout
|
||||
The project is split up into 5 seperate modules.
|
||||
|
||||
* **API** - The public, semantically versioned API used by other plugins wishing to integrate with and retrieve data from LuckPerms. This module (for the most part) does not contain any implementation itself, and is provided by the plugin.
|
||||
* **Common** - The common module contains most of the code which implements the respective LuckPerms plugins. This abstract module reduces duplicated code throughout the project.
|
||||
* **Bukkit, BungeeCord & Sponge** - Each use the common module to implement plugins on the respective server platforms.
|
||||
@@ -1,17 +1,17 @@
|
||||

|
||||
# LuckPerms [](https://ci.lucko.me/job/LuckPerms/)
|
||||
# LuckPerms [](https://ci.lucko.me/job/LuckPerms/) [](https://javadoc.io/doc/me.lucko.luckperms/luckperms-api) [](https://maven-badges.herokuapp.com/maven-central/me.lucko.luckperms/luckperms-api)
|
||||
|
||||
LuckPerms is an advanced permissions implementation aiming to be a fast, reliable and flexible alternative to existing permission plugins. The project's main goals are centered around high performance and a wide feature set, filling the gaps of functionality and building upon existing features found in other plugins.
|
||||
|
||||
LuckPerms also includes an extensive API for developers, and support for a variety of Minecraft server software & data storage options.
|
||||
|
||||
[See the charts below](https://github.com/lucko/LuckPerms#luckperms-vs-other-plugins) for a partial list of features & comparison with other providers.
|
||||
The main features of the project are detailed on this wiki page: [Why LuckPerms?](https://github.com/lucko/LuckPerms/wiki/Why-LuckPerms)
|
||||
|
||||
* [Spigot Plugin Release Page](https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-system.28140/ "Spigot Plugin Page")
|
||||
* [Sponge Plugin Release Page](https://forums.spongepowered.org/t/luckperms-an-advanced-permissions-system/14274 "Sponge Plugin Page")
|
||||
|
||||
## Useful Links
|
||||
* **Development Builds** - <https://ci.lucko.me/job/LuckPerms>
|
||||
* **Javadocs** - <https://luckperms.lucko.me/javadocs/>
|
||||
* **Downloads** - <https://ci.lucko.me/job/LuckPerms>
|
||||
* **Wiki** - <https://github.com/lucko/LuckPerms/wiki>
|
||||
|
||||
## Building
|
||||
@@ -32,32 +32,20 @@ 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!)
|
||||
If you make any changes or improvements to the plugin which you think would be beneficial to others, 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.
|
||||
LuckPerms loosely follows the [Google Java Style Guide](https://google.github.io/styleguide/javaguide.html). 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.
|
||||
* **API** - The public, semantically versioned API used by other plugins wishing to integrate with and retrieve data from LuckPerms. This module (for the most part) does not contain any implementation itself, and is provided by the plugin.
|
||||
* **Common** - The common module contains most of the code which implements the respective LuckPerms plugins. This abstract module reduces duplicated code throughout the project.
|
||||
* **Bukkit, BungeeCord & Sponge** - Each use the common module to implement plugins on the respective server 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.
|
||||
|
||||
## 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
|
||||
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
|
||||
LuckPerms is currently the only maintained and 'working' permissions plugin for Sponge servers.
|
||||
|
||||
|
||||
## Thanks
|
||||
Thanks to ej-technologies for granting LuckPerms an open source licence to their [Java Profiling Software](http://www.ej-technologies.com/products/jprofiler/overview.html "Java Profiler").
|
||||
+3
-23
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>luckperms</artifactId>
|
||||
<groupId>me.lucko.luckperms</groupId>
|
||||
<version>4.0-SNAPSHOT</version>
|
||||
<version>4.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
@@ -74,27 +74,6 @@
|
||||
</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>
|
||||
@@ -105,6 +84,7 @@
|
||||
<version>19.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- jsr305 -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents the object responsible for handling action logging.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface ActionLogger {
|
||||
|
||||
/**
|
||||
* Returns a new {@link LogEntry.Builder} instance
|
||||
*
|
||||
* @return a new builder
|
||||
*/
|
||||
@Nonnull
|
||||
LogEntry.Builder newEntryBuilder();
|
||||
|
||||
/**
|
||||
* Gets a {@link Log} instance from the plugin storage.
|
||||
*
|
||||
* <p>Returns the same result as {@link Storage#getLog()}.</p>
|
||||
*
|
||||
* @return a log instance
|
||||
* @see Storage#getLog()
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Log> getLog();
|
||||
|
||||
/**
|
||||
* Submits a log entry to the plugin to be handled.
|
||||
*
|
||||
* <p>This method submits the log to the storage provider and broadcasts
|
||||
* it.</p>
|
||||
*
|
||||
* <p>It is therefore roughly equivalent to calling
|
||||
* {@link #submitToStorage(LogEntry)} and {@link #broadcastAction(LogEntry)},
|
||||
* however, using this method is preferred to making the calls individually.</p>
|
||||
*
|
||||
* <p>If you want to submit a log entry but don't know which method to pick,
|
||||
* use this one.</p>
|
||||
*
|
||||
* @param entry the entry to submit
|
||||
* @return a future which will complete when the action is done
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> submit(@Nonnull LogEntry entry);
|
||||
|
||||
/**
|
||||
* Submits a log entry to the plugins storage handler.
|
||||
*
|
||||
* <p>Performs the same action as {@link Storage#logAction(LogEntry)}.</p>
|
||||
*
|
||||
* @param entry the entry to submit
|
||||
* @return a future which will complete when the action is done
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> submitToStorage(@Nonnull LogEntry entry);
|
||||
|
||||
/**
|
||||
* Submits a log entry to the plugins log broadcasting handler.
|
||||
*
|
||||
* <p>If enabled, this method will also dispatch the log entry via the
|
||||
* plugins {@link MessagingService}.</p>
|
||||
*
|
||||
* @param entry the entry to submit
|
||||
* @return a future which will complete when the action is done
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> broadcastAction(@Nonnull LogEntry entry);
|
||||
|
||||
}
|
||||
@@ -25,9 +25,8 @@
|
||||
|
||||
package me.lucko.luckperms.api;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -42,43 +41,41 @@ public enum ChatMetaType {
|
||||
/**
|
||||
* Represents a prefix
|
||||
*/
|
||||
PREFIX {
|
||||
PREFIX("prefix") {
|
||||
@Override
|
||||
public boolean matches(Node node) {
|
||||
return Preconditions.checkNotNull(node, "node").isPrefix();
|
||||
public boolean matches(@Nonnull Node node) {
|
||||
return Objects.requireNonNull(node, "node").isPrefix();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@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();
|
||||
public Map.Entry<Integer, String> getEntry(@Nonnull Node node) {
|
||||
return Objects.requireNonNull(node, "node").getPrefix();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Represents a suffix
|
||||
*/
|
||||
SUFFIX {
|
||||
SUFFIX("suffix") {
|
||||
@Override
|
||||
public boolean matches(Node node) {
|
||||
return Preconditions.checkNotNull(node, "node").isSuffix();
|
||||
public boolean matches(@Nonnull Node node) {
|
||||
return Objects.requireNonNull(node, "node").isSuffix();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@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();
|
||||
public Map.Entry<Integer, String> getEntry(@Nonnull Node node) {
|
||||
return Objects.requireNonNull(node, "node").getSuffix();
|
||||
}
|
||||
};
|
||||
|
||||
private final String str;
|
||||
|
||||
ChatMetaType(String str) {
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the passed node matches the type
|
||||
*
|
||||
@@ -93,7 +90,9 @@ public enum ChatMetaType {
|
||||
* @param node the node to test
|
||||
* @return true if the node does not share the same type
|
||||
*/
|
||||
public abstract boolean shouldIgnore(@Nonnull Node node);
|
||||
public boolean shouldIgnore(@Nonnull Node node) {
|
||||
return !matches(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the corresponding entry from the given node
|
||||
@@ -105,6 +104,11 @@ public enum ChatMetaType {
|
||||
@Nonnull
|
||||
public abstract Map.Entry<Integer, String> getEntry(@Nonnull Node node);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a ChatMetaType from the given node.
|
||||
*
|
||||
|
||||
@@ -25,20 +25,22 @@
|
||||
|
||||
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 java.util.Objects;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Encapsulates the options and settings for a permission lookup.
|
||||
* Encapsulates the options and settings for a permission or meta lookup.
|
||||
*
|
||||
* <p>This class is immutable.</p>
|
||||
*
|
||||
* @since 2.11
|
||||
*/
|
||||
@Immutable
|
||||
public class Contexts {
|
||||
|
||||
/**
|
||||
@@ -56,15 +58,7 @@ public class Contexts {
|
||||
*
|
||||
* 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
|
||||
);
|
||||
private static final Contexts GLOBAL = new Contexts(ContextSet.empty(), true, true, true, true, true, false);
|
||||
|
||||
/**
|
||||
* Gets the {@link FullySatisfiedContexts} instance.
|
||||
@@ -87,6 +81,7 @@ public class Contexts {
|
||||
return GLOBAL;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
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);
|
||||
}
|
||||
@@ -133,7 +128,7 @@ public class Contexts {
|
||||
private final int hashCode;
|
||||
|
||||
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.context = Objects.requireNonNull(context, "context").makeImmutable();
|
||||
this.includeGlobal = includeGlobal;
|
||||
this.includeGlobalWorld = includeGlobalWorld;
|
||||
this.applyGroups = applyGroups;
|
||||
@@ -209,18 +204,17 @@ public class Contexts {
|
||||
return this.applyGlobalWorldGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Contexts(" +
|
||||
"context=" + this.getContexts() + ", " +
|
||||
"op=" + this.isOp() + ", " +
|
||||
"includeGlobal=" + this.isIncludeGlobal() + ", " +
|
||||
"includeGlobalWorld=" + this.isIncludeGlobalWorld() + ", " +
|
||||
"applyGroups=" + this.isApplyGroups() + ", " +
|
||||
"applyGlobalGroups=" + this.isApplyGlobalGroups() + ", " +
|
||||
"applyGlobalWorldGroups=" + this.isApplyGlobalWorldGroups() +
|
||||
")";
|
||||
"context=" + this.context + ", " +
|
||||
"op=" + this.op + ", " +
|
||||
"includeGlobal=" + this.includeGlobal + ", " +
|
||||
"includeGlobalWorld=" + this.includeGlobalWorld + ", " +
|
||||
"applyGroups=" + this.applyGroups + ", " +
|
||||
"applyGlobalGroups=" + this.applyGlobalGroups + ", " +
|
||||
"applyGlobalWorldGroups=" + this.applyGlobalWorldGroups + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -228,33 +222,32 @@ public class Contexts {
|
||||
if (o == this) return true;
|
||||
if (o == allowAll()) return false;
|
||||
if (!(o instanceof Contexts)) return false;
|
||||
final Contexts that = (Contexts) o;
|
||||
|
||||
final Contexts other = (Contexts) o;
|
||||
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();
|
||||
return this.context.equals(that.context) &&
|
||||
this.op == that.op &&
|
||||
this.includeGlobal == that.includeGlobal &&
|
||||
this.includeGlobalWorld == that.includeGlobalWorld &&
|
||||
this.applyGroups == that.applyGroups &&
|
||||
this.applyGlobalGroups == that.applyGlobalGroups &&
|
||||
this.applyGlobalWorldGroups == that.applyGlobalWorldGroups;
|
||||
}
|
||||
|
||||
private int calculateHashCode() {
|
||||
final int PRIME = 59;
|
||||
int result = 1;
|
||||
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);
|
||||
result = result * PRIME + (this.isApplyGroups() ? 79 : 97);
|
||||
result = result * PRIME + (this.isApplyGlobalGroups() ? 79 : 97);
|
||||
result = result * PRIME + (this.isApplyGlobalWorldGroups() ? 79 : 97);
|
||||
result = result * PRIME + this.context.hashCode();
|
||||
result = result * PRIME + (this.op ? 79 : 97);
|
||||
result = result * PRIME + (this.includeGlobal ? 79 : 97);
|
||||
result = result * PRIME + (this.includeGlobalWorld ? 79 : 97);
|
||||
result = result * PRIME + (this.applyGroups ? 79 : 97);
|
||||
result = result * PRIME + (this.applyGlobalGroups ? 79 : 97);
|
||||
result = result * PRIME + (this.applyGlobalWorldGroups ? 79 : 97);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
return this.hashCode;
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,12 @@ public enum DataMutateResult {
|
||||
SUCCESS(true),
|
||||
|
||||
/**
|
||||
* Indicates the mutation failed because the subject already has something
|
||||
* Indicates the mutation failed because the subject of the action already has something
|
||||
*/
|
||||
ALREADY_HAS(false),
|
||||
|
||||
/**
|
||||
* Indicates the mutation failed because the subject lacks something
|
||||
* Indicates the mutation failed because the subject of the action lacks something
|
||||
*/
|
||||
LACKS(false),
|
||||
|
||||
@@ -62,7 +62,7 @@ public enum DataMutateResult {
|
||||
* @return a boolean representation
|
||||
*/
|
||||
public boolean asBoolean() {
|
||||
return value;
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +72,7 @@ public enum DataMutateResult {
|
||||
* @since 3.4
|
||||
*/
|
||||
public boolean wasSuccess() {
|
||||
return value;
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ public enum DataMutateResult {
|
||||
* @since 3.4
|
||||
*/
|
||||
public boolean wasFailure() {
|
||||
return !value;
|
||||
return !this.value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 java.util.UUID;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an entity on the server.
|
||||
*
|
||||
* <p>This does not relate directly to a "Minecraft Entity". The closest
|
||||
* comparison is to a "CommandSender" or "CommandSource" in Bukkit/Sponge.</p>
|
||||
*
|
||||
* <p>The various types of {@link Entity} are detailed in {@link Type}.</p>
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface Entity {
|
||||
|
||||
/**
|
||||
* Gets the unique id of the entity, if it has one.
|
||||
*
|
||||
* <p>For players, this returns their uuid assigned by the server.</p>
|
||||
*
|
||||
* @return the uuid of the object, if available
|
||||
*/
|
||||
@Nullable
|
||||
UUID getUniqueId();
|
||||
|
||||
/**
|
||||
* Gets the name of the object
|
||||
*
|
||||
* @return the object name
|
||||
*/
|
||||
@Nonnull
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Gets the entities type.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
@Nonnull
|
||||
Type getType();
|
||||
|
||||
/**
|
||||
* The different types of {@link Entity}
|
||||
*/
|
||||
enum Type {
|
||||
|
||||
/**
|
||||
* Represents a player connected to the server
|
||||
*/
|
||||
PLAYER,
|
||||
|
||||
/**
|
||||
* Represents the server console
|
||||
*/
|
||||
CONSOLE
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
package me.lucko.luckperms.api;
|
||||
|
||||
import me.lucko.luckperms.api.caching.CachedData;
|
||||
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;
|
||||
@@ -36,9 +36,9 @@ import javax.annotation.Nonnull;
|
||||
*
|
||||
* <p></p>
|
||||
* <ul>
|
||||
* <li>{@link UserData#getPermissionData(Contexts)}</li>
|
||||
* <li>{@link UserData#getMetaData(Contexts)}</li>
|
||||
* <li>{@link UserData#getMetaData(MetaContexts)}</li>
|
||||
* <li>{@link CachedData#getPermissionData(Contexts)}</li>
|
||||
* <li>{@link CachedData#getMetaData(Contexts)}</li>
|
||||
* <li>{@link CachedData#getMetaData(MetaContexts)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>... will always satisfy all contextual requirements.</p>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
package me.lucko.luckperms.api;
|
||||
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.caching.GroupData;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
|
||||
@@ -44,28 +44,6 @@ public interface Group extends PermissionHolder {
|
||||
@Nonnull
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* 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(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Check to see if a group inherits another group directly
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
boolean inheritsGroup(@Nonnull Group group, @Nonnull ContextSet contextSet);
|
||||
|
||||
/**
|
||||
* Gets the weight of this group, if present.
|
||||
*
|
||||
@@ -75,4 +53,14 @@ public interface Group extends PermissionHolder {
|
||||
@Nonnull
|
||||
OptionalInt getWeight();
|
||||
|
||||
/**
|
||||
* Gets the groups's {@link GroupData} cache.
|
||||
*
|
||||
* @return the groups cached data.
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
@Override
|
||||
GroupData getCachedData();
|
||||
|
||||
}
|
||||
@@ -31,13 +31,15 @@ import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* A relationship between a PermissionHolder and a permission
|
||||
* A relationship between a {@link PermissionHolder} and a permission.
|
||||
*
|
||||
* @param <T> the identifier type of the holder
|
||||
* @since 2.17
|
||||
*/
|
||||
@Immutable
|
||||
public interface HeldPermission<T> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,12 +26,14 @@
|
||||
package me.lucko.luckperms.api;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* A node with a traceable origin
|
||||
*
|
||||
* @since 2.11
|
||||
*/
|
||||
@Immutable
|
||||
public interface LocalizedNode extends Node {
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.SortedSet;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Represents the internal LuckPerms log.
|
||||
@@ -41,6 +42,7 @@ import javax.annotation.Nonnull;
|
||||
*
|
||||
* <p>All methods are thread safe, and return immutable and thread safe collections.</p>
|
||||
*/
|
||||
@Immutable
|
||||
public interface Log {
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,10 +30,14 @@ import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Represents a logged action.
|
||||
*
|
||||
* @see ActionLogger#newEntryBuilder() for creating an instance
|
||||
*/
|
||||
@Immutable
|
||||
public interface LogEntry extends Comparable<LogEntry> {
|
||||
|
||||
/**
|
||||
@@ -50,6 +54,7 @@ public interface LogEntry extends Comparable<LogEntry> {
|
||||
*
|
||||
* @return the actor id
|
||||
*/
|
||||
@Nonnull
|
||||
UUID getActor();
|
||||
|
||||
/**
|
||||
@@ -57,6 +62,7 @@ public interface LogEntry extends Comparable<LogEntry> {
|
||||
*
|
||||
* @return the name of the actor
|
||||
*/
|
||||
@Nonnull
|
||||
String getActorName();
|
||||
|
||||
/**
|
||||
@@ -64,6 +70,7 @@ public interface LogEntry extends Comparable<LogEntry> {
|
||||
*
|
||||
* @return the action type
|
||||
*/
|
||||
@Nonnull
|
||||
Type getType();
|
||||
|
||||
/**
|
||||
@@ -73,6 +80,7 @@ public interface LogEntry extends Comparable<LogEntry> {
|
||||
*
|
||||
* @return the uuid of acted object
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<UUID> getActed();
|
||||
|
||||
/**
|
||||
@@ -80,6 +88,7 @@ public interface LogEntry extends Comparable<LogEntry> {
|
||||
*
|
||||
* @return the name of the acted object
|
||||
*/
|
||||
@Nonnull
|
||||
String getActedName();
|
||||
|
||||
/**
|
||||
@@ -90,6 +99,7 @@ public interface LogEntry extends Comparable<LogEntry> {
|
||||
*
|
||||
* @return the action
|
||||
*/
|
||||
@Nonnull
|
||||
String getAction();
|
||||
|
||||
/**
|
||||
@@ -112,6 +122,7 @@ public interface LogEntry extends Comparable<LogEntry> {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Type valueOf(char code) {
|
||||
switch (code) {
|
||||
case 'U':
|
||||
@@ -134,27 +145,81 @@ public interface LogEntry extends Comparable<LogEntry> {
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Sets the timestamp of the entry.
|
||||
*
|
||||
* @param timestamp the timestamp
|
||||
* @return the builder
|
||||
* @see LogEntry#getTimestamp()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setTimestamp(long timestamp);
|
||||
|
||||
/**
|
||||
* Sets the actor of the entry.
|
||||
*
|
||||
* @param actor the actor
|
||||
* @return the builder
|
||||
* @see LogEntry#getActor()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setActor(@Nonnull UUID actor);
|
||||
|
||||
/**
|
||||
* Sets the actor name of the entry.
|
||||
*
|
||||
* @param actorName the actor name
|
||||
* @return the builder
|
||||
* @see LogEntry#getActorName()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setActorName(@Nonnull String actorName);
|
||||
|
||||
/**
|
||||
* Sets the type of the entry.
|
||||
*
|
||||
* @param type the type
|
||||
* @return the builder
|
||||
* @see LogEntry#getType()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setType(@Nonnull Type type);
|
||||
|
||||
/**
|
||||
* Sets the acted object for the entry.
|
||||
*
|
||||
* @param acted the acted object
|
||||
* @return the builder
|
||||
* @see LogEntry#getActed()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setActed(@Nullable UUID acted);
|
||||
|
||||
/**
|
||||
* Sets the acted name for the entry.
|
||||
*
|
||||
* @param actedName the acted name
|
||||
* @return the builder
|
||||
* @see LogEntry#getActedName()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setActedName(@Nonnull String actedName);
|
||||
|
||||
/**
|
||||
* Sets the action of the entry.
|
||||
*
|
||||
* @param action the action
|
||||
* @return the builder
|
||||
* @see LogEntry#getAction()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setAction(@Nonnull String action);
|
||||
|
||||
/**
|
||||
* Creates a {@link LogEntry} instance from the builder.
|
||||
*
|
||||
* @return a new log entry instance
|
||||
*/
|
||||
@Nonnull
|
||||
LogEntry build();
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ 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.messenger.MessengerProvider;
|
||||
import me.lucko.luckperms.api.metastacking.MetaStackFactory;
|
||||
import me.lucko.luckperms.api.platform.PlatformInfo;
|
||||
|
||||
@@ -65,7 +66,14 @@ public interface LuckPermsApi {
|
||||
PlatformInfo getPlatformInfo();
|
||||
|
||||
/**
|
||||
* Gets the user manager
|
||||
* Gets the {@link UserManager}, responsible for managing
|
||||
* {@link User} instances.
|
||||
*
|
||||
* <p>This manager can be used to retrieve instances of {@link User} by uuid
|
||||
* or name, or query all loaded users.</p>
|
||||
*
|
||||
* <p>The {@link #getStorage() storage} instance should be used to
|
||||
* load/create/save users.</p>
|
||||
*
|
||||
* @return the user manager
|
||||
* @since 4.0
|
||||
@@ -74,7 +82,14 @@ public interface LuckPermsApi {
|
||||
UserManager getUserManager();
|
||||
|
||||
/**
|
||||
* Gets the group manager
|
||||
* Gets the {@link GroupManager}, responsible for managing
|
||||
* {@link Group} instances.
|
||||
*
|
||||
* <p>This manager can be used to retrieve instances of {@link Group} by
|
||||
* name, or query all loaded groups.</p>
|
||||
*
|
||||
* <p>The {@link #getStorage() storage} instance should be used to
|
||||
* load/create/save/delete groups.</p>
|
||||
*
|
||||
* @return the group manager
|
||||
* @since 4.0
|
||||
@@ -83,7 +98,14 @@ public interface LuckPermsApi {
|
||||
GroupManager getGroupManager();
|
||||
|
||||
/**
|
||||
* Gets the track manager
|
||||
* Gets the {@link TrackManager}, responsible for managing
|
||||
* {@link Track} instances.
|
||||
*
|
||||
* <p>This manager can be used to retrieve instances of {@link Track} by
|
||||
* name, or query all loaded tracks.</p>
|
||||
*
|
||||
* <p>The {@link #getStorage() storage} instance should be used to
|
||||
* load/create/save/delete tracks.</p>
|
||||
*
|
||||
* @return the track manager
|
||||
* @since 4.0
|
||||
@@ -92,15 +114,23 @@ public interface LuckPermsApi {
|
||||
TrackManager getTrackManager();
|
||||
|
||||
/**
|
||||
* Schedules an update task to run
|
||||
* Schedules the execution of an update task, and returns an encapsulation
|
||||
* of the task as a {@link CompletableFuture}.
|
||||
*
|
||||
* <p>The exact actions performed in an update task remains an
|
||||
* implementation detail of the plugin, however, as a minimum, it is
|
||||
* expected to perform a full reload of user, group and track data, and
|
||||
* ensure that any changes are fully applied and propagated.</p>
|
||||
*
|
||||
* @return a future
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> runUpdateTask();
|
||||
|
||||
/**
|
||||
* Gets the event bus, used for subscribing to events
|
||||
* Gets the {@link EventBus}, used for subscribing to internal LuckPerms
|
||||
* events.
|
||||
*
|
||||
* @return the event bus
|
||||
* @since 3.0
|
||||
@@ -109,15 +139,18 @@ public interface LuckPermsApi {
|
||||
EventBus getEventBus();
|
||||
|
||||
/**
|
||||
* Gets the configuration
|
||||
* Gets a representation of the plugins configuration
|
||||
*
|
||||
* @return a configuration instance
|
||||
* @return the configuration
|
||||
*/
|
||||
@Nonnull
|
||||
LPConfiguration getConfiguration();
|
||||
|
||||
/**
|
||||
* Gets the backend storage dao
|
||||
* Gets an object representing the plugins primary {@link Storage} backend.
|
||||
*
|
||||
* <p>The instance propagates calls to the internal DAO (Data Access Object),
|
||||
* and applies any changes to the storage provider.</p>
|
||||
*
|
||||
* @return a storage instance
|
||||
* @since 2.14
|
||||
@@ -126,24 +159,61 @@ public interface LuckPermsApi {
|
||||
Storage getStorage();
|
||||
|
||||
/**
|
||||
* Gets the messaging service
|
||||
* Gets the {@link MessagingService}, if present.
|
||||
*
|
||||
* @return an optional that may contain a messaging service instance.
|
||||
* <p>The MessagingService is used to dispatch updates throughout a network
|
||||
* of servers running the plugin.</p>
|
||||
*
|
||||
* <p>Not all instances of LuckPerms will have a messaging service setup and
|
||||
* configured, but it is recommended that all users of the API account for
|
||||
* and make use of this.</p>
|
||||
*
|
||||
* @return the messaging service instance, if present.
|
||||
*/
|
||||
@Nonnull
|
||||
Optional<MessagingService> getMessagingService();
|
||||
|
||||
/**
|
||||
* Gets a {@link UuidCache} instance, providing read access to the LuckPerms
|
||||
* internal uuid caching system
|
||||
* Registers a {@link MessengerProvider} for use by the platform.
|
||||
*
|
||||
* @return a uuid cache instance
|
||||
* <p>Note that the mere action of registering a provider doesn't
|
||||
* necessarily mean that it will be used.</p>
|
||||
*
|
||||
* @param messengerProvider the messenger provider.
|
||||
* @since 4.1
|
||||
*/
|
||||
void registerMessengerProvider(@Nonnull MessengerProvider messengerProvider);
|
||||
|
||||
/**
|
||||
* Gets the {@link ActionLogger}.
|
||||
*
|
||||
* <p>The action logger is responsible for saving and broadcasting defined
|
||||
* actions occurring on the platform.</p>
|
||||
*
|
||||
* @return the action logger
|
||||
* @since 4.1
|
||||
*/
|
||||
ActionLogger getActionLogger();
|
||||
|
||||
/**
|
||||
* Gets a {@link UuidCache}.
|
||||
*
|
||||
* <p>The uuid cache provides read access to the internal LuckPerms uuid
|
||||
* mapping system.</p>
|
||||
*
|
||||
* @return the uuid cache
|
||||
* @deprecated this feature is now handled internally - and the API returns a
|
||||
* No-op implementation of this class.
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
UuidCache getUuidCache();
|
||||
|
||||
/**
|
||||
* Gets the context manager
|
||||
* Gets the {@link ContextManager}.
|
||||
*
|
||||
* <p>The context manager manages {@link ContextCalculator}s, and calculates
|
||||
* applicable contexts for a given type.</p>
|
||||
*
|
||||
* @return the context manager
|
||||
* @since 4.0
|
||||
@@ -151,7 +221,9 @@ public interface LuckPermsApi {
|
||||
ContextManager getContextManager();
|
||||
|
||||
/**
|
||||
* Gets the node factory
|
||||
* Gets the {@link NodeFactory}.
|
||||
*
|
||||
* <p>The node factory provides methods for building {@link Node} instances.</p>
|
||||
*
|
||||
* @return the node factory
|
||||
*/
|
||||
@@ -159,7 +231,11 @@ public interface LuckPermsApi {
|
||||
NodeFactory getNodeFactory();
|
||||
|
||||
/**
|
||||
* Gets the MetaStackFactory
|
||||
* Gets the {@link MetaStackFactory}.
|
||||
*
|
||||
* <p>The metastack factory provides methods for retrieving
|
||||
* {@link me.lucko.luckperms.api.metastacking.MetaStackElement}s and constructing
|
||||
* {@link me.lucko.luckperms.api.metastacking.MetaStackDefinition}s.</p>
|
||||
*
|
||||
* @return the meta stack factory
|
||||
* @since 3.2
|
||||
@@ -167,7 +243,18 @@ public interface LuckPermsApi {
|
||||
@Nonnull
|
||||
MetaStackFactory getMetaStackFactory();
|
||||
|
||||
// convenience methods
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The following methods are provided only for convenience, and offer no
|
||||
* additional functionality.
|
||||
*
|
||||
* They are implemented as "default" methods, using the manager and factory
|
||||
* instances provided by the methods above.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets a wrapped user object from the user storage
|
||||
@@ -349,7 +436,10 @@ public interface LuckPermsApi {
|
||||
* @return a new builder
|
||||
* @since 4.0
|
||||
*/
|
||||
LogEntry.Builder newLogEntryBuilder();
|
||||
@Nonnull
|
||||
default LogEntry.Builder newLogEntryBuilder() {
|
||||
return getActionLogger().newEntryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a permission builder instance
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
package me.lucko.luckperms.api;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A means to push changes to other servers using the platforms networking
|
||||
*
|
||||
@@ -33,11 +35,42 @@ package me.lucko.luckperms.api;
|
||||
public interface MessagingService {
|
||||
|
||||
/**
|
||||
* Uses the messaging service to inform other servers about changes.
|
||||
* Gets the name of this messaging service
|
||||
*
|
||||
* <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>
|
||||
* @return the name of this messaging service
|
||||
* @since 4.1
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Uses the messaging service to inform other servers about a general
|
||||
* change.
|
||||
*
|
||||
* <p>The standard response by other servers will be to execute a overall
|
||||
* sync of all live data, equivalent to calling
|
||||
* {@link LuckPermsApi#runUpdateTask()}.</p>
|
||||
*
|
||||
* <p>This will push the update asynchronously, and this method will return
|
||||
* immediately. Note that this method will not cause an update to be
|
||||
* processed on the local server.</p>
|
||||
*/
|
||||
void pushUpdate();
|
||||
|
||||
/**
|
||||
* Uses the messaging service to inform other servers about a change to a
|
||||
* specific user.
|
||||
*
|
||||
* <p>The standard response by other servers is undefined, however the
|
||||
* current implementation will reload the corresponding users data if they
|
||||
* are online.</p>
|
||||
*
|
||||
* <p>This will push the update asynchronously, and this method will return
|
||||
* immediately. Note that this method will not cause an update to be
|
||||
* processed on the local server.</p>
|
||||
*
|
||||
* @param user the user to push the update for
|
||||
* @since 4.1
|
||||
*/
|
||||
void pushUserUpdate(@Nonnull User user);
|
||||
|
||||
}
|
||||
@@ -32,19 +32,59 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Represents a permission node.
|
||||
* Represents a LuckPerms "node".
|
||||
*
|
||||
* <p>All implementations of this interface must be immutable.</p>
|
||||
* <p>The {@link Node} class encapsulates more than just permission assignments.
|
||||
* Nodes are used to store data about inherited groups, as well as assigned
|
||||
* prefixes, suffixes and meta values.</p>
|
||||
*
|
||||
* <p>Use the {@link NodeFactory} to obtain and construct instances.</p>
|
||||
* <p>Combining these various states into one object (a "node") means that a
|
||||
* holder only has to have one type of data set (a set of nodes) in order to
|
||||
* take on various properties.</p>
|
||||
*
|
||||
* <p>It is recommended that users of the API make use of {@link Stream}s
|
||||
* to manipulate data and obtain the required information.</p>
|
||||
*
|
||||
* <p>This interface provides a number of methods to read the attributes of the
|
||||
* node, as well as methods to query and extract additional state and properties
|
||||
* from these settings.</p>
|
||||
*
|
||||
* <p>Nodes have the following attributes:</p>
|
||||
* <p></p>
|
||||
* <ul>
|
||||
* <li>{@link #getPermission() permission} - the actual permission string</li>
|
||||
* <li>{@link #getValuePrimitive() value} - the value of the node (false for negated)</li>
|
||||
* <li>{@link #isOverride() override} - if the node is marked as having special priority over other nodes</li>
|
||||
* <li>{@link #getServer() server} - the specific server where this node should apply</li>
|
||||
* <li>{@link #getWorld() world} - the specific world where this node should apply</li>
|
||||
* <li>{@link #getContexts() context} - the additional contexts required for this node to apply </li>
|
||||
* <li>{@link #getExpiry() expiry} - the time when this node should expire</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Nodes can also fall into the following sub categories.</p>
|
||||
* <p></p>
|
||||
* <ul>
|
||||
* <li>normal - just a regular permission</li>
|
||||
* <li>{@link #isGroupNode() group node} - a "group node" marks that the holder should inherit data from another group</li>
|
||||
* <li>{@link #isPrefix() prefix} - represents an assigned prefix</li>
|
||||
* <li>{@link #isSuffix() suffix} - represents an assigned suffix</li>
|
||||
* <li>{@link #isMeta() meta} - represents an assigned meta option</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>The core node state must be immutable in all implementations.</p>
|
||||
*
|
||||
* @see NodeFactory for obtaining and constructing instances.
|
||||
* @since 2.6
|
||||
*/
|
||||
public interface Node extends Map.Entry<String, Boolean> {
|
||||
@Immutable
|
||||
public interface Node {
|
||||
|
||||
/**
|
||||
* Gets the permission string
|
||||
@@ -61,7 +101,6 @@ public interface Node extends Map.Entry<String, Boolean> {
|
||||
*
|
||||
* @return the permission's value
|
||||
*/
|
||||
@Override
|
||||
@Nonnull
|
||||
default Boolean getValue() {
|
||||
return getValuePrimitive();
|
||||
@@ -315,82 +354,220 @@ public interface Node extends Map.Entry<String, Boolean> {
|
||||
Map.Entry<Integer, String> getSuffix() throws IllegalStateException;
|
||||
|
||||
/**
|
||||
* Checks if this Node is equal to another node
|
||||
* Returns if this Node is equal to another node
|
||||
*
|
||||
* @param obj the other node
|
||||
* @return true if this node is equal to the other provided
|
||||
* @see #equalsIgnoringValue(Node) for a less strict implementation of this method
|
||||
* @see StandardNodeEquality#EXACT
|
||||
*/
|
||||
@Override
|
||||
boolean equals(Object obj);
|
||||
|
||||
/**
|
||||
* Similar to {@link Node#equals(Object)}, except doesn't take note of the value
|
||||
* Returns if this Node is equal to another node as defined by the given
|
||||
* {@link StandardNodeEquality} predicate.
|
||||
*
|
||||
* @param other the other node
|
||||
* @param equalityPredicate the predicate
|
||||
* @return true if this node is considered equal
|
||||
* @since 4.1
|
||||
*/
|
||||
boolean standardEquals(Node other, StandardNodeEquality equalityPredicate);
|
||||
|
||||
/**
|
||||
* Returns if this Node is equal to another node as defined by the given
|
||||
* {@link NodeEqualityPredicate}.
|
||||
*
|
||||
* @param other the other node
|
||||
* @param equalityPredicate the predicate
|
||||
* @return true if this node is considered equal
|
||||
* @since 4.1
|
||||
*/
|
||||
default boolean equals(Node other, NodeEqualityPredicate equalityPredicate) {
|
||||
return equalityPredicate.areEqual(this, other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link Node#equals(Object)}, except doesn't take note of the
|
||||
* value.
|
||||
*
|
||||
* @param other the other node
|
||||
* @return true if the two nodes are almost equal
|
||||
* @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)}
|
||||
* @see StandardNodeEquality#IGNORE_VALUE
|
||||
*/
|
||||
boolean equalsIgnoringValue(@Nonnull Node other);
|
||||
@Deprecated
|
||||
default boolean equalsIgnoringValue(@Nonnull Node other) {
|
||||
return equals(other, StandardNodeEquality.IGNORE_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link Node#equals(Object)}, except doesn't take note of the expiry time or value
|
||||
* Similar to {@link Node#equals(Object)}, except doesn't take note of the
|
||||
* expiry time or value.
|
||||
*
|
||||
* @param other the other node
|
||||
* @return true if the two nodes are almost equal
|
||||
* @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)}
|
||||
* @see StandardNodeEquality#IGNORE_EXPIRY_TIME_AND_VALUE
|
||||
*/
|
||||
boolean almostEquals(@Nonnull Node other);
|
||||
@Deprecated
|
||||
default boolean almostEquals(@Nonnull Node other) {
|
||||
return equals(other, StandardNodeEquality.IGNORE_EXPIRY_TIME_AND_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link Node#equals(Object)}, except doesn't take note of the value or if the node is temporary
|
||||
* Similar to {@link Node#equals(Object)}, except doesn't take note of the
|
||||
* value or if the node is temporary.
|
||||
*
|
||||
* @param other the other node
|
||||
* @return true if the two nodes are almost equal
|
||||
* @since 2.8
|
||||
* @deprecated in favour of {@link #equals(Node, NodeEqualityPredicate)}
|
||||
* @see StandardNodeEquality#IGNORE_VALUE_OR_IF_TEMPORARY
|
||||
*/
|
||||
boolean equalsIgnoringValueOrTemp(@Nonnull Node other);
|
||||
@Deprecated
|
||||
default boolean equalsIgnoringValueOrTemp(@Nonnull Node other) {
|
||||
return equals(other, StandardNodeEquality.IGNORE_VALUE_OR_IF_TEMPORARY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new builder initially containing the current properties of
|
||||
* this node.
|
||||
*
|
||||
* @return a new builder
|
||||
* @since 4.1
|
||||
*/
|
||||
Builder toBuilder();
|
||||
|
||||
/**
|
||||
* Builds a Node instance
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Sets the value of negated for the node.
|
||||
*
|
||||
* @param negated the value
|
||||
* @return the builder
|
||||
* @see Node#isNegated()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setNegated(boolean negated);
|
||||
|
||||
/**
|
||||
* Sets the value of the node.
|
||||
*
|
||||
* @param value the value
|
||||
* @return the builder
|
||||
* @see Node#getValuePrimitive()
|
||||
*/
|
||||
@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.
|
||||
* Sets the override property for the node.
|
||||
*
|
||||
* <p>Warning: this value does not persist, and disappears when the holder is re-loaded.
|
||||
* It is therefore only useful for transient nodes.</p>
|
||||
*
|
||||
* @param override the override state
|
||||
* @return the builder
|
||||
* @see Node#isOverride()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setOverride(boolean override);
|
||||
|
||||
/**
|
||||
* Sets the nodes expiry as a unix timestamp in seconds.
|
||||
*
|
||||
* @param expireAt the expiry time
|
||||
* @return the builder
|
||||
* @see Node#getExpiryUnixTime()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setExpiry(long expireAt);
|
||||
|
||||
/**
|
||||
* Sets the world value for the node.
|
||||
*
|
||||
* @param world the world value
|
||||
* @return the builder
|
||||
* @see Node#getWorld()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setWorld(@Nonnull String world);
|
||||
Builder setWorld(@Nullable String world);
|
||||
|
||||
/**
|
||||
* Sets the server value for the node.
|
||||
*
|
||||
* @param server the world value
|
||||
* @return the builder
|
||||
* @see Node#getServer()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder setServer(@Nonnull String server) throws IllegalArgumentException;
|
||||
Builder setServer(@Nullable String server);
|
||||
|
||||
/**
|
||||
* Appends an extra context onto the node.
|
||||
*
|
||||
* @param key the context key
|
||||
* @param value the context value
|
||||
* @return the builder
|
||||
* @see ContextSet
|
||||
* @see Node#getContexts()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder withExtraContext(@Nonnull String key, @Nonnull String value);
|
||||
|
||||
/**
|
||||
* Appends extra contexts onto the node.
|
||||
*
|
||||
* @param map a map of contexts
|
||||
* @return the builder
|
||||
* @see ContextSet
|
||||
* @see Node#getContexts()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder withExtraContext(@Nonnull Map<String, String> map);
|
||||
|
||||
/**
|
||||
* Appends extra contexts onto the node.
|
||||
*
|
||||
* @param context a set of contexts
|
||||
* @return the builder
|
||||
* @see ContextSet
|
||||
* @see Node#getContexts()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder withExtraContext(@Nonnull Set<Map.Entry<String, String>> context);
|
||||
|
||||
/**
|
||||
* Appends an extra context onto the node.
|
||||
*
|
||||
* @param entry the context
|
||||
* @return the builder
|
||||
* @see ContextSet
|
||||
* @see Node#getContexts()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder withExtraContext(@Nonnull Map.Entry<String, String> entry);
|
||||
|
||||
/**
|
||||
* Appends extra contexts onto the node.
|
||||
*
|
||||
* @param set a contextset
|
||||
* @return the builder
|
||||
* @see ContextSet
|
||||
* @see Node#getContexts()
|
||||
*/
|
||||
@Nonnull
|
||||
Builder withExtraContext(@Nonnull ContextSet set);
|
||||
|
||||
/**
|
||||
* Creates a {@link Node} instance from the builder.
|
||||
*
|
||||
* @return a new node instance
|
||||
*/
|
||||
@Nonnull
|
||||
Node build();
|
||||
}
|
||||
|
||||
+21
-15
@@ -23,25 +23,31 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.constants;
|
||||
package me.lucko.luckperms.api;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Random constants used throughout the plugin implementation.
|
||||
* A rule for determining if two nodes are equal.
|
||||
*
|
||||
* <p>Generally, individual instances of this interface should fulfil the same
|
||||
* requirements as the {@link Object#equals(Object)} contract.</p>
|
||||
*
|
||||
* <p>Some standard implementations are provided by
|
||||
* {@link StandardNodeEquality}.</p>
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
@UtilityClass
|
||||
public class Constants {
|
||||
@FunctionalInterface
|
||||
public interface NodeEqualityPredicate {
|
||||
|
||||
public static final UUID CONSOLE_UUID = UUID.fromString("00000000-0000-0000-0000-000000000000");
|
||||
public static final String CONSOLE_NAME = "Console";
|
||||
|
||||
public static final UUID IMPORT_UUID = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
public static final String IMPORT_NAME = "Import";
|
||||
|
||||
public static final char COLOR_CHAR = '\u00A7';
|
||||
public static final char FORMAT_CHAR = '&';
|
||||
/**
|
||||
* Returns if the two nodes are equal
|
||||
*
|
||||
* @param o1 the first node
|
||||
* @param o2 the second node
|
||||
* @return true if equal
|
||||
*/
|
||||
boolean areEqual(@Nonnull Node o1, @Nonnull Node o2);
|
||||
|
||||
}
|
||||
@@ -92,6 +92,7 @@ public interface NodeFactory {
|
||||
/**
|
||||
* Creates a node builder for the given chat meta type
|
||||
*
|
||||
* @param type the type
|
||||
* @param priority the priority
|
||||
* @param value the value for the prefix/suffix
|
||||
* @return a node builder instance
|
||||
|
||||
@@ -28,6 +28,7 @@ package me.lucko.luckperms.api;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import me.lucko.luckperms.api.caching.CachedData;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
|
||||
@@ -35,6 +36,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -70,6 +72,24 @@ public interface PermissionHolder {
|
||||
@Nonnull
|
||||
String getFriendlyName();
|
||||
|
||||
/**
|
||||
* Gets the holders {@link CachedData} cache.
|
||||
*
|
||||
* @return the holders cached data.
|
||||
* @since 3.2
|
||||
*/
|
||||
@Nonnull
|
||||
CachedData getCachedData();
|
||||
|
||||
/**
|
||||
* Refreshes and applies any changes to the cached holder data.
|
||||
*
|
||||
* @return the task future
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> refreshCachedData();
|
||||
|
||||
/**
|
||||
* Gets the backing multimap containing every permission this holder has.
|
||||
*
|
||||
@@ -251,6 +271,42 @@ public interface PermissionHolder {
|
||||
*/
|
||||
void auditTemporaryPermissions();
|
||||
|
||||
/**
|
||||
* Checks to see if the object has a certain permission
|
||||
*
|
||||
* @param node the node to check for
|
||||
* @param equalityPredicate how to determine if a node matches
|
||||
* @return a Tristate for the holders permission status for the node
|
||||
* @throws NullPointerException if the node is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
Tristate hasPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate);
|
||||
|
||||
/**
|
||||
* Checks to see if the object has a certain permission
|
||||
*
|
||||
* @param node the node to check for
|
||||
* @param equalityPredicate how to determine if a node matches
|
||||
* @return a Tristate for the holders permission status for the node
|
||||
* @throws NullPointerException if the node is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
Tristate hasTransientPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate);
|
||||
|
||||
/**
|
||||
* Checks to see if the object inherits a certain permission
|
||||
*
|
||||
* @param node the node to check for
|
||||
* @param equalityPredicate how to determine if a node matches
|
||||
* @return a Tristate for the holders inheritance status for the node
|
||||
* @throws NullPointerException if the node is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
Tristate inheritsPermission(@Nonnull Node node, @Nonnull NodeEqualityPredicate equalityPredicate);
|
||||
|
||||
/**
|
||||
* Checks to see if the object has a certain permission
|
||||
*
|
||||
@@ -284,6 +340,29 @@ public interface PermissionHolder {
|
||||
@Nonnull
|
||||
Tristate inheritsPermission(@Nonnull Node node);
|
||||
|
||||
/**
|
||||
* Check to see if this holder 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.
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean inheritsGroup(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Check to see if this holder inherits another group directly
|
||||
*
|
||||
* @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 4.0
|
||||
*/
|
||||
boolean inheritsGroup(@Nonnull Group group, @Nonnull ContextSet contextSet);
|
||||
|
||||
/**
|
||||
* Sets a permission for the object
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Standard {@link NodeEqualityPredicate}s.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public enum StandardNodeEquality implements NodeEqualityPredicate {
|
||||
|
||||
/**
|
||||
* Represents an exact match.
|
||||
*
|
||||
* <p>All attributes of the nodes must match for them to be considered
|
||||
* equal.</p>
|
||||
*/
|
||||
EXACT,
|
||||
|
||||
/**
|
||||
* All attributes must match, except for
|
||||
* {@link Node#getValuePrimitive() value}, which is ignored.
|
||||
*/
|
||||
IGNORE_VALUE,
|
||||
|
||||
/**
|
||||
* All attributes must match, except for the
|
||||
* {@link Node#getExpiry() expiry time}, which is ignored.
|
||||
*
|
||||
* <p>Note that with this setting, whether a node is temporary or not is
|
||||
* still considered.</p>
|
||||
*/
|
||||
IGNORE_EXPIRY_TIME,
|
||||
|
||||
/**
|
||||
* All attributes must match, except for
|
||||
* {@link Node#getValuePrimitive() value} and the
|
||||
* {@link Node#getExpiry() expiry time}, which are ignored.
|
||||
*/
|
||||
IGNORE_EXPIRY_TIME_AND_VALUE,
|
||||
|
||||
/**
|
||||
* All attributes must match, except for
|
||||
* {@link Node#getValuePrimitive() value} and the if the node is
|
||||
* {@link Node#isTemporary() temporary}, which are ignored.
|
||||
*/
|
||||
IGNORE_VALUE_OR_IF_TEMPORARY;
|
||||
|
||||
@Override
|
||||
public boolean areEqual(@Nonnull Node o1, @Nonnull Node o2) {
|
||||
return o1.standardEquals(o2, this);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
package me.lucko.luckperms.api;
|
||||
|
||||
import me.lucko.luckperms.api.manager.GroupManager;
|
||||
import me.lucko.luckperms.api.manager.TrackManager;
|
||||
import me.lucko.luckperms.api.manager.UserManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -38,13 +42,15 @@ import javax.annotation.Nullable;
|
||||
/**
|
||||
* A means of loading and saving permission data to/from the backend.
|
||||
*
|
||||
* <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>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, register a
|
||||
* callback using {@link CompletableFuture#thenAcceptAsync(Consumer, Executor)} and {@link #getSyncExecutor()}.</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, register a
|
||||
* callback using {@link CompletableFuture#thenAcceptAsync(Consumer, Executor)}.</p>
|
||||
*
|
||||
* @since 2.14
|
||||
*/
|
||||
@@ -58,40 +64,6 @@ public interface Storage {
|
||||
@Nonnull
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Gets whether the storage instance is allowing logins on the platform.
|
||||
*
|
||||
* @return true if logins are enabled
|
||||
*/
|
||||
boolean isAcceptingLogins();
|
||||
|
||||
/**
|
||||
* Returns an executor which will run all passed runnables on the main server thread.
|
||||
*
|
||||
* @return an executor instance
|
||||
*/
|
||||
@Nonnull
|
||||
Executor getSyncExecutor();
|
||||
|
||||
/**
|
||||
* Returns an executor which will run all passed runnables asynchronously using the platforms scheduler and thread
|
||||
* pools.
|
||||
*
|
||||
* @return an executor instance
|
||||
*/
|
||||
@Nonnull
|
||||
Executor getAsyncExecutor();
|
||||
|
||||
/**
|
||||
* Saves an action to storage
|
||||
*
|
||||
* @param entry the log entry to be saved
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if entry is null
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Boolean> logAction(@Nonnull LogEntry entry);
|
||||
|
||||
/**
|
||||
* Loads and returns the entire log from storage
|
||||
*
|
||||
@@ -100,40 +72,6 @@ public interface Storage {
|
||||
@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, or null if it is not known.
|
||||
* @return if the operation completed successfully
|
||||
* @throws NullPointerException if uuid is null
|
||||
*/
|
||||
@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.
|
||||
*
|
||||
* @param user the user to save
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if user is null
|
||||
* @throws IllegalStateException if the user instance was not obtained from LuckPerms.
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Boolean> saveUser(@Nonnull User user);
|
||||
|
||||
/**
|
||||
* Gets a set all "unique" user UUIDs.
|
||||
*
|
||||
@@ -155,58 +93,6 @@ public interface Storage {
|
||||
@Nonnull
|
||||
CompletableFuture<List<HeldPermission<UUID>>> getUsersWithPermission(@Nonnull String permission);
|
||||
|
||||
/**
|
||||
* Creates and loads a group into the plugins local storage
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @return true if the operation completed successfully
|
||||
* @throws NullPointerException if name is null
|
||||
* @throws IllegalArgumentException if the name is invalid
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Boolean> createAndLoadGroup(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Loads a group into the plugins local storage.
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @return true if the operation completed successfully
|
||||
* @throws NullPointerException if name is null
|
||||
* @throws IllegalArgumentException if the name is invalid
|
||||
*/
|
||||
@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();
|
||||
|
||||
/**
|
||||
* Saves a group back to storage. You should call this after you make any changes to a group.
|
||||
*
|
||||
* @param group the group to save
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if group is null
|
||||
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Boolean> saveGroup(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Permanently deletes a group from storage.
|
||||
*
|
||||
* @param group the group to delete
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if group is null
|
||||
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Boolean> deleteGroup(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Searches for a list of groups with a given permission.
|
||||
*
|
||||
@@ -218,58 +104,6 @@ public interface Storage {
|
||||
@Nonnull
|
||||
CompletableFuture<List<HeldPermission<String>>> getGroupsWithPermission(@Nonnull String permission);
|
||||
|
||||
/**
|
||||
* Creates and loads a track into the plugins local storage
|
||||
*
|
||||
* @param name the name of the track
|
||||
* @return true if the operation completed successfully
|
||||
* @throws NullPointerException if name is null
|
||||
* @throws IllegalArgumentException if the name is invalid
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Boolean> createAndLoadTrack(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Loads a track into the plugins local storage.
|
||||
*
|
||||
* @param name the name of the track
|
||||
* @return true if the operation completed successfully
|
||||
* @throws NullPointerException if name is null
|
||||
* @throws IllegalArgumentException if the name is invalid
|
||||
*/
|
||||
@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();
|
||||
|
||||
/**
|
||||
* Saves a track back to storage. You should call this after you make any changes to a track.
|
||||
*
|
||||
* @param track the track to save
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if track is null
|
||||
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Boolean> saveTrack(@Nonnull Track track);
|
||||
|
||||
/**
|
||||
* Permanently deletes a track from storage
|
||||
*
|
||||
* @param track the track to delete
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if track is null
|
||||
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Boolean> deleteTrack(@Nonnull Track track);
|
||||
|
||||
/**
|
||||
* Saves UUID caching data to the global cache
|
||||
*
|
||||
@@ -304,4 +138,221 @@ public interface Storage {
|
||||
@Nonnull
|
||||
CompletableFuture<String> getName(@Nonnull UUID uuid);
|
||||
|
||||
/**
|
||||
* Gets whether the storage instance is allowing logins on the platform.
|
||||
*
|
||||
* @return true if logins are enabled
|
||||
* @deprecated as this method always returns true.
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isAcceptingLogins();
|
||||
|
||||
/**
|
||||
* Returns an executor which will run all passed runnables on the
|
||||
* main server thread.
|
||||
*
|
||||
* <p>This method is deprecated as plugins should create and use their own
|
||||
* executor instances.</p>
|
||||
*
|
||||
* @return an executor instance
|
||||
* @deprecated as plugins should create their own executors
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
Executor getSyncExecutor();
|
||||
|
||||
/**
|
||||
* Returns an executor which will run all passed runnables asynchronously
|
||||
* using the platforms scheduler and thread pools.
|
||||
*
|
||||
* <p>This method is deprecated as plugins should create and use their own
|
||||
* executor instances.</p>
|
||||
*
|
||||
* @return an executor instance
|
||||
* @deprecated as plugins should create their own executors
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
Executor getAsyncExecutor();
|
||||
|
||||
/**
|
||||
* Saves an action to storage
|
||||
*
|
||||
* @param entry the log entry to be saved
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if entry is null
|
||||
* @deprecated in favour of {@link ActionLogger#submit(LogEntry)}.
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> logAction(@Nonnull LogEntry entry);
|
||||
|
||||
/**
|
||||
* 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, or null if it is not known.
|
||||
* @return if the operation completed successfully
|
||||
* @throws NullPointerException if uuid is null
|
||||
* @deprecated in favour of {@link UserManager#loadUser(UUID, String)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
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
|
||||
* @deprecated in favour of {@link UserManager#loadUser(UUID)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
default CompletableFuture<Boolean> loadUser(@Nonnull UUID uuid) {
|
||||
return loadUser(uuid, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a user object back to storage.
|
||||
*
|
||||
* <p>You should call this after you make any changes to a user.</p>
|
||||
*
|
||||
* @param user the user to save
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if user is null
|
||||
* @throws IllegalStateException if the user instance was not obtained from LuckPerms.
|
||||
* @deprecated in favour of {@link UserManager#saveUser(User)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> saveUser(@Nonnull User user);
|
||||
|
||||
/**
|
||||
* Creates and loads a group into the plugins local storage
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @return true if the operation completed successfully
|
||||
* @throws NullPointerException if name is null
|
||||
* @throws IllegalArgumentException if the name is invalid
|
||||
* @deprecated in favour of {@link GroupManager#createAndLoadGroup(String)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> createAndLoadGroup(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Loads a group into the plugins local storage.
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @return true if the operation completed successfully
|
||||
* @throws NullPointerException if name is null
|
||||
* @throws IllegalArgumentException if the name is invalid
|
||||
* @deprecated in favour of {@link GroupManager#loadGroup(String)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> loadGroup(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Loads all groups from the storage into memory
|
||||
*
|
||||
* @return true if the operation completed successfully.
|
||||
* @deprecated in favour of {@link GroupManager#loadAllGroups()}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> loadAllGroups();
|
||||
|
||||
/**
|
||||
* Saves a group back to storage.
|
||||
*
|
||||
* <p>You should call this after you make any changes to a group.</p>
|
||||
*
|
||||
* @param group the group to save
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if group is null
|
||||
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
|
||||
* @deprecated in favour of {@link GroupManager#saveGroup(Group)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> saveGroup(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Permanently deletes a group from storage.
|
||||
*
|
||||
* @param group the group to delete
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if group is null
|
||||
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
|
||||
* @deprecated in favour of {@link GroupManager#deleteGroup(Group)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> deleteGroup(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Creates and loads a track into the plugins local storage
|
||||
*
|
||||
* @param name the name of the track
|
||||
* @return true if the operation completed successfully
|
||||
* @throws NullPointerException if name is null
|
||||
* @throws IllegalArgumentException if the name is invalid
|
||||
* @deprecated in favour of {@link TrackManager#createAndLoadTrack(String)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> createAndLoadTrack(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Loads a track into the plugins local storage.
|
||||
*
|
||||
* @param name the name of the track
|
||||
* @return true if the operation completed successfully
|
||||
* @throws NullPointerException if name is null
|
||||
* @throws IllegalArgumentException if the name is invalid
|
||||
* @deprecated in favour of {@link TrackManager#loadTrack(String)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> loadTrack(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Loads all tracks from the storage into memory
|
||||
*
|
||||
* @return true if the operation completed successfully.
|
||||
* @deprecated in favour of {@link TrackManager#loadAllTracks()}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> loadAllTracks();
|
||||
|
||||
/**
|
||||
* Saves a track back to storage. You should call this after you make any changes to a track.
|
||||
*
|
||||
* @param track the track to save
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if track is null
|
||||
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
|
||||
* @deprecated in favour of {@link TrackManager#saveTrack(Track)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> saveTrack(@Nonnull Track track);
|
||||
|
||||
/**
|
||||
* Permanently deletes a track from storage
|
||||
*
|
||||
* @param track the track to delete
|
||||
* @return true if the operation completed successfully.
|
||||
* @throws NullPointerException if track is null
|
||||
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
|
||||
* @deprecated in favour of {@link TrackManager#deleteTrack(Track)}
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
CompletableFuture<Boolean> deleteTrack(@Nonnull Track track);
|
||||
|
||||
}
|
||||
@@ -59,13 +59,29 @@ public enum Tristate {
|
||||
* Returns a {@link Tristate} from a boolean
|
||||
*
|
||||
* @param val the boolean value
|
||||
* @return {@link #TRUE} or {@link #FALSE}, depending on the value of the boolean.
|
||||
* @return {@link #TRUE} or {@link #FALSE}, if the value is <code>true</code> or <code>false</code>, respectively.
|
||||
*/
|
||||
@Nonnull
|
||||
public static Tristate fromBoolean(boolean val) {
|
||||
return val ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Tristate} from a nullable boolean.
|
||||
*
|
||||
* <p>Unlike {@link #fromBoolean(boolean)}, this method returns {@link #UNDEFINED}
|
||||
* if the value is null.</p>
|
||||
*
|
||||
* @param val the boolean value
|
||||
* @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value
|
||||
* is <code>null</code>, <code>true</code> or <code>false</code>, respectively.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
public static Tristate fromNullableBoolean(Boolean val) {
|
||||
return val == null ? UNDEFINED : val ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
private final boolean booleanValue;
|
||||
|
||||
Tristate(boolean booleanValue) {
|
||||
@@ -80,6 +96,6 @@ public enum Tristate {
|
||||
* @return a boolean representation of the Tristate.
|
||||
*/
|
||||
public boolean asBoolean() {
|
||||
return booleanValue;
|
||||
return this.booleanValue;
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,8 @@
|
||||
package me.lucko.luckperms.api;
|
||||
|
||||
import me.lucko.luckperms.api.caching.UserData;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -85,38 +83,9 @@ public interface User extends PermissionHolder {
|
||||
* @since 3.2
|
||||
*/
|
||||
@Nonnull
|
||||
@Override
|
||||
UserData getCachedData();
|
||||
|
||||
/**
|
||||
* Refreshes and applies any changes to the cached user data.
|
||||
*
|
||||
* @return the task future
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> refreshCachedData();
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
|
||||
*/
|
||||
boolean isInGroup(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Check to see if the user is a direct member of a group in a specific context
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
boolean isInGroup(@Nonnull Group group, @Nonnull ContextSet contextSet);
|
||||
|
||||
/**
|
||||
* Refresh and re-assign the users permissions.
|
||||
*
|
||||
|
||||
@@ -30,36 +30,40 @@ import java.util.UUID;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A UUID cache for online users, between external Mojang UUIDs, and internal LuckPerms UUIDs.
|
||||
* A UUID cache for online users, between external Mojang UUIDs, and internal
|
||||
* LuckPerms UUIDs.
|
||||
*
|
||||
* <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>When this setting is disabled, this cache becomes active, and allows you to convert
|
||||
* between 'internal' and 'server provided' uuids.</p>
|
||||
*
|
||||
* <p><strong>This is only effective for online players. Use {@link Storage#getUUID(String)} for offline players.</strong></p>
|
||||
* @deprecated this feature is now handled internally - and the API returns a
|
||||
* No-op implementation of this class.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface UuidCache {
|
||||
|
||||
/**
|
||||
* Gets a users "internal" LuckPerms UUID, from the one given by the server.
|
||||
*
|
||||
* <p>When <code>use-server-uuids</code> is true, this returns the same UUID instance.</p>
|
||||
* <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>
|
||||
* @param mojangUuid the UUID assigned by the server, through
|
||||
* <code>Player#getUniqueId</code> or
|
||||
* <code>ProxiedPlayer#getUniqueId</code>
|
||||
* @return the corresponding internal UUID
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
UUID getUUID(@Nonnull UUID mojangUuid);
|
||||
|
||||
/**
|
||||
* Gets a users "external", server 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 internalUuid the UUID used within LuckPerms, through <code>User#getUuid</code>
|
||||
* @param internalUuid the UUID used within LuckPerms, through
|
||||
* <code>User#getUuid</code>
|
||||
* @return the corresponding external UUID
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated
|
||||
UUID getExternalUUID(@Nonnull UUID internalUuid);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* 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 me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.PermissionHolder;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Holds cached permission and meta lookup data for a {@link PermissionHolder}.
|
||||
*
|
||||
* <p>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.</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 4.0
|
||||
*/
|
||||
public interface CachedData {
|
||||
|
||||
/**
|
||||
* Gets PermissionData from the cache, given a specified context.
|
||||
*
|
||||
* @param contexts the contexts to get the permission data in
|
||||
* @return a permission data instance
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
@Nonnull
|
||||
PermissionData getPermissionData(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Gets MetaData from the cache, given a specified context.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @param contexts the contexts to get the permission data in
|
||||
* @return a meta data instance
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
@Nonnull
|
||||
MetaData getMetaData(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Calculates permission data, bypassing the cache.
|
||||
*
|
||||
* <p>The result of this operation is calculated each time the method is called.
|
||||
* The result is not added to the internal cache.</p>
|
||||
*
|
||||
* <p>It is therefore highly recommended to use {@link #getPermissionData(Contexts)} instead.</p>
|
||||
*
|
||||
* <p>The use cases of this method are more around constructing one-time
|
||||
* instances of {@link PermissionData}, without adding the result to the cache.</p>
|
||||
*
|
||||
* @param contexts the contexts to get permission data in
|
||||
* @return a permission data instance
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
@Nonnull
|
||||
PermissionData calculatePermissions(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Calculates meta data, bypassing the cache.
|
||||
*
|
||||
* <p>The result of this operation is calculated each time the method is called.
|
||||
* The result is not added to the internal cache.</p>
|
||||
*
|
||||
* <p>It is therefore highly recommended to use {@link #getMetaData(MetaContexts)} instead.</p>
|
||||
*
|
||||
* <p>The use cases of this method are more around constructing one-time
|
||||
* instances of {@link MetaData}, without adding the result to the cache.</p>
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* <p>The result of this operation is calculated each time the method is called.
|
||||
* The result is not added to the internal cache.</p>
|
||||
*
|
||||
* <p>It is therefore highly recommended to use {@link #getMetaData(Contexts)} instead.</p>
|
||||
*
|
||||
* <p>The use cases of this method are more around constructing one-time
|
||||
* instances of {@link MetaData}, without adding the result to the cache.</p>
|
||||
*
|
||||
* @param contexts the contexts to get meta data in
|
||||
* @return a meta data instance
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
@Nonnull
|
||||
MetaData calculateMeta(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* (Re)calculates permission data for a given context.
|
||||
*
|
||||
* <p>This method returns immediately in all cases. The (re)calculation is
|
||||
* performed asynchronously and applied to the cache in the background.</p>
|
||||
*
|
||||
* <p>If there was a previous {@link PermissionData} instance associated with
|
||||
* the given {@link Contexts}, then that instance will continue to be returned by
|
||||
* {@link #getPermissionData(Contexts)} until the recalculation is completed.</p>
|
||||
*
|
||||
* <p>If there was no value calculated and cached prior to the call of this
|
||||
* method, then one will be calculated.</p>
|
||||
*
|
||||
* @param contexts the contexts to recalculate in.
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
void recalculatePermissions(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* (Re)calculates meta data for a given context.
|
||||
*
|
||||
* <p>This method returns immediately in all cases. The (re)calculation is
|
||||
* performed asynchronously and applied to the cache in the background.</p>
|
||||
*
|
||||
* <p>If there was a previous {@link MetaData} instance associated with
|
||||
* the given {@link MetaContexts}, then that instance will continue to be returned by
|
||||
* {@link #getMetaData(MetaContexts)} until the recalculation is completed.</p>
|
||||
*
|
||||
* <p>If there was no value calculated and cached prior to the call of this
|
||||
* method, then one will be calculated.</p>
|
||||
*
|
||||
* @param contexts the contexts to recalculate in.
|
||||
* @throws NullPointerException if contexts is null
|
||||
* @since 3.2
|
||||
*/
|
||||
void recalculateMeta(@Nonnull MetaContexts contexts);
|
||||
|
||||
/**
|
||||
* (Re)calculates meta data for a given context.
|
||||
*
|
||||
* <p>This method returns immediately in all cases. The (re)calculation is
|
||||
* performed asynchronously and applied to the cache in the background.</p>
|
||||
*
|
||||
* <p>If there was a previous {@link MetaData} instance associated with
|
||||
* the given {@link Contexts}, then that instance will continue to be returned by
|
||||
* {@link #getMetaData(Contexts)} until the recalculation is completed.</p>
|
||||
*
|
||||
* <p>If there was no value calculated and cached prior to the call of this
|
||||
* method, then one will be calculated.</p>
|
||||
*
|
||||
* @param contexts the contexts to recalculate in.
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
void recalculateMeta(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* (Re)loads permission data for a given context.
|
||||
*
|
||||
* <p>Unlike {@link #recalculatePermissions(Contexts)}, this method immediately
|
||||
* invalidates any previous {@link PermissionData} values contained within the cache,
|
||||
* and then schedules a task to reload a new {@link PermissionData} instance to
|
||||
* replace the one which was invalidated.</p>
|
||||
*
|
||||
* <p>The invalidation happens immediately during the execution of this method.
|
||||
* The result of the re-computation encapsulated by the future.</p>
|
||||
*
|
||||
* <p>Subsequent calls to {@link #getPermissionData(Contexts)} will block until
|
||||
* the result of this operation is complete.</p>
|
||||
*
|
||||
* <p>If there was no value calculated and cached prior to the call of this
|
||||
* method, then one will be calculated.</p>
|
||||
*
|
||||
* <p>This method returns a Future so users can optionally choose to wait
|
||||
* until the recalculation has been performed.</p>
|
||||
*
|
||||
* @param contexts the contexts to reload in.
|
||||
* @throws NullPointerException if contexts is null
|
||||
* @return a future
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<? extends PermissionData> reloadPermissions(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* (Re)loads meta data for a given context.
|
||||
*
|
||||
* <p>Unlike {@link #recalculateMeta(MetaContexts)}, this method immediately
|
||||
* invalidates any previous {@link MetaData} values contained within the cache,
|
||||
* and then schedules a task to reload a new {@link MetaData} instance to
|
||||
* replace the one which was invalidated.</p>
|
||||
*
|
||||
* <p>The invalidation happens immediately during the execution of this method.
|
||||
* The result of the re-computation encapsulated by the future.</p>
|
||||
*
|
||||
* <p>Subsequent calls to {@link #getMetaData(MetaContexts)} will block until
|
||||
* the result of this operation is complete.</p>
|
||||
*
|
||||
* <p>If there was no value calculated and cached prior to the call of this
|
||||
* method, then one will be calculated.</p>
|
||||
*
|
||||
* <p>This method returns a Future so users can optionally choose to wait
|
||||
* until the recalculation has been performed.</p>
|
||||
*
|
||||
* @param contexts the contexts to reload in.
|
||||
* @throws NullPointerException if contexts is null
|
||||
* @return a future
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<? extends MetaData> reloadMeta(@Nonnull MetaContexts contexts);
|
||||
|
||||
/**
|
||||
* (Re)loads meta data for a given context.
|
||||
*
|
||||
* <p>Unlike {@link #recalculateMeta(Contexts)}, this method immediately
|
||||
* invalidates any previous {@link MetaData} values contained within the cache,
|
||||
* and then schedules a task to reload a new {@link MetaData} instance to
|
||||
* replace the one which was invalidated.</p>
|
||||
*
|
||||
* <p>The invalidation happens immediately during the execution of this method.
|
||||
* The result of the re-computation encapsulated by the future.</p>
|
||||
*
|
||||
* <p>Subsequent calls to {@link #getMetaData(Contexts)} will block until
|
||||
* the result of this operation is complete.</p>
|
||||
*
|
||||
* <p>If there was no value calculated and cached prior to the call of this
|
||||
* method, then one will be calculated.</p>
|
||||
*
|
||||
* <p>This method returns a Future so users can optionally choose to wait
|
||||
* until the recalculation has been performed.</p>
|
||||
*
|
||||
* @param contexts the contexts to reload in.
|
||||
* @throws NullPointerException if contexts is null
|
||||
* @return a future
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<? extends MetaData> reloadMeta(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Recalculates permission data for all known contexts.
|
||||
*
|
||||
* <p>This method returns immediately. The recalculation is performed
|
||||
* asynchronously and applied to the cache in the background.</p>
|
||||
*
|
||||
* <p>The previous {@link PermissionData} instances will continue to be returned
|
||||
* by {@link #getPermissionData(Contexts)} until the recalculation is completed.</p>
|
||||
*/
|
||||
void recalculatePermissions();
|
||||
|
||||
/**
|
||||
* Recalculates meta data for all known contexts.
|
||||
*
|
||||
* <p>This method returns immediately. The recalculation is performed
|
||||
* asynchronously and applied to the cache in the background.</p>
|
||||
*
|
||||
* <p>The previous {@link MetaData} instances will continue to be returned
|
||||
* by {@link #getMetaData(MetaContexts)} and {@link #getMetaData(Contexts)}
|
||||
* until the recalculation is completed.</p>
|
||||
*/
|
||||
void recalculateMeta();
|
||||
|
||||
/**
|
||||
* Reloads permission data for all known contexts.
|
||||
*
|
||||
* <p>Unlike {@link #recalculatePermissions()}, this method immediately
|
||||
* invalidates all previous {@link PermissionData} values contained within the cache,
|
||||
* and then schedules a task to reload a new {@link PermissionData} instances to
|
||||
* replace the ones which were invalidated.</p>
|
||||
*
|
||||
* <p>The invalidation happens immediately during the execution of this method.
|
||||
* The result of the re-computation encapsulated by the future.</p>
|
||||
*
|
||||
* <p>Subsequent calls to {@link #getPermissionData(Contexts)} will block until
|
||||
* the result of this operation is complete.</p>
|
||||
*
|
||||
* <p>This method returns a Future so users can optionally choose to wait
|
||||
* until the recalculation has been performed.</p>
|
||||
*
|
||||
* @return a future
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> reloadPermissions();
|
||||
|
||||
/**
|
||||
* Reloads meta data for all known contexts.
|
||||
*
|
||||
* <p>Unlike {@link #recalculateMeta()}, this method immediately
|
||||
* invalidates all previous {@link MetaData} values contained within the cache,
|
||||
* and then schedules a task to reload a new {@link MetaData} instances to
|
||||
* replace the ones which were invalidated.</p>
|
||||
*
|
||||
* <p>The invalidation happens immediately during the execution of this method.
|
||||
* The result of the re-computation encapsulated by the future.</p>
|
||||
*
|
||||
* <p>Subsequent calls to {@link #getMetaData(MetaContexts)} and
|
||||
* {@link #getMetaData(Contexts)} will block until the result of this operation
|
||||
* is complete.</p>
|
||||
*
|
||||
* <p>This method returns a Future so users can optionally choose to wait
|
||||
* until the recalculation has been performed.</p>
|
||||
*
|
||||
* @return a future
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> reloadMeta();
|
||||
|
||||
/**
|
||||
* Pre-calculates and caches {@link PermissionData} and {@link MetaData}
|
||||
* instances for the given contexts.
|
||||
*
|
||||
* <p>If the cache already contains a value for the given context,
|
||||
* no action is taken.</p>
|
||||
*
|
||||
* <p>This method blocks until the calculation is completed.</p>
|
||||
*
|
||||
* @param contexts a set of contexts
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
default void preCalculate(@Nonnull Set<Contexts> contexts) {
|
||||
contexts.forEach(this::preCalculate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-calculates and caches {@link PermissionData} and {@link MetaData}
|
||||
* instances for a given context.
|
||||
*
|
||||
* <p>If the cache already contains a value for the given context,
|
||||
* no action is taken.</p>
|
||||
*
|
||||
* <p>This method blocks until the calculation is completed.</p>
|
||||
*
|
||||
* @param contexts the contexts to pre-calculate for
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
void preCalculate(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Invalidates any cached {@link PermissionData} instances mapped to the given
|
||||
* {@link Contexts}.
|
||||
*
|
||||
* @param contexts the contexts to invalidate for
|
||||
* @since 4.0
|
||||
*/
|
||||
void invalidatePermissions(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Invalidates any cached {@link MetaData} instances mapped to the given
|
||||
* {@link MetaContexts}.
|
||||
*
|
||||
* @param contexts the contexts to invalidate for
|
||||
* @since 4.0
|
||||
*/
|
||||
void invalidateMeta(@Nonnull MetaContexts contexts);
|
||||
|
||||
/**
|
||||
* Invalidates any cached {@link MetaData} instances mapped to the given
|
||||
* {@link Contexts}.
|
||||
*
|
||||
* @param contexts the contexts to invalidate for
|
||||
* @since 4.0
|
||||
*/
|
||||
void invalidateMeta(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Invalidates all of the underlying Permission calculators.
|
||||
*
|
||||
* <p>Can be called to allow for an update in defaults.</p>
|
||||
*/
|
||||
void invalidatePermissionCalculators();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 me.lucko.luckperms.api.Contexts;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Holds cached lookup data in a specific set of contexts.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface CachedDataContainer {
|
||||
|
||||
/**
|
||||
* Gets the contexts this container is holding data for.
|
||||
*
|
||||
* @return the contexts this container is caching
|
||||
*/
|
||||
@Nonnull
|
||||
Contexts getContexts();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.Group;
|
||||
|
||||
/**
|
||||
* Holds cached permission and meta lookup data for a {@link Group}.
|
||||
*
|
||||
* <p>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.</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 4.0
|
||||
*/
|
||||
public interface GroupData extends CachedData {
|
||||
|
||||
}
|
||||
@@ -25,21 +25,23 @@
|
||||
|
||||
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 java.util.Objects;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Represents the context for a meta lookup.
|
||||
* Encapsulates the options and settings for a meta lookup.
|
||||
*
|
||||
* <p>Consisting of a standard {@link Contexts} element, plus options to define how
|
||||
* <p>Consists of a standard {@link Contexts} element, plus options to define how
|
||||
* the meta stack should be constructed.</p>
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
@Immutable
|
||||
public final class MetaContexts {
|
||||
|
||||
/**
|
||||
@@ -69,9 +71,9 @@ public final class MetaContexts {
|
||||
* @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.contexts = Objects.requireNonNull(contexts, "contexts");
|
||||
this.prefixStackDefinition = Objects.requireNonNull(prefixStackDefinition, "prefixStackDefinition");
|
||||
this.suffixStackDefinition = Objects.requireNonNull(suffixStackDefinition, "suffixStackDefinition");
|
||||
this.hashCode = calculateHashCode();
|
||||
}
|
||||
|
||||
@@ -104,23 +106,18 @@ public final class MetaContexts {
|
||||
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());
|
||||
final MetaContexts that = (MetaContexts) o;
|
||||
return this.contexts.equals(that.contexts) &&
|
||||
this.prefixStackDefinition.equals(that.prefixStackDefinition) &&
|
||||
this.suffixStackDefinition.equals(that.suffixStackDefinition);
|
||||
}
|
||||
|
||||
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;
|
||||
return Objects.hash(this.contexts, this.prefixStackDefinition, this.suffixStackDefinition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
return this.hashCode;
|
||||
}
|
||||
}
|
||||
@@ -36,11 +36,19 @@ import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Holds cached Meta lookup data for a specific set of contexts
|
||||
* Holds cached meta lookup data for a specific set of contexts.
|
||||
*
|
||||
* @since 2.13
|
||||
*/
|
||||
public interface MetaData {
|
||||
public interface MetaData extends CachedDataContainer {
|
||||
|
||||
/**
|
||||
* Gets the contexts this container is holding data for.
|
||||
*
|
||||
* @return the contexts this container is caching
|
||||
*/
|
||||
@Nonnull
|
||||
MetaContexts getMetaContexts();
|
||||
|
||||
/**
|
||||
* Gets an immutable copy of the meta this user has.
|
||||
|
||||
@@ -32,14 +32,14 @@ import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Holds cached Permission lookup data for a specific set of contexts
|
||||
* Holds cached permission lookup data for a specific set of contexts.
|
||||
*
|
||||
* @since 2.13
|
||||
*/
|
||||
public interface PermissionData {
|
||||
public interface PermissionData extends CachedDataContainer {
|
||||
|
||||
/**
|
||||
* Gets a permission value for the given permission node
|
||||
* Gets a permission check result for the given permission node.
|
||||
*
|
||||
* @param permission the permission node
|
||||
* @return a tristate result
|
||||
|
||||
@@ -26,16 +26,13 @@
|
||||
package me.lucko.luckperms.api.caching;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import me.lucko.luckperms.api.User;
|
||||
|
||||
/**
|
||||
* Holds cached permission and meta lookup data for a {@link me.lucko.luckperms.api.User}.
|
||||
* Holds cached permission and meta lookup data for a {@link 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.</p>
|
||||
* <p>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.</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
|
||||
@@ -43,143 +40,6 @@ import javax.annotation.Nonnull;
|
||||
*
|
||||
* @since 2.13
|
||||
*/
|
||||
public interface UserData {
|
||||
|
||||
/**
|
||||
* Gets PermissionData from the cache, given a specified context.
|
||||
*
|
||||
* <p>If the data is not cached, it is calculated. Therefore, this call could be costly.</p>
|
||||
*
|
||||
* @param contexts the contexts to get the permission data in
|
||||
* @return a permission data instance
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
@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>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@Nonnull
|
||||
MetaData getMetaData(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Calculates permission data, bypassing the cache.
|
||||
*
|
||||
* @param contexts the contexts to get permission data in
|
||||
* @return a permission data instance
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
@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.
|
||||
*
|
||||
* @param contexts the contexts to get meta data in
|
||||
* @return a meta data instance
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
@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>
|
||||
*
|
||||
* @param contexts the contexts to recalculate in.
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
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>
|
||||
*
|
||||
* @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(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Calls {@link #recalculatePermissions(Contexts)} for all current loaded contexts
|
||||
*/
|
||||
void recalculatePermissions();
|
||||
|
||||
/**
|
||||
* Calls {@link #recalculateMeta(Contexts)} for all current loaded contexts
|
||||
*/
|
||||
void recalculateMeta();
|
||||
|
||||
/**
|
||||
* Calls {@link #preCalculate(Contexts)} for the given contexts
|
||||
*
|
||||
* @param contexts a set of contexts
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
void preCalculate(@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>
|
||||
*
|
||||
* @param contexts the contexts to pre-calculate for
|
||||
* @throws NullPointerException if contexts is null
|
||||
*/
|
||||
void preCalculate(@Nonnull Contexts contexts);
|
||||
|
||||
/**
|
||||
* Invalidates all of the underlying Permission calculators.
|
||||
*
|
||||
* <p>Can be called to allow for an update in defaults.</p>
|
||||
*/
|
||||
void invalidatePermissionCalculators();
|
||||
public interface UserData extends CachedData {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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 com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
abstract class AbstractContextSet implements ContextSet {
|
||||
|
||||
protected abstract Multimap<String, String> backing();
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated
|
||||
public Map<String, String> toMap() {
|
||||
ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
|
||||
for (Map.Entry<String, String> e : backing().entries()) {
|
||||
m.put(e.getKey(), e.getValue());
|
||||
}
|
||||
return m.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(@Nonnull String key) {
|
||||
return backing().containsKey(sanitizeKey(key));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<String> getValues(@Nonnull String key) {
|
||||
Collection<String> values = backing().asMap().get(sanitizeKey(key));
|
||||
return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(@Nonnull String key, @Nonnull String value) {
|
||||
return backing().containsEntry(sanitizeKey(key), sanitizeValue(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
|
||||
String v = sanitizeValue(value);
|
||||
|
||||
Collection<String> values = backing().asMap().get(sanitizeKey(key));
|
||||
if (values == null || values.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (values.contains(v)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (String val : values) {
|
||||
if (val.equalsIgnoreCase(v)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return backing().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return backing().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof ContextSet)) return false;
|
||||
final ContextSet that = (ContextSet) o;
|
||||
|
||||
final Multimap<String, String> otherContexts;
|
||||
|
||||
if (that instanceof AbstractContextSet) {
|
||||
otherContexts = ((AbstractContextSet) that).backing();
|
||||
} else {
|
||||
otherContexts = that.toMultimap();
|
||||
}
|
||||
|
||||
return backing().equals(otherContexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return backing().hashCode();
|
||||
}
|
||||
|
||||
static String sanitizeKey(String key) {
|
||||
Objects.requireNonNull(key, "key is null");
|
||||
if (stringIsEmpty(key)) {
|
||||
throw new IllegalArgumentException("key is (effectively) empty");
|
||||
}
|
||||
|
||||
return key.toLowerCase().intern();
|
||||
}
|
||||
|
||||
static String sanitizeValue(String value) {
|
||||
Objects.requireNonNull(value, "value is null");
|
||||
if (stringIsEmpty(value)) {
|
||||
throw new IllegalArgumentException("value is (effectively) empty");
|
||||
}
|
||||
|
||||
return value.intern();
|
||||
}
|
||||
|
||||
private static boolean stringIsEmpty(String s) {
|
||||
if (s.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
for (char c : s.toCharArray()) {
|
||||
if (c != ' ') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,9 +40,14 @@ import javax.annotation.Nonnull;
|
||||
* 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}.
|
||||
* Specifically:
|
||||
*
|
||||
* <p></p>
|
||||
* <ul>
|
||||
* <li>{@code org.bukkit.entity.Player}</li>
|
||||
* <li>{@code net.md_5.bungee.api.connection.ProxiedPlayer}</li>
|
||||
* <li>{@code org.spongepowered.api.service.permission.Subject}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
@@ -148,7 +153,6 @@ public interface ContextManager {
|
||||
*
|
||||
* @param calculator the calculator
|
||||
*/
|
||||
@Nonnull
|
||||
void registerCalculator(@Nonnull ContextCalculator<?> calculator);
|
||||
|
||||
/**
|
||||
@@ -158,7 +162,6 @@ public interface ContextManager {
|
||||
*
|
||||
* @param calculator the calculator
|
||||
*/
|
||||
@Nonnull
|
||||
void registerStaticCalculator(@Nonnull StaticContextCalculator calculator);
|
||||
|
||||
/**
|
||||
@@ -166,7 +169,6 @@ public interface ContextManager {
|
||||
*
|
||||
* @param subject the subject
|
||||
*/
|
||||
@Nonnull
|
||||
void invalidateCache(@Nonnull Object subject);
|
||||
|
||||
}
|
||||
@@ -25,36 +25,50 @@
|
||||
|
||||
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.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A set of context pairs.
|
||||
* A set of contexts.
|
||||
*
|
||||
* <p>You can think of ContextSets as a wrapped <code>Multimap<String, String></code>.
|
||||
* Each key can be mapped to multiple values.</p>
|
||||
* <p>Context in the most basic sense simply means the circumstances where
|
||||
* something will apply.</p>
|
||||
*
|
||||
* <p>Keys are automatically converted to lowercase when added, and are therefore
|
||||
* case-insensitive. Values however are not.</p>
|
||||
* <p>A single "context" consists of a key and a value, both strings. The key
|
||||
* represents the type of context, and the value represents the setting of the
|
||||
* context key.</p>
|
||||
*
|
||||
* <p>Implementations may be either mutable or immutable.</p>
|
||||
* <p>Contexts can be combined with each other to form so called
|
||||
* "context sets" - simply a collection of context pairs.</p>
|
||||
*
|
||||
* <p>Context keys are case-insensitive, and will be converted to
|
||||
* {@link String#toLowerCase() lowercase} by all implementations.
|
||||
* Values however are case-sensitive.</p>
|
||||
*
|
||||
* <p>Context keys and values may not be null or empty. A key/value will be
|
||||
* deemed empty if it's length is zero, or if it consists of only space
|
||||
* characters.</p>
|
||||
*
|
||||
* <p>Two default ContextSet implementations are provided.
|
||||
* {@link MutableContextSet} allows the addition and removal of context keys
|
||||
* after construction, and {@link ImmutableContextSet} does not.</p>
|
||||
*
|
||||
* @since 2.13
|
||||
*/
|
||||
public interface ContextSet {
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from a context pair
|
||||
* Creates an {@link ImmutableContextSet} from a context pair.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @return a new ImmutableContextSet containing one KV pair
|
||||
* @return a new ImmutableContextSet containing one context pair
|
||||
* @throws NullPointerException if key or value is null
|
||||
*/
|
||||
@Nonnull
|
||||
@@ -63,7 +77,7 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from two context pairs
|
||||
* Creates an {@link ImmutableContextSet} from two context pairs.
|
||||
*
|
||||
* @param key1 the first key
|
||||
* @param value1 the first value
|
||||
@@ -79,7 +93,7 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from an existing iterable of Map Entries
|
||||
* Creates an {@link ImmutableContextSet} from an existing {@link Iterable} of {@link Map.Entry}s.
|
||||
*
|
||||
* @param iterable the iterable to copy from
|
||||
* @return a new ImmutableContextSet representing the pairs in the iterable
|
||||
@@ -91,7 +105,7 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from an existing map
|
||||
* Creates an {@link ImmutableContextSet} from an existing {@link Map}.
|
||||
*
|
||||
* @param map the map to copy from
|
||||
* @return a new ImmutableContextSet representing the pairs from the map
|
||||
@@ -103,7 +117,7 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from an existing multimap
|
||||
* Creates an {@link ImmutableContextSet} from an existing {@link Multimap}.
|
||||
*
|
||||
* @param multimap the multimap to copy from
|
||||
* @return a new ImmutableContextSet representing the pairs in the multimap
|
||||
@@ -116,8 +130,9 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an new ImmutableContextSet from an existing set.
|
||||
* Only really useful for converting between mutable and immutable types.
|
||||
* Creates an new {@link ImmutableContextSet} from an existing {@link Set}.
|
||||
*
|
||||
* <p>Only really useful for converting between mutable and immutable types.</p>
|
||||
*
|
||||
* @param contextSet the context set to copy from
|
||||
* @return a new ImmutableContextSet with the same content and the one provided
|
||||
@@ -129,9 +144,9 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty ImmutableContextSet.
|
||||
* Returns an empty {@link ImmutableContextSet}.
|
||||
*
|
||||
* @return a new ImmutableContextSet
|
||||
* @return an empty ImmutableContextSet
|
||||
*/
|
||||
@Nonnull
|
||||
static ImmutableContextSet empty() {
|
||||
@@ -139,22 +154,30 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this set is in an immutable form
|
||||
* Gets if this {@link ContextSet} is immutable.
|
||||
*
|
||||
* <p>The state of immutable instances will never change.</p>
|
||||
*
|
||||
* @return true if the set is immutable
|
||||
*/
|
||||
boolean isImmutable();
|
||||
|
||||
/**
|
||||
* If the set is mutable, this method will return an immutable copy. Otherwise just returns itself.
|
||||
* Returns an immutable representation of this {@link ContextSet}.
|
||||
*
|
||||
* @return an immutable ContextSet
|
||||
* <p>If the set is already immutable, the same object will be returned.
|
||||
* If the set is mutable, an immutable copy will be made.</p>
|
||||
*
|
||||
* @return an immutable representation of this set
|
||||
*/
|
||||
@Nonnull
|
||||
ImmutableContextSet makeImmutable();
|
||||
|
||||
/**
|
||||
* Creates a mutable copy of this set.
|
||||
* Creates a mutable copy of this {@link ContextSet}.
|
||||
*
|
||||
* <p>A new copy is returned regardless of the
|
||||
* {@link #isImmutable() mutability} of this set.</p>
|
||||
*
|
||||
* @return a mutable ContextSet
|
||||
* @since 2.16
|
||||
@@ -163,7 +186,11 @@ public interface ContextSet {
|
||||
MutableContextSet mutableCopy();
|
||||
|
||||
/**
|
||||
* Converts this ContextSet to an immutable {@link Set} of {@link Map.Entry}s.
|
||||
* Returns a {@link Set} of {@link Map.Entry}s representing the current
|
||||
* state of this {@link ContextSet}.
|
||||
*
|
||||
* <p>The returned set is immutable, and is a copy of the current set.
|
||||
* (will not update live)</p>
|
||||
*
|
||||
* @return an immutable set
|
||||
*/
|
||||
@@ -171,10 +198,18 @@ public interface ContextSet {
|
||||
Set<Map.Entry<String, String>> toSet();
|
||||
|
||||
/**
|
||||
* Converts this ContextSet to an immutable {@link Map}
|
||||
* Returns a {@link Map} <b>loosely</b> representing the current state of
|
||||
* this {@link ContextSet}.
|
||||
*
|
||||
* <b>IMPORTANT: Use of this method may result in data being lost. ContextSets can contain lots of different values for
|
||||
* one key.</b>
|
||||
* <p>The returned map is immutable, and is a copy of the current set.
|
||||
* (will not update live)</p>
|
||||
*
|
||||
* <p>As a single context key can be mapped to multiple values, this method
|
||||
* may not be a true representation of the set.</p>
|
||||
*
|
||||
* <p>If you need a representation of the set in a Java collection instance,
|
||||
* use {@link #toSet()} or {@link #toMultimap()} followed by
|
||||
* {@link Multimap#asMap()}.</p>
|
||||
*
|
||||
* @return an immutable map
|
||||
* @deprecated because the resultant map may not contain all data in the ContextSet
|
||||
@@ -184,7 +219,11 @@ public interface ContextSet {
|
||||
Map<String, String> toMap();
|
||||
|
||||
/**
|
||||
* Converts this ContextSet to an immutable {@link Multimap}
|
||||
* Returns a {@link Multimap} representing the current state of this
|
||||
* {@link ContextSet}.
|
||||
*
|
||||
* <p>The returned multimap is immutable, and is a copy of the current set.
|
||||
* (will not update live)</p>
|
||||
*
|
||||
* @return a multimap
|
||||
* @since 2.16
|
||||
@@ -193,7 +232,8 @@ public interface ContextSet {
|
||||
Multimap<String, String> toMultimap();
|
||||
|
||||
/**
|
||||
* Check if the set contains at least one value for the given key.
|
||||
* Returns if the {@link ContextSet} contains at least one value for the
|
||||
* given key.
|
||||
*
|
||||
* @param key the key to check for
|
||||
* @return true if the set contains a value for the key
|
||||
@@ -202,9 +242,12 @@ public interface ContextSet {
|
||||
boolean containsKey(@Nonnull String key);
|
||||
|
||||
/**
|
||||
* Gets a set of all of the values mapped to the given key
|
||||
* Returns a {@link Set} of the values mapped to the given key.
|
||||
*
|
||||
* @param key the key to find values for
|
||||
* <p>The returned set is immutable, and only represents the current state
|
||||
* of the {@link ContextSet}. (will not update live)</p>
|
||||
*
|
||||
* @param key the key to get values for
|
||||
* @return a set of values
|
||||
* @throws NullPointerException if the key is null
|
||||
*/
|
||||
@@ -212,7 +255,10 @@ public interface ContextSet {
|
||||
Set<String> getValues(@Nonnull String key);
|
||||
|
||||
/**
|
||||
* Returns any value from this set matching the key, if present.
|
||||
* Returns any value from this {@link ContextSet} matching the key, if present.
|
||||
*
|
||||
* <p>Note that context keys can be mapped to multiple values.
|
||||
* Use {@link #getValues(String)} to retrieve all associated values.</p>
|
||||
*
|
||||
* @param key the key to find values for
|
||||
* @return an optional containing any match
|
||||
@@ -224,53 +270,69 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if thr set contains a given key mapped to a given value
|
||||
* Returns if the {@link ContextSet} contains a given context pairing.
|
||||
*
|
||||
* <p>This lookup is case-sensitive on the value.</p>
|
||||
*
|
||||
* @param key the key to look for
|
||||
* @param value the value to look for (case sensitive)
|
||||
* @return true if the set contains the KV pair
|
||||
* @return true if the set contains the context pair
|
||||
* @throws NullPointerException if the key or value is null
|
||||
*/
|
||||
boolean has(@Nonnull String key, @Nonnull String value);
|
||||
|
||||
/**
|
||||
* Check if thr set contains a given key mapped to a given value
|
||||
* Returns if the {@link ContextSet} contains a given context pairing,
|
||||
* ignoring the case of values.
|
||||
*
|
||||
* @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.
|
||||
* <p>This lookup is case-insensitive on the value.</p>
|
||||
*
|
||||
* @param key the key to look for
|
||||
* @param value the value to look for
|
||||
* @return true if the set contains the KV pair
|
||||
* @return true if the set contains the context pair
|
||||
* @throws NullPointerException if the key or value is null
|
||||
*/
|
||||
boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value);
|
||||
|
||||
/**
|
||||
* Same as {@link #has(Map.Entry)}, except ignores the case of the value.
|
||||
* Returns if the {@link ContextSet} contains a given context pairing.
|
||||
*
|
||||
* <p>This lookup is case-sensitive on the value.</p>
|
||||
*
|
||||
* @param entry the entry to look for
|
||||
* @return true if the set contains the KV pair
|
||||
* @return true if the set contains the context pair
|
||||
* @throws NullPointerException if the key or value is null
|
||||
*/
|
||||
default boolean has(@Nonnull Map.Entry<String, String> entry) {
|
||||
Objects.requireNonNull(entry, "entry");
|
||||
return has(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the {@link ContextSet} contains a given context pairing,
|
||||
* ignoring the case of values.
|
||||
*
|
||||
* <p>This lookup is case-insensitive on the value.</p>
|
||||
*
|
||||
* @param entry the entry to look for
|
||||
* @return true if the set contains the context 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");
|
||||
Objects.requireNonNull(entry, "entry");
|
||||
return hasIgnoreCase(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if all entries in this context set are also included in another set.
|
||||
* Returns if this {@link ContextSet} is fully "satisfied" by another set.
|
||||
*
|
||||
* <p>For a context set to "satisfy" another, it must itself contain all of
|
||||
* the context pairings in the other set.</p>
|
||||
*
|
||||
* <p>Mathematically, this method returns true if this set is a <b>subset</b> of the other.</p>
|
||||
*
|
||||
* <p>This check is case-sensitive. For a case-insensitive check,
|
||||
* use {@link #isSatisfiedBy(ContextSet, boolean)}.</p>
|
||||
*
|
||||
* @param other the other set to check
|
||||
* @return true if all entries in this set are also in the other set
|
||||
@@ -281,15 +343,24 @@ public interface ContextSet {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if all entries in this context set are also included in another set.
|
||||
* Returns if this {@link ContextSet} is fully "satisfied" by another set.
|
||||
*
|
||||
* <p>For a context set to "satisfy" another, it must itself contain all of
|
||||
* the context pairings in the other set.</p>
|
||||
*
|
||||
* <p>Mathematically, this method returns true if this set is a <b>subset</b> of the other.</p>
|
||||
*
|
||||
* @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)}.
|
||||
* @param caseSensitive if the check should be case sensitive
|
||||
* @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 == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Objects.requireNonNull(other, "other");
|
||||
if (this.isEmpty()) {
|
||||
// this is empty, so is therefore always satisfied.
|
||||
return true;
|
||||
@@ -301,31 +372,30 @@ public interface ContextSet {
|
||||
return false;
|
||||
} else {
|
||||
// neither are empty, we need to compare the individual entries
|
||||
for (Map.Entry<String, String> pair : toSet()) {
|
||||
for (Map.Entry<String, String> context : toSet()) {
|
||||
if (caseSensitive) {
|
||||
if (!other.has(pair)) {
|
||||
if (!other.has(context)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!other.hasIgnoreCase(pair)) {
|
||||
if (!other.hasIgnoreCase(context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the set is empty
|
||||
* Returns if the {@link ContextSet} is empty.
|
||||
*
|
||||
* @return true if the set is empty
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Gets the number of key-value context pairs in the set
|
||||
* Gets the number of context pairs in the {@link ContextSet}.
|
||||
*
|
||||
* @return the size of the set
|
||||
*/
|
||||
|
||||
@@ -25,48 +25,51 @@
|
||||
|
||||
package me.lucko.luckperms.api.context;
|
||||
|
||||
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 java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
@Immutable
|
||||
public final class ImmutableContextSet extends AbstractContextSet implements ContextSet {
|
||||
private static final ImmutableContextSet EMPTY = new ImmutableContextSet(ImmutableSetMultimap.of());
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from a context pair
|
||||
* Creates an {@link ImmutableContextSet.Builder}.
|
||||
*
|
||||
* @return a new ImmutableContextSet builder
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link ImmutableContextSet} from a context pair.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @return a new ImmutableContextSet containing one KV pair
|
||||
* @return a new ImmutableContextSet containing one context pair
|
||||
* @throws NullPointerException if key or value is null
|
||||
*/
|
||||
@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()
|
||||
));
|
||||
return new ImmutableContextSet(ImmutableSetMultimap.of(sanitizeKey(key), sanitizeValue(value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from two context pairs
|
||||
* Creates an {@link ImmutableContextSet} from two context pairs.
|
||||
*
|
||||
* @param key1 the first key
|
||||
* @param value1 the first value
|
||||
@@ -79,15 +82,15 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
@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()
|
||||
sanitizeKey(key1),
|
||||
sanitizeValue(value1),
|
||||
sanitizeKey(key2),
|
||||
sanitizeValue(value2)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from an existing iterable of Map Entries
|
||||
* Creates an {@link ImmutableContextSet} from an existing {@link Iterable} of {@link Map.Entry}s.
|
||||
*
|
||||
* @param iterable the iterable to copy from
|
||||
* @return a new ImmutableContextSet representing the pairs in the iterable
|
||||
@@ -95,18 +98,16 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
*/
|
||||
@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());
|
||||
Objects.requireNonNull(iterable, "iterable");
|
||||
ImmutableContextSet.Builder builder = builder();
|
||||
for (Map.Entry<String, String> entry : iterable) {
|
||||
builder.add(entry);
|
||||
}
|
||||
|
||||
return new ImmutableContextSet(b.build());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from an existing map
|
||||
* Creates an {@link ImmutableContextSet} from an existing {@link Map}.
|
||||
*
|
||||
* @param map the map to copy from
|
||||
* @return a new ImmutableContextSet representing the pairs from the map
|
||||
@@ -114,24 +115,26 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
*/
|
||||
@Nonnull
|
||||
public static ImmutableContextSet fromMap(@Nonnull Map<String, String> map) {
|
||||
return fromEntries(checkNotNull(map, "map").entrySet());
|
||||
return fromEntries(Objects.requireNonNull(map, "map").entrySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ImmutableContextSet from an existing multimap
|
||||
* Creates an {@link ImmutableContextSet} from an existing {@link 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
|
||||
*/
|
||||
@Nonnull
|
||||
public static ImmutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) {
|
||||
return fromEntries(checkNotNull(multimap, "multimap").entries());
|
||||
return fromEntries(Objects.requireNonNull(multimap, "multimap").entries());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ImmutableContextSet from an existing set.
|
||||
* Only really useful for converting between mutable and immutable types.
|
||||
* Creates an new {@link ImmutableContextSet} from an existing {@link Set}.
|
||||
*
|
||||
* <p>Only really useful for converting between mutable and immutable types.</p>
|
||||
*
|
||||
* @param contextSet the context set to copy from
|
||||
* @return a new ImmutableContextSet with the same content and the one provided
|
||||
@@ -139,13 +142,13 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
*/
|
||||
@Nonnull
|
||||
public static ImmutableContextSet fromSet(@Nonnull ContextSet contextSet) {
|
||||
return checkNotNull(contextSet, "contextSet").makeImmutable();
|
||||
return Objects.requireNonNull(contextSet, "contextSet").makeImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an new empty ContextSet.
|
||||
* Returns an empty {@link ImmutableContextSet}.
|
||||
*
|
||||
* @return a new ContextSet
|
||||
* @return an empty ImmutableContextSet
|
||||
*/
|
||||
@Nonnull
|
||||
public static ImmutableContextSet empty() {
|
||||
@@ -153,9 +156,16 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
}
|
||||
|
||||
private final ImmutableSetMultimap<String, String> map;
|
||||
private final int hashCode;
|
||||
|
||||
ImmutableContextSet(ImmutableSetMultimap<String, String> contexts) {
|
||||
this.map = contexts;
|
||||
this.hashCode = this.map.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Multimap<String, String> backing() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,112 +173,174 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@Deprecated // This set is already immutable!
|
||||
@Nonnull
|
||||
public ImmutableContextSet makeImmutable() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public MutableContextSet mutableCopy() {
|
||||
return MutableContextSet.fromSet(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Map.Entry<String, String>> toSet() {
|
||||
return map.entries();
|
||||
return this.map.entries();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Map<String, String> toMap() {
|
||||
ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
|
||||
for (Map.Entry<String, String> e : map.entries()) {
|
||||
m.put(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
return m.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Multimap<String, String> toMultimap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public boolean containsKey(@Nonnull String key) {
|
||||
return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
|
||||
}
|
||||
|
||||
@Override
|
||||
@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
|
||||
@Nonnull
|
||||
public boolean has(@Nonnull String key, @Nonnull String value) {
|
||||
return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
|
||||
}
|
||||
|
||||
@Override
|
||||
@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());
|
||||
|
||||
if (values == null || values.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String val : values) {
|
||||
if (val.equalsIgnoreCase(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof ContextSet)) return false;
|
||||
final ContextSet other = (ContextSet) o;
|
||||
|
||||
// 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.equals(otherContexts);
|
||||
return this.map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return map.hashCode();
|
||||
return this.hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImmutableContextSet(contexts=" + this.map + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for {@link ImmutableContextSet}.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public static final class Builder {
|
||||
private ImmutableSetMultimap.Builder<String, String> builder;
|
||||
|
||||
private Builder() {
|
||||
|
||||
}
|
||||
|
||||
private synchronized ImmutableSetMultimap.Builder<String, String> builder() {
|
||||
if (this.builder == null) {
|
||||
this.builder = ImmutableSetMultimap.builder();
|
||||
}
|
||||
return this.builder;
|
||||
}
|
||||
|
||||
private void put(String key, String value) {
|
||||
builder().put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a context to the set.
|
||||
*
|
||||
* @param key the key to add
|
||||
* @param value the value to add
|
||||
* @return the builder
|
||||
* @throws NullPointerException if the key or value is null
|
||||
* @see MutableContextSet#add(String, String)
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder add(@Nonnull String key, @Nonnull String value) {
|
||||
put(sanitizeKey(key), sanitizeValue(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a context to the set.
|
||||
*
|
||||
* @param entry the entry to add
|
||||
* @return the builder
|
||||
* @throws NullPointerException if the entry is null
|
||||
* @see MutableContextSet#add(Map.Entry)
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder add(@Nonnull Map.Entry<String, String> entry) {
|
||||
Objects.requireNonNull(entry, "entry");
|
||||
add(entry.getKey(), entry.getValue());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the contexts contained in the given {@link Iterable} to the set.
|
||||
*
|
||||
* @param iterable an iterable of key value context pairs
|
||||
* @return the builder
|
||||
* @throws NullPointerException if iterable is null
|
||||
* @see MutableContextSet#addAll(Iterable)
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder addAll(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
|
||||
for (Map.Entry<String, String> e : Objects.requireNonNull(iterable, "iterable")) {
|
||||
add(e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the contexts contained in the given {@link Map} to the set.
|
||||
*
|
||||
* @param map the map to add from
|
||||
* @return the builder
|
||||
* @throws NullPointerException if the map is null
|
||||
* @see MutableContextSet#addAll(Map)
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder addAll(@Nonnull Map<String, String> map) {
|
||||
addAll(Objects.requireNonNull(map, "map").entrySet());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the contexts contained in the given {@link Multimap} to the set.
|
||||
*
|
||||
* @param multimap the multimap to add from
|
||||
* @return the builder
|
||||
* @throws NullPointerException if the map is null
|
||||
* @since 3.4
|
||||
* @see MutableContextSet#addAll(Multimap)
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder addAll(@Nonnull Multimap<String, String> multimap) {
|
||||
addAll(Objects.requireNonNull(multimap, "multimap").entries());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds of of the contexts in another {@link ContextSet} to the set.
|
||||
*
|
||||
* @param contextSet the set to add from
|
||||
* @return the builder
|
||||
* @throws NullPointerException if the contextSet is null
|
||||
* @see MutableContextSet#addAll(ContextSet)
|
||||
*/
|
||||
@Nonnull
|
||||
public Builder addAll(@Nonnull ContextSet contextSet) {
|
||||
Objects.requireNonNull(contextSet, "contextSet");
|
||||
if (contextSet instanceof AbstractContextSet) {
|
||||
AbstractContextSet other = ((AbstractContextSet) contextSet);
|
||||
if (!other.isEmpty()) {
|
||||
builder().putAll(other.backing());
|
||||
}
|
||||
} else {
|
||||
addAll(contextSet.toMultimap());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ImmutableContextSet} from the values previously
|
||||
* added to the builder.
|
||||
*
|
||||
* @return an {@link ImmutableContextSet} from the builder
|
||||
*/
|
||||
@Nonnull
|
||||
public ImmutableContextSet build() {
|
||||
if (this.builder == null) {
|
||||
return empty();
|
||||
} else {
|
||||
return new ImmutableContextSet(this.builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,7 @@
|
||||
|
||||
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;
|
||||
@@ -36,41 +34,37 @@ import com.google.common.collect.SetMultimap;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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 {
|
||||
public final class MutableContextSet extends AbstractContextSet implements ContextSet {
|
||||
|
||||
/**
|
||||
* Make a singleton MutableContextSet from a context pair
|
||||
* Creates a {@link MutableContextSet} from a context pair.
|
||||
*
|
||||
* @param key the key
|
||||
* @param value the value
|
||||
* @return a new MutableContextSet containing one KV pair
|
||||
* @return a new MutableContextSet containing one context pair
|
||||
* @throws NullPointerException if key or value is null
|
||||
*/
|
||||
@Nonnull
|
||||
public static MutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
|
||||
checkNotNull(key, "key");
|
||||
checkNotNull(value, "value");
|
||||
Objects.requireNonNull(key, "key");
|
||||
Objects.requireNonNull(value, "value");
|
||||
MutableContextSet set = MutableContextSet.create();
|
||||
set.add(key, value);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a MutableContextSet from two context pairs
|
||||
* Creates a {@link MutableContextSet} from two context pairs.
|
||||
*
|
||||
* @param key1 the first key
|
||||
* @param value1 the first value
|
||||
@@ -82,18 +76,18 @@ public final class MutableContextSet implements ContextSet {
|
||||
*/
|
||||
@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();
|
||||
Objects.requireNonNull(key1, "key1");
|
||||
Objects.requireNonNull(value1, "value1");
|
||||
Objects.requireNonNull(key2, "key2");
|
||||
Objects.requireNonNull(value2, "value2");
|
||||
MutableContextSet set = create();
|
||||
set.add(key1, value1);
|
||||
set.add(key2, value2);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MutableContextSet from an existing iterable of Map Entries
|
||||
* Creates a {@link MutableContextSet} from an existing {@link Iterable} of {@link Map.Entry}s.
|
||||
*
|
||||
* @param iterable the iterable to copy from
|
||||
* @return a new MutableContextSet representing the pairs in the iterable
|
||||
@@ -101,14 +95,14 @@ public final class MutableContextSet implements ContextSet {
|
||||
*/
|
||||
@Nonnull
|
||||
public static MutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
|
||||
checkNotNull(iterable, "iterable");
|
||||
MutableContextSet set = MutableContextSet.create();
|
||||
Objects.requireNonNull(iterable, "iterable");
|
||||
MutableContextSet set = create();
|
||||
set.addAll(iterable);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MutableContextSet from an existing map
|
||||
* Creates a {@link MutableContextSet} from an existing {@link Map}.
|
||||
*
|
||||
* @param map the map to copy from
|
||||
* @return a new MutableContextSet representing the pairs from the map
|
||||
@@ -116,30 +110,32 @@ public final class MutableContextSet implements ContextSet {
|
||||
*/
|
||||
@Nonnull
|
||||
public static MutableContextSet fromMap(@Nonnull Map<String, String> map) {
|
||||
checkNotNull(map, "map");
|
||||
MutableContextSet set = MutableContextSet.create();
|
||||
Objects.requireNonNull(map, "map");
|
||||
MutableContextSet set = create();
|
||||
set.addAll(map);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a MutableContextSet from an existing multimap
|
||||
* Creates a {@link MutableContextSet} from an existing {@link Multimap}.
|
||||
*
|
||||
* @param multimap the multimap to copy from
|
||||
* @return a new MutableContextSet representing the pairs in the multimap
|
||||
* @throws NullPointerException if the multimap is null
|
||||
* @since 2.16
|
||||
*/
|
||||
@Nonnull
|
||||
public static MutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) {
|
||||
checkNotNull(multimap, "multimap");
|
||||
MutableContextSet set = MutableContextSet.create();
|
||||
Objects.requireNonNull(multimap, "multimap");
|
||||
MutableContextSet set = create();
|
||||
set.addAll(multimap);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MutableContextSet from an existing set.
|
||||
* Only really useful for converting between mutable and immutable types.
|
||||
* Creates a new {@link MutableContextSet} from an existing {@link Set}.
|
||||
*
|
||||
* <p>Only really useful for converting between mutable and immutable types.</p>
|
||||
*
|
||||
* @param contextSet the context set to copy from
|
||||
* @return a new MutableContextSet with the same content and the one provided
|
||||
@@ -147,9 +143,9 @@ public final class MutableContextSet implements ContextSet {
|
||||
*/
|
||||
@Nonnull
|
||||
public static MutableContextSet fromSet(@Nonnull ContextSet contextSet) {
|
||||
Preconditions.checkNotNull(contextSet, "contextSet");
|
||||
MutableContextSet set = new MutableContextSet();
|
||||
set.addAll(contextSet.toSet());
|
||||
Objects.requireNonNull(contextSet, "contextSet");
|
||||
MutableContextSet set = create();
|
||||
set.addAll(contextSet);
|
||||
return set;
|
||||
}
|
||||
|
||||
@@ -173,6 +169,11 @@ public final class MutableContextSet implements ContextSet {
|
||||
this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other.map));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Multimap<String, String> backing() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmutable() {
|
||||
return false;
|
||||
@@ -181,7 +182,11 @@ public final class MutableContextSet implements ContextSet {
|
||||
@Nonnull
|
||||
@Override
|
||||
public ImmutableContextSet makeImmutable() {
|
||||
return new ImmutableContextSet(ImmutableSetMultimap.copyOf(map));
|
||||
// if the map is empty, don't create a new instance
|
||||
if (this.map.isEmpty()) {
|
||||
return ImmutableContextSet.empty();
|
||||
}
|
||||
return new ImmutableContextSet(ImmutableSetMultimap.copyOf(this.map));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@@ -193,211 +198,127 @@ public final class MutableContextSet implements ContextSet {
|
||||
@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();
|
||||
for (Map.Entry<String, String> e : map.entries()) {
|
||||
m.put(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
return m.build();
|
||||
return ImmutableSet.copyOf(this.map.entries());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Multimap<String, String> toMultimap() {
|
||||
return ImmutableSetMultimap.copyOf(map);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public boolean containsKey(@Nonnull String key) {
|
||||
return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
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(@Nonnull String key, @Nonnull String value) {
|
||||
return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
|
||||
value = checkNotNull(value, "value").intern();
|
||||
Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
|
||||
|
||||
if (values == null || values.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String val : values) {
|
||||
if (val.equalsIgnoreCase(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
return ImmutableSetMultimap.copyOf(this.map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new key value pair to the set
|
||||
* Adds a context to this set.
|
||||
*
|
||||
* @param key the key to add
|
||||
* @param value the value to add
|
||||
* @throws NullPointerException if the key or value is null
|
||||
*/
|
||||
public void add(@Nonnull String key, @Nonnull String value) {
|
||||
map.put(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
|
||||
this.map.put(sanitizeKey(key), sanitizeValue(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new key value pair to the set
|
||||
* Adds a context to this set.
|
||||
*
|
||||
* @param entry the entry to add
|
||||
* @throws NullPointerException if the entry is null
|
||||
*/
|
||||
public void add(@Nonnull Map.Entry<String, String> entry) {
|
||||
checkNotNull(entry, "entry");
|
||||
Objects.requireNonNull(entry, "entry");
|
||||
add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an iterable containing contexts to the set
|
||||
* Adds the contexts contained in the given {@link Iterable} to this set.
|
||||
*
|
||||
* @param iterable an iterable of key value context pairs
|
||||
* @throws NullPointerException if iterable is null
|
||||
*/
|
||||
public void addAll(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
|
||||
for (Map.Entry<String, String> e : checkNotNull(iterable, "iterable")) {
|
||||
for (Map.Entry<String, String> e : Objects.requireNonNull(iterable, "iterable")) {
|
||||
add(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the entry set of a map to the set
|
||||
* Adds the contexts contained in the given {@link Map} to this set.
|
||||
*
|
||||
* @param map the map to add from
|
||||
* @throws NullPointerException if the map is null
|
||||
*/
|
||||
public void addAll(@Nonnull Map<String, String> map) {
|
||||
addAll(checkNotNull(map, "map").entrySet());
|
||||
addAll(Objects.requireNonNull(map, "map").entrySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the entries of a multimap to the set
|
||||
* Adds the contexts contained in the given {@link Multimap} to this 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());
|
||||
addAll(Objects.requireNonNull(multimap, "multimap").entries());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds of of the values in another ContextSet to this set
|
||||
* Adds of of the contexts in another {@link ContextSet} to this set.
|
||||
*
|
||||
* @param contextSet the set to add from
|
||||
* @throws NullPointerException if the contextSet is null
|
||||
*/
|
||||
public void addAll(@Nonnull ContextSet contextSet) {
|
||||
checkNotNull(contextSet, "contextSet");
|
||||
if (contextSet instanceof MutableContextSet) {
|
||||
MutableContextSet other = ((MutableContextSet) contextSet);
|
||||
this.map.putAll(other.map);
|
||||
Objects.requireNonNull(contextSet, "contextSet");
|
||||
if (contextSet instanceof AbstractContextSet) {
|
||||
AbstractContextSet other = ((AbstractContextSet) contextSet);
|
||||
this.map.putAll(other.backing());
|
||||
} else {
|
||||
addAll(contextSet.toMultimap());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a key value pair from this set
|
||||
* Removes a context from this set.
|
||||
*
|
||||
* @param key the key to remove
|
||||
* @param value the value to remove (case sensitive)
|
||||
* @throws NullPointerException if the key or value is null
|
||||
*/
|
||||
public void remove(@Nonnull String key, @Nonnull String value) {
|
||||
String k = checkNotNull(key, "key").toLowerCase().intern();
|
||||
String v = checkNotNull(value, "value").intern();
|
||||
|
||||
//noinspection StringEquality
|
||||
map.entries().removeIf(entry -> entry.getKey() == k && entry.getValue() == v);
|
||||
this.map.remove(sanitizeKey(key), sanitizeValue(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #remove(String, String)}, except ignores the case of the value
|
||||
* Removes a context from this set. (case-insensitive)
|
||||
*
|
||||
* @param key the key to remove
|
||||
* @param value the value to remove
|
||||
* @throws NullPointerException if the key or value is null
|
||||
*/
|
||||
public void removeIgnoreCase(@Nonnull String key, @Nonnull String value) {
|
||||
String k = checkNotNull(key, "key").toLowerCase().intern();
|
||||
String v = checkNotNull(value, "value").intern();
|
||||
|
||||
//noinspection StringEquality
|
||||
map.entries().removeIf(e -> e.getKey() == k && e.getValue().equalsIgnoreCase(v));
|
||||
String v = sanitizeValue(value);
|
||||
Collection<String> strings = this.map.asMap().get(sanitizeKey(key));
|
||||
if (strings != null) {
|
||||
strings.removeIf(e -> e.equalsIgnoreCase(v));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all pairs with the given key
|
||||
* Removes all contexts from this set with the given key.
|
||||
*
|
||||
* @param key the key to remove
|
||||
* @throws NullPointerException if the key is null
|
||||
*/
|
||||
public void removeAll(@Nonnull String key) {
|
||||
map.removeAll(checkNotNull(key, "key").toLowerCase());
|
||||
this.map.removeAll(sanitizeKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the set
|
||||
* Removes all contexts from the set.
|
||||
*/
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof ContextSet)) return false;
|
||||
final ContextSet other = (ContextSet) o;
|
||||
|
||||
final Multimap<String, String> otherContexts;
|
||||
|
||||
if (other instanceof MutableContextSet) {
|
||||
otherContexts = ((MutableContextSet) other).map;
|
||||
} else {
|
||||
otherContexts = other.toMultimap();
|
||||
}
|
||||
|
||||
return this.map.equals(otherContexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return map.hashCode();
|
||||
this.map.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,4 +42,35 @@ public interface Cancellable {
|
||||
@Nonnull
|
||||
AtomicBoolean getCancellationState();
|
||||
|
||||
/**
|
||||
* Returns true if the event is currently cancelled.
|
||||
*
|
||||
* @return if the event is cancelled
|
||||
* @since 4.1
|
||||
*/
|
||||
default boolean isCancelled() {
|
||||
return getCancellationState().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the event is not currently cancelled.
|
||||
*
|
||||
* @return if the event is not cancelled
|
||||
* @since 4.1
|
||||
*/
|
||||
default boolean isNotCancelled() {
|
||||
return !isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cancellation state of the event.
|
||||
*
|
||||
* @param cancelled the new state
|
||||
* @return the previous state
|
||||
* @since 4.1
|
||||
*/
|
||||
default boolean setCancelled(boolean cancelled) {
|
||||
return getCancellationState().getAndSet(cancelled);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,14 +31,16 @@ import java.util.function.Consumer;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* The LuckPerms event bus. Used for subscribing (or registering listeners) to events.
|
||||
* The internal LuckPerms event bus.
|
||||
*
|
||||
* <p>LuckPerms events are posted to any listeners registered with the bus.</p>
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface EventBus {
|
||||
|
||||
/**
|
||||
* Subscribe to an event
|
||||
* Subscribes to an event.
|
||||
*
|
||||
* @param eventClass the event class
|
||||
* @param handler the event handler
|
||||
@@ -49,7 +51,7 @@ public interface EventBus {
|
||||
<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
|
||||
* Gets a set of all registered handlers for a given event.
|
||||
*
|
||||
* @param eventClass the event to find handlers for
|
||||
* @param <T> the event class
|
||||
|
||||
@@ -34,7 +34,7 @@ import javax.annotation.Nonnull;
|
||||
*
|
||||
* @param <T> the event class
|
||||
*/
|
||||
public interface EventHandler<T extends LuckPermsEvent> {
|
||||
public interface EventHandler<T extends LuckPermsEvent> extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Gets the class this handler is listening to
|
||||
@@ -73,4 +73,8 @@ public interface EventHandler<T extends LuckPermsEvent> {
|
||||
*/
|
||||
int getCallCount();
|
||||
|
||||
@Override
|
||||
default void close() {
|
||||
unregister();
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import me.lucko.luckperms.api.LuckPermsApi;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* The base event interface
|
||||
* A superinterface for all LuckPerms events.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import me.lucko.luckperms.api.event.source.Source;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents an event with a {@link Source}.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface Sourced {
|
||||
|
||||
/**
|
||||
* Gets the events source.
|
||||
*
|
||||
* <p>Never returns null. In situations where the source is unknown, a
|
||||
* {@link Source} with type {@link Source.Type#UNKNOWN} is returned.</p>
|
||||
*
|
||||
* @return the source
|
||||
*/
|
||||
@Nonnull
|
||||
Source getSource();
|
||||
|
||||
}
|
||||
+25
-18
@@ -23,28 +23,35 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.comparisons.impl;
|
||||
package me.lucko.luckperms.api.event.group;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.Comparison;
|
||||
import me.lucko.luckperms.api.Group;
|
||||
import me.lucko.luckperms.api.caching.GroupData;
|
||||
import me.lucko.luckperms.api.event.LuckPermsEvent;
|
||||
|
||||
public class ComparisonNotSimilar implements Comparison {
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@Override
|
||||
public String getSymbol() {
|
||||
return "!~";
|
||||
}
|
||||
/**
|
||||
* Called when a groups {@link GroupData} is loaded.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface GroupCacheLoadEvent extends LuckPermsEvent {
|
||||
|
||||
@Override
|
||||
public boolean matches(String str, String expr) {
|
||||
// form expression
|
||||
expr = expr.toLowerCase();
|
||||
expr = expr.replace(".", "\\.");
|
||||
/**
|
||||
* Gets the group whose data was loaded
|
||||
*
|
||||
* @return the group
|
||||
*/
|
||||
@Nonnull
|
||||
Group getGroup();
|
||||
|
||||
// convert from SQL LIKE syntax to regex
|
||||
expr = expr.replace("_", ".");
|
||||
expr = expr.replace("%", ".*");
|
||||
|
||||
return !str.toLowerCase().matches(expr);
|
||||
}
|
||||
/**
|
||||
* Gets the data that was loaded
|
||||
*
|
||||
* @return the loaded data
|
||||
*/
|
||||
@Nonnull
|
||||
GroupData getLoadedData();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.group;
|
||||
|
||||
import me.lucko.luckperms.api.Group;
|
||||
import me.lucko.luckperms.api.caching.GroupData;
|
||||
import me.lucko.luckperms.api.event.LuckPermsEvent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Called when a groups cached data is refreshed
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface GroupDataRecalculateEvent extends LuckPermsEvent {
|
||||
|
||||
/**
|
||||
* Gets the group whose data was recalculated
|
||||
*
|
||||
* @return the group
|
||||
*/
|
||||
@Nonnull
|
||||
Group getGroup();
|
||||
|
||||
/**
|
||||
* Gets the data that was recalculated
|
||||
*
|
||||
* @return the data
|
||||
*/
|
||||
@Nonnull
|
||||
GroupData getData();
|
||||
|
||||
}
|
||||
@@ -61,12 +61,17 @@ public interface LogBroadcastEvent extends LuckPermsEvent, Cancellable {
|
||||
enum Origin {
|
||||
|
||||
/**
|
||||
* Represents a log entry which originated from the current server instance
|
||||
* Marks 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
|
||||
* Marks a log entry which originated from an API call on the current server instance
|
||||
*/
|
||||
LOCAL_API,
|
||||
|
||||
/**
|
||||
* Marks a log entry which was sent to this server via the messaging service
|
||||
*/
|
||||
REMOTE
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.Entity;
|
||||
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 specific notifiable object on
|
||||
* the platform.
|
||||
*
|
||||
* <p>This event is not called for players without the notify permission,
|
||||
* but is called for objects which are ignoring log notifications (called with
|
||||
* the cancelled flag set to true).</p>
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface LogNotifyEvent extends LuckPermsEvent, Cancellable {
|
||||
|
||||
/**
|
||||
* Gets the log entry to be sent
|
||||
*
|
||||
* @return the log entry to be sent
|
||||
*/
|
||||
@Nonnull
|
||||
LogEntry getEntry();
|
||||
|
||||
/**
|
||||
* Gets where the log entry originated from.
|
||||
*
|
||||
* @return the origin of the log
|
||||
*/
|
||||
@Nonnull
|
||||
Origin getOrigin();
|
||||
|
||||
/**
|
||||
* Gets the object to be notified.
|
||||
*
|
||||
* @return the object to notify
|
||||
*/
|
||||
@Nonnull
|
||||
Entity getNotifiable();
|
||||
|
||||
/**
|
||||
* Represents where a log entry is from
|
||||
*/
|
||||
enum Origin {
|
||||
|
||||
/**
|
||||
* Marks a log entry which originated from the current server instance
|
||||
*/
|
||||
LOCAL,
|
||||
|
||||
/**
|
||||
* Marks a log entry which originated from an API call on the current server instance
|
||||
*/
|
||||
LOCAL_API,
|
||||
|
||||
/**
|
||||
* Marks a log entry which was sent to this server via the messaging service
|
||||
*/
|
||||
REMOTE
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.source;
|
||||
|
||||
import me.lucko.luckperms.api.Entity;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents an {@link Entity} which was the {@link Source} of something.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface EntitySource extends Source {
|
||||
|
||||
/**
|
||||
* Gets the entity.
|
||||
*
|
||||
* @return the entity
|
||||
*/
|
||||
@Nonnull
|
||||
Entity getEntity();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.source;
|
||||
|
||||
import me.lucko.luckperms.api.Entity;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents the source of an event.
|
||||
*
|
||||
* <p>Could also be described as the "thing" that caused an event to occur.</p>
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface Source {
|
||||
|
||||
/**
|
||||
* Gets the source type
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
@Nonnull
|
||||
Type getType();
|
||||
|
||||
/**
|
||||
* Represents a type of source
|
||||
*/
|
||||
enum Type {
|
||||
|
||||
/**
|
||||
* Represents an {@link Entity} source
|
||||
*
|
||||
* @see EntitySource
|
||||
*/
|
||||
ENTITY,
|
||||
|
||||
/**
|
||||
* Represents an unknown source
|
||||
*/
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,7 @@ package me.lucko.luckperms.api.event.user.track;
|
||||
import me.lucko.luckperms.api.Track;
|
||||
import me.lucko.luckperms.api.User;
|
||||
import me.lucko.luckperms.api.event.LuckPermsEvent;
|
||||
import me.lucko.luckperms.api.event.Sourced;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -36,7 +37,7 @@ import javax.annotation.Nonnull;
|
||||
/**
|
||||
* Called when a user interacts with a track through a promotion or demotion
|
||||
*/
|
||||
public interface UserTrackEvent extends LuckPermsEvent {
|
||||
public interface UserTrackEvent extends LuckPermsEvent, Sourced {
|
||||
|
||||
/**
|
||||
* Gets the track involved in the event
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
package me.lucko.luckperms.api.manager;
|
||||
|
||||
import me.lucko.luckperms.api.Group;
|
||||
import me.lucko.luckperms.api.Storage;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -41,7 +43,103 @@ import javax.annotation.Nullable;
|
||||
public interface GroupManager {
|
||||
|
||||
/**
|
||||
* Gets a wrapped group object from the group storage
|
||||
* Creates a new group in the plugin's storage provider and then loads it
|
||||
* into memory.
|
||||
*
|
||||
* <p>If a group by the same name already exists, it will be loaded.</p>
|
||||
*
|
||||
* <p>This method is effectively the same as
|
||||
* {@link Storage#createAndLoadGroup(String)}, however, the Future returns
|
||||
* the resultant group instance instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a group cannot be loaded,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @return the resultant group
|
||||
* @throws NullPointerException if the name is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Group> createAndLoadGroup(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Loads a group from the plugin's storage provider into memory.
|
||||
*
|
||||
* <p>Returns an {@link Optional#empty() empty optional} if the group does
|
||||
* not exist.</p>
|
||||
*
|
||||
* <p>This method is effectively the same as
|
||||
* {@link Storage#loadGroup(String)}, however, the Future returns
|
||||
* the resultant group instance instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a group cannot be loaded,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param name the name of the group
|
||||
* @return the resultant group
|
||||
* @throws NullPointerException if the name is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Optional<Group>> loadGroup(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Saves a group's data back to the plugin's storage provider.
|
||||
*
|
||||
* <p>You should call this after you make any changes to a group.</p>
|
||||
*
|
||||
* <p>This method is effectively the same as {@link Storage#saveGroup(Group)},
|
||||
* however, the Future returns void instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a group cannot be saved,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param group the group to save
|
||||
* @return a future to encapsulate the operation.
|
||||
* @throws NullPointerException if group is null
|
||||
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> saveGroup(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Permanently deletes a group from the plugin's storage provider.
|
||||
*
|
||||
* <p>This method is effectively the same as {@link Storage#deleteGroup(Group)},
|
||||
* however, the Future returns void instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a group cannot be deleted,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
*
|
||||
* @param group the group to delete
|
||||
* @return a future to encapsulate the operation.
|
||||
* @throws NullPointerException if group is null
|
||||
* @throws IllegalStateException if the group instance was not obtained from LuckPerms.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> deleteGroup(@Nonnull Group group);
|
||||
|
||||
/**
|
||||
* Loads all groups into memory.
|
||||
*
|
||||
* <p>This method is effectively the same as {@link Storage#loadAllTracks()},
|
||||
* however, the Future returns void instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a group cannot be loaded,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @return a future to encapsulate the operation.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> loadAllGroups();
|
||||
|
||||
/**
|
||||
* Gets a loaded group.
|
||||
*
|
||||
* @param name the name of the group to get
|
||||
* @return a {@link Group} object, if one matching the name exists, or null if not
|
||||
@@ -51,7 +149,7 @@ public interface GroupManager {
|
||||
Group getGroup(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Gets a wrapped group object from the group storage.
|
||||
* Gets a loaded group.
|
||||
*
|
||||
* <p>This method does not return null, unlike {@link #getGroup}</p>
|
||||
*
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
|
||||
package me.lucko.luckperms.api.manager;
|
||||
|
||||
import me.lucko.luckperms.api.Storage;
|
||||
import me.lucko.luckperms.api.Track;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -41,7 +43,103 @@ import javax.annotation.Nullable;
|
||||
public interface TrackManager {
|
||||
|
||||
/**
|
||||
* Gets a wrapped track object from the track storage
|
||||
* Creates a new track in the plugin's storage provider and then loads it
|
||||
* into memory.
|
||||
*
|
||||
* <p>If a track by the same name already exists, it will be loaded.</p>
|
||||
*
|
||||
* <p>This method is effectively the same as
|
||||
* {@link Storage#createAndLoadTrack(String)}, however, the Future returns
|
||||
* the resultant track instance instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a track cannot be loaded,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param name the name of the track
|
||||
* @return the resultant track
|
||||
* @throws NullPointerException if the name is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Track> createAndLoadTrack(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Loads a track from the plugin's storage provider into memory.
|
||||
*
|
||||
* <p>Returns an {@link Optional#empty() empty optional} if the track does
|
||||
* not exist.</p>
|
||||
*
|
||||
* <p>This method is effectively the same as
|
||||
* {@link Storage#loadTrack(String)}, however, the Future returns
|
||||
* the resultant track instance instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a track cannot be loaded,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param name the name of the track
|
||||
* @return the resultant track
|
||||
* @throws NullPointerException if the name is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Optional<Track>> loadTrack(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Saves a track's data back to the plugin's storage provider.
|
||||
*
|
||||
* <p>You should call this after you make any changes to a track.</p>
|
||||
*
|
||||
* <p>This method is effectively the same as {@link Storage#saveTrack(Track)},
|
||||
* however, the Future returns void instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a track cannot be saved,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param track the track to save
|
||||
* @return a future to encapsulate the operation.
|
||||
* @throws NullPointerException if track is null
|
||||
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> saveTrack(@Nonnull Track track);
|
||||
|
||||
/**
|
||||
* Permanently deletes a track from the plugin's storage provider.
|
||||
*
|
||||
* <p>This method is effectively the same as {@link Storage#deleteTrack(Track)},
|
||||
* however, the Future returns void instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a track cannot be deleted,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
*
|
||||
* @param track the track to delete
|
||||
* @return a future to encapsulate the operation.
|
||||
* @throws NullPointerException if track is null
|
||||
* @throws IllegalStateException if the track instance was not obtained from LuckPerms.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> deleteTrack(@Nonnull Track track);
|
||||
|
||||
/**
|
||||
* Loads all tracks into memory.
|
||||
*
|
||||
* <p>This method is effectively the same as {@link Storage#loadAllTracks()},
|
||||
* however, the Future returns void instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a track cannot be loaded,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @return a future to encapsulate the operation.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> loadAllTracks();
|
||||
|
||||
/**
|
||||
* Gets a loaded track.
|
||||
*
|
||||
* @param name the name of the track to get
|
||||
* @return a {@link Track} object, if one matching the name exists, or null if not
|
||||
@@ -51,7 +149,7 @@ public interface TrackManager {
|
||||
Track getTrack(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Gets a wrapped track object from the track storage.
|
||||
* Gets a loaded track.
|
||||
*
|
||||
* <p>This method does not return null, unlike {@link #getTrack}</p>
|
||||
*
|
||||
|
||||
@@ -25,11 +25,13 @@
|
||||
|
||||
package me.lucko.luckperms.api.manager;
|
||||
|
||||
import me.lucko.luckperms.api.Storage;
|
||||
import me.lucko.luckperms.api.User;
|
||||
|
||||
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;
|
||||
@@ -37,12 +39,74 @@ import javax.annotation.Nullable;
|
||||
/**
|
||||
* Represents the object responsible for managing {@link User} instances.
|
||||
*
|
||||
* <p>Note that User instances are automatically loaded for online players.
|
||||
* It's likely that offline players will not have an instance pre-loaded.</p>
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface UserManager {
|
||||
|
||||
/**
|
||||
* Gets a wrapped user object from the user storage
|
||||
* Loads a user from the plugin's storage provider into memory.
|
||||
*
|
||||
* <p>This method is effectively the same as
|
||||
* {@link Storage#loadUser(UUID, String)}, however, the Future returns the
|
||||
* resultant user instance instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a user cannot be loaded,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param uuid the uuid of the user
|
||||
* @param username the username, if known
|
||||
* @return the resultant user
|
||||
* @throws NullPointerException if the uuid is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<User> loadUser(@Nonnull UUID uuid, @Nullable String username);
|
||||
|
||||
/**
|
||||
* Loads a user from the plugin's storage provider into memory.
|
||||
*
|
||||
* <p>This method is effectively the same as {@link Storage#loadUser(UUID)},
|
||||
* however, the Future returns the resultant user instance instead of a
|
||||
* boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a user cannot be loaded,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param uuid the uuid of the user
|
||||
* @return the resultant user
|
||||
* @throws NullPointerException if the uuid is null
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
default CompletableFuture<User> loadUser(@Nonnull UUID uuid) {
|
||||
return loadUser(uuid, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a user's data back to the plugin's storage provider.
|
||||
*
|
||||
* <p>You should call this after you make any changes to a user.</p>
|
||||
*
|
||||
* <p>This method is effectively the same as {@link Storage#saveUser(User)},
|
||||
* however, the Future returns void instead of a boolean flag.</p>
|
||||
*
|
||||
* <p>Unlike the method in {@link Storage}, when a user cannot be saved,
|
||||
* the future will be {@link CompletableFuture completed exceptionally}.</p>
|
||||
*
|
||||
* @param user the user to save
|
||||
* @return a future to encapsulate the operation.
|
||||
* @throws NullPointerException if user is null
|
||||
* @throws IllegalStateException if the user instance was not obtained from LuckPerms.
|
||||
* @since 4.1
|
||||
*/
|
||||
@Nonnull
|
||||
CompletableFuture<Void> saveUser(@Nonnull User user);
|
||||
|
||||
/**
|
||||
* Gets a loaded user.
|
||||
*
|
||||
* @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
|
||||
@@ -52,7 +116,7 @@ public interface UserManager {
|
||||
User getUser(@Nonnull UUID uuid);
|
||||
|
||||
/**
|
||||
* Gets a wrapped user object from the user storage.
|
||||
* Gets a loaded user.
|
||||
*
|
||||
* @param uuid the uuid of the user to get
|
||||
* @return an optional {@link User} object
|
||||
@@ -64,7 +128,7 @@ public interface UserManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a wrapped user object from the user storage
|
||||
* Gets a loaded user.
|
||||
*
|
||||
* @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
|
||||
@@ -74,7 +138,7 @@ public interface UserManager {
|
||||
User getUser(@Nonnull String name);
|
||||
|
||||
/**
|
||||
* Gets a wrapped user object from the user storage.
|
||||
* Gets a loaded user.
|
||||
*
|
||||
* @param name the username of the user to get
|
||||
* @return an optional {@link User} object
|
||||
|
||||
@@ -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.messenger;
|
||||
|
||||
import me.lucko.luckperms.api.messenger.message.Message;
|
||||
import me.lucko.luckperms.api.messenger.message.OutgoingMessage;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Encapsulates the LuckPerms system which accepts incoming {@link Message}s
|
||||
* from implementations of {@link MessengerProvider}.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface IncomingMessageConsumer {
|
||||
|
||||
/**
|
||||
* Consumes a message instance.
|
||||
*
|
||||
* <p>The boolean returned from this method indicates whether or not the
|
||||
* platform accepted the message. Some implementations which have multiple
|
||||
* distribution channels may wish to use this result to dispatch the same
|
||||
* message back to additional receivers.</p>
|
||||
*
|
||||
* <p>The implementation will usually return <code>false</code> if a message
|
||||
* with the same ping id has already been processed.</p>
|
||||
*
|
||||
* @param message the message
|
||||
* @return true if the message was accepted by the plugin
|
||||
*/
|
||||
boolean consumeIncomingMessage(@Nonnull Message message);
|
||||
|
||||
/**
|
||||
* Consumes a message in an encoded string format.
|
||||
*
|
||||
* <p>This method will decode strings obtained by calling
|
||||
* {@link OutgoingMessage#asEncodedString()}. This means that basic
|
||||
* implementations can successfully implement {@link Messenger} without
|
||||
* providing their own serialisation.</p>
|
||||
*
|
||||
* <p>The boolean returned from this method indicates whether or not the
|
||||
* platform accepted the message. Some implementations which have multiple
|
||||
* distribution channels may wish to use this result to dispatch the same
|
||||
* message back to additional receivers.</p>
|
||||
*
|
||||
* <p>The implementation will usually return <code>false</code> if a message
|
||||
* with the same ping id has already been processed.</p>
|
||||
*
|
||||
* @param encodedString the encoded string
|
||||
* @return true if the message was accepted by the plugin
|
||||
*/
|
||||
boolean consumeIncomingMessageAsString(@Nonnull String encodedString);
|
||||
|
||||
}
|
||||
+23
-20
@@ -23,33 +23,36 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.bukkit.processors;
|
||||
package me.lucko.luckperms.api.messenger;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.lucko.luckperms.api.messenger.message.Message;
|
||||
import me.lucko.luckperms.api.messenger.message.OutgoingMessage;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Performs the initial setup for Bukkit permission processors
|
||||
* Represents an object which dispatches {@link OutgoingMessage}s.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitProcessorsSetupTask implements Runnable {
|
||||
private final LPBukkitPlugin plugin;
|
||||
public interface Messenger extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Performs the necessary action to dispatch the message using the means
|
||||
* of the messenger.
|
||||
*
|
||||
* <p>The outgoing message instance is guaranteed to be an instance of one
|
||||
* of the interfaces extending {@link Message} in the
|
||||
* 'api.messenger.message.type' package.</p>
|
||||
*
|
||||
* <p>This call is always made async.</p>
|
||||
*
|
||||
* @param outgoingMessage the outgoing message
|
||||
*/
|
||||
void sendOutgoingMessage(@Nonnull OutgoingMessage outgoingMessage);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
plugin.getDefaultsProvider().refresh();
|
||||
plugin.getChildPermissionProvider().setup();
|
||||
default void close() {
|
||||
|
||||
Set<String> perms = new HashSet<>();
|
||||
plugin.getServer().getPluginManager().getPermissions().forEach(p -> {
|
||||
perms.add(p.getName());
|
||||
perms.addAll(p.getChildren().keySet());
|
||||
});
|
||||
|
||||
perms.forEach(p -> plugin.getPermissionVault().offer(p));
|
||||
}
|
||||
}
|
||||
+31
-32
@@ -23,44 +23,43 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.bulkupdate.comparisons;
|
||||
package me.lucko.luckperms.api.messenger;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.lucko.luckperms.api.LuckPermsApi;
|
||||
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.impl.ComparisonEqual;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.impl.ComparisonNotEqual;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.impl.ComparisonNotSimilar;
|
||||
import me.lucko.luckperms.common.bulkupdate.comparisons.impl.ComparisonSimilar;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An enumeration of all {@link Comparison}s.
|
||||
* Represents a provider for {@link Messenger} instances.
|
||||
*
|
||||
* <p>Users wishing to provide their own implementation for the plugins
|
||||
* "Messaging Service" should implement and register this interface.</p>
|
||||
*
|
||||
* @since 4.1
|
||||
* @see LuckPermsApi#registerMessengerProvider(MessengerProvider)
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum ComparisonType {
|
||||
public interface MessengerProvider {
|
||||
|
||||
EQUAL("==", new ComparisonEqual()),
|
||||
NOT_EQUAL("!=", new ComparisonNotEqual()),
|
||||
SIMILAR("~~", new ComparisonSimilar()),
|
||||
NOT_SIMILAR("!~", new ComparisonNotSimilar());
|
||||
/**
|
||||
* Gets the name of this provider.
|
||||
*
|
||||
* @return the provider name
|
||||
*/
|
||||
@Nonnull
|
||||
String getName();
|
||||
|
||||
private final String symbol;
|
||||
private final Comparison instance;
|
||||
|
||||
public String getSymbol() {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public Comparison getComparison() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static ComparisonType parseComparison(String s) {
|
||||
for (ComparisonType t : values()) {
|
||||
if (t.getSymbol().equals(s)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Creates and returns a new {@link Messenger} instance, which passes
|
||||
* incoming messages to the provided {@link IncomingMessageConsumer}.
|
||||
*
|
||||
* <p>As the agent should pass incoming messages to the given consumer,
|
||||
* this method should always return a new object.</p>
|
||||
*
|
||||
* @param incomingMessageConsumer the consumer the new instance should pass
|
||||
* incoming messages to
|
||||
* @return a new messenger agent instance
|
||||
*/
|
||||
@Nonnull
|
||||
Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.messenger.message;
|
||||
|
||||
import me.lucko.luckperms.api.messenger.Messenger;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents a message sent received via a {@link Messenger}.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface Message {
|
||||
|
||||
/**
|
||||
* Gets the unique id associated with this message.
|
||||
*
|
||||
* <p>This ID is used to ensure a single server instance doesn't process
|
||||
* the same message twice.</p>
|
||||
*
|
||||
* @return the id of the message
|
||||
*/
|
||||
@Nonnull
|
||||
UUID getId();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.messenger.message;
|
||||
|
||||
import me.lucko.luckperms.api.messenger.IncomingMessageConsumer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents an outgoing {@link Message}.
|
||||
*
|
||||
* <p>Outgoing messages are ones which have been generated by this instance.
|
||||
* (in other words, they are implemented by LuckPerms)</p>
|
||||
*
|
||||
* <p>Note that all implementations of this interface are guaranteed to be an
|
||||
* instance of one of the interfaces extending {@link Message} in the
|
||||
* 'api.messenger.message.type' package.</p>
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface OutgoingMessage extends Message {
|
||||
|
||||
/**
|
||||
* Gets an encoded string form of this message.
|
||||
*
|
||||
* <p>The format of this string is likely to change between versions and
|
||||
* should not be depended on.</p>
|
||||
*
|
||||
* <p>Implementations which want to use a standard method of serialisation
|
||||
* can send outgoing messages using the string returned by this method, and
|
||||
* pass on the message on the "other side" using
|
||||
* {@link IncomingMessageConsumer#consumeIncomingMessageAsString(String)}.</p>
|
||||
*
|
||||
* @return an encoded string form of the message
|
||||
*/
|
||||
@Nonnull
|
||||
String asEncodedString();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.messenger.message.type;
|
||||
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.messenger.message.Message;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents an "log" message.
|
||||
*
|
||||
* <p>Used to dispatch live log updates to other servers.</p>
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface LogMessage extends Message {
|
||||
|
||||
/**
|
||||
* Gets the log entry being sent
|
||||
*
|
||||
* @return the log entry
|
||||
*/
|
||||
@Nonnull
|
||||
LogEntry getLogEntry();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.messenger.message.type;
|
||||
|
||||
import me.lucko.luckperms.api.messenger.message.Message;
|
||||
|
||||
/**
|
||||
* Represents an "update" message.
|
||||
*
|
||||
* <p>Used to notify other servers of general changes.</p>
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface UpdateMessage extends Message {
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.messenger.message.type;
|
||||
|
||||
import me.lucko.luckperms.api.messenger.message.Message;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Represents an "user update" message.
|
||||
*
|
||||
* <p>Used to notify other servers of a change to a specific user.</p>
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface UserUpdateMessage extends Message {
|
||||
|
||||
/**
|
||||
* Gets the user the message is for.
|
||||
*
|
||||
* @return the user
|
||||
*/
|
||||
@Nonnull
|
||||
UUID getUser();
|
||||
|
||||
}
|
||||
@@ -28,6 +28,7 @@ package me.lucko.luckperms.api.metastacking;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Represents a meta stack model, consisting of a chain of elements, separated by spacers.
|
||||
@@ -40,6 +41,7 @@ import javax.annotation.Nonnull;
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
@Immutable
|
||||
public interface MetaStackDefinition {
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,12 +33,16 @@ import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* Represents an element within a {@link MetaStackDefinition}.
|
||||
*
|
||||
* <p>The element itself does not contain any mutable state.</p>
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
@Immutable
|
||||
public interface MetaStackElement {
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,8 @@ public enum PlatformType {
|
||||
|
||||
BUKKIT("Bukkit"),
|
||||
BUNGEE("Bungee"),
|
||||
SPONGE("Sponge");
|
||||
SPONGE("Sponge"),
|
||||
NUKKIT("Nukkit");
|
||||
|
||||
private final String friendlyName;
|
||||
|
||||
@@ -51,6 +52,6 @@ public enum PlatformType {
|
||||
*/
|
||||
@Nonnull
|
||||
public String getFriendlyName() {
|
||||
return friendlyName;
|
||||
return this.friendlyName;
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>luckperms</artifactId>
|
||||
<groupId>me.lucko.luckperms</groupId>
|
||||
<version>4.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>luckperms-bukkit-legacy</artifactId>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean package</defaultGoal>
|
||||
<finalName>LuckPerms-Bukkit-Legacy-${full.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${compiler.version}</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>${shade.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.google.common</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.guava</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.google.gson</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.gson</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>net.kyori.text</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.text</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</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>
|
||||
<groupId>me.lucko.luckperms</groupId>
|
||||
<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 -->
|
||||
<!-- gson -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- guava -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>19.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+118
-61
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>luckperms</artifactId>
|
||||
<groupId>me.lucko.luckperms</groupId>
|
||||
<version>4.0-SNAPSHOT</version>
|
||||
<version>4.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -45,10 +45,65 @@
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<!-- shaded dependencies -->
|
||||
<relocation>
|
||||
<pattern>net.kyori.text</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.text</shadedPattern>
|
||||
</relocation>
|
||||
|
||||
<!-- relocated dependencies -->
|
||||
<relocation>
|
||||
<pattern>com.github.benmanes.caffeine</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.caffeine</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>okio</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.okio</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>okhttp3</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.okhttp3</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.mariadb.jdbc</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.mariadb</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mysql</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.mysql</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.postgresql</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.postgresql</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.zaxxer.hikari</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mongodb</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.mongodb</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.bson</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.bson</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>redis.clients.jedis</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.jedis</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.apache.commons.pool2</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.commonspool2</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>ninja.leaping.configurate</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.configurate</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.typesafe.config</pattern>
|
||||
<shadedPattern>me.lucko.luckperms.lib.hocon</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
@@ -57,38 +112,47 @@
|
||||
</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>
|
||||
<!-- Spigot API -->
|
||||
<!-- common -->
|
||||
<dependency>
|
||||
<groupId>me.lucko.luckperms</groupId>
|
||||
<artifactId>luckperms-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- jsr305 -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- caffeine -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>2.6.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- HikariCP -->
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>2.7.6</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spigot -->
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.12-R0.1-SNAPSHOT</version>
|
||||
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Vault -->
|
||||
<dependency>
|
||||
<groupId>net.milkbowl.vault</groupId>
|
||||
@@ -102,27 +166,6 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- LuckPerms Common -->
|
||||
<dependency>
|
||||
<groupId>me.lucko.luckperms</groupId>
|
||||
<artifactId>luckperms-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- LuckPerms API -->
|
||||
<dependency>
|
||||
<groupId>me.lucko.luckperms</groupId>
|
||||
<artifactId>luckperms-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- LilyPad -->
|
||||
<dependency>
|
||||
<groupId>lilypad.client.connect</groupId>
|
||||
@@ -130,7 +173,6 @@
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- ************** Migration Plugins ************** -->
|
||||
<!-- zPermissions -->
|
||||
<dependency>
|
||||
<groupId>org.tyrannyofheaven.bukkit</groupId>
|
||||
@@ -165,20 +207,10 @@
|
||||
</dependency>
|
||||
<!-- PowerfulPerms -->
|
||||
<dependency>
|
||||
<groupId>com.github.cheesesoftware</groupId>
|
||||
<groupId>com.github.gustav9797</groupId>
|
||||
<artifactId>PowerfulPermsAPI</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<version>4.5.2</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>
|
||||
@@ -194,5 +226,30 @@
|
||||
<version>2.12</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- PermissionsBukkit -->
|
||||
<dependency>
|
||||
<groupId>com.platymuus</groupId>
|
||||
<artifactId>bukkit-permissions</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mcstats.bukkit</groupId>
|
||||
<artifactId>metrics</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
@@ -30,7 +30,6 @@ 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 org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@@ -40,6 +39,7 @@ import org.bukkit.command.TabExecutor;
|
||||
import java.util.List;
|
||||
|
||||
public class BukkitCommandExecutor extends CommandManager implements CommandExecutor, TabExecutor {
|
||||
private static final Splitter TAB_COMPLETE_ARGUMENT_SPLITTER = Splitter.on(COMMAND_SEPARATOR_PATTERN);
|
||||
private static final Splitter ARGUMENT_SPLITTER = Splitter.on(COMMAND_SEPARATOR_PATTERN).omitEmptyStrings();
|
||||
private static final Joiner ARGUMENT_JOINER = Joiner.on(' ');
|
||||
|
||||
@@ -52,8 +52,8 @@ public class BukkitCommandExecutor extends CommandManager implements CommandExec
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
Sender lpSender = plugin.getSenderFactory().wrap(sender);
|
||||
List<String> arguments = Util.stripQuotes(ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
|
||||
Sender lpSender = this.plugin.getSenderFactory().wrap(sender);
|
||||
List<String> arguments = stripQuotes(ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
|
||||
|
||||
onCommand(lpSender, label, arguments);
|
||||
return true;
|
||||
@@ -61,8 +61,8 @@ public class BukkitCommandExecutor extends CommandManager implements CommandExec
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
|
||||
Sender lpSender = plugin.getSenderFactory().wrap(sender);
|
||||
List<String> arguments = Util.stripQuotes(ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
|
||||
Sender lpSender = this.plugin.getSenderFactory().wrap(sender);
|
||||
List<String> arguments = stripQuotes(TAB_COMPLETE_ARGUMENT_SPLITTER.splitToList(ARGUMENT_JOINER.join(args)));
|
||||
|
||||
return onTabComplete(lpSender, arguments);
|
||||
}
|
||||
|
||||
@@ -25,10 +25,9 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import me.lucko.luckperms.common.config.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.config.adapter.AbstractConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.config.adapter.ConfigurationAdapter;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
@@ -40,55 +39,51 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitConfigAdapter implements ConfigurationAdapter {
|
||||
|
||||
@Getter
|
||||
private final LPBukkitPlugin plugin;
|
||||
public class BukkitConfigAdapter extends AbstractConfigurationAdapter implements ConfigurationAdapter {
|
||||
|
||||
private final File file;
|
||||
private YamlConfiguration configuration;
|
||||
|
||||
public BukkitConfigAdapter(LuckPermsPlugin plugin, File file) {
|
||||
super(plugin);
|
||||
this.file = file;
|
||||
reload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
File configFile = new File(plugin.getDataFolder(), "config.yml");
|
||||
|
||||
if (!configFile.exists()) {
|
||||
configFile.getParentFile().mkdirs();
|
||||
plugin.saveResource("config.yml", false);
|
||||
}
|
||||
|
||||
configuration = YamlConfiguration.loadConfiguration(configFile);
|
||||
public void reload() {
|
||||
this.configuration = YamlConfiguration.loadConfiguration(this.file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String path) {
|
||||
return configuration.contains(path);
|
||||
return this.configuration.contains(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String path, String def) {
|
||||
return configuration.getString(path, def);
|
||||
return this.configuration.getString(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String path, int def) {
|
||||
return configuration.getInt(path, def);
|
||||
return this.configuration.getInt(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String path, boolean def) {
|
||||
return configuration.getBoolean(path, def);
|
||||
return this.configuration.getBoolean(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getList(String path, List<String> def) {
|
||||
List<String> ret = configuration.getStringList(path);
|
||||
List<String> ret = this.configuration.getStringList(path);
|
||||
return ret == null ? def : ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getObjectList(String path, List<String> def) {
|
||||
ConfigurationSection section = configuration.getConfigurationSection(path);
|
||||
ConfigurationSection section = this.configuration.getConfigurationSection(path);
|
||||
if (section == null) {
|
||||
return def;
|
||||
}
|
||||
@@ -100,7 +95,7 @@ public class BukkitConfigAdapter implements ConfigurationAdapter {
|
||||
@Override
|
||||
public Map<String, String> getMap(String path, Map<String, String> def) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
ConfigurationSection section = configuration.getConfigurationSection(path);
|
||||
ConfigurationSection section = this.configuration.getConfigurationSection(path);
|
||||
if (section == null) {
|
||||
return def;
|
||||
}
|
||||
|
||||
@@ -25,53 +25,48 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
|
||||
import me.lucko.luckperms.common.plugin.SchedulerTask;
|
||||
import me.lucko.luckperms.common.utils.SafeIteration;
|
||||
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BukkitSchedulerAdapter implements SchedulerAdapter {
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
@Getter
|
||||
@Accessors(fluent = true)
|
||||
private ExecutorService asyncLp;
|
||||
private final ExecutorService asyncFallback;
|
||||
private final Executor asyncBukkit;
|
||||
private final Executor sync;
|
||||
private final Executor async;
|
||||
|
||||
@Getter
|
||||
@Accessors(fluent = true)
|
||||
private Executor asyncBukkit;
|
||||
private boolean useFallback = true;
|
||||
|
||||
@Getter
|
||||
@Accessors(fluent = true)
|
||||
private Executor sync;
|
||||
|
||||
@Getter
|
||||
@Accessors(fluent = true)
|
||||
private Executor async;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean useBukkitAsync = false;
|
||||
|
||||
private final Set<BukkitTask> tasks = ConcurrentHashMap.newKeySet();
|
||||
private final Set<SchedulerTask> tasks = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public BukkitSchedulerAdapter(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
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);
|
||||
this.sync = new SyncExecutor();
|
||||
this.asyncFallback = new FallbackAsyncExecutor();
|
||||
this.asyncBukkit = new BukkitAsyncExecutor();
|
||||
this.async = new AsyncExecutor();
|
||||
}
|
||||
|
||||
private BukkitScheduler scheduler() {
|
||||
return this.plugin.getServer().getScheduler();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,37 +80,107 @@ public class BukkitSchedulerAdapter implements SchedulerAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void asyncRepeating(Runnable runnable, long intervalTicks) {
|
||||
BukkitTask task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, runnable, intervalTicks, intervalTicks);
|
||||
tasks.add(task);
|
||||
public SchedulerTask asyncRepeating(Runnable runnable, long intervalTicks) {
|
||||
SchedulerTask task = new BukkitSchedulerTask(scheduler().runTaskTimerAsynchronously(this.plugin, runnable, intervalTicks, intervalTicks));
|
||||
this.tasks.add(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncRepeating(Runnable runnable, long intervalTicks) {
|
||||
BukkitTask task = plugin.getServer().getScheduler().runTaskTimer(plugin, runnable, intervalTicks, intervalTicks);
|
||||
tasks.add(task);
|
||||
public SchedulerTask syncRepeating(Runnable runnable, long intervalTicks) {
|
||||
SchedulerTask task = new BukkitSchedulerTask(scheduler().runTaskTimer(this.plugin, runnable, intervalTicks, intervalTicks));
|
||||
this.tasks.add(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void asyncLater(Runnable runnable, long delayTicks) {
|
||||
plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, runnable, delayTicks);
|
||||
public SchedulerTask asyncLater(Runnable runnable, long delayTicks) {
|
||||
return new BukkitSchedulerTask(scheduler().runTaskLaterAsynchronously(this.plugin, runnable, delayTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncLater(Runnable runnable, long delayTicks) {
|
||||
plugin.getServer().getScheduler().runTaskLater(plugin, runnable, delayTicks);
|
||||
public SchedulerTask syncLater(Runnable runnable, long delayTicks) {
|
||||
return new BukkitSchedulerTask(scheduler().runTaskLater(this.plugin, runnable, delayTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
tasks.forEach(BukkitTask::cancel);
|
||||
SafeIteration.iterate(this.tasks, SchedulerTask::cancel);
|
||||
|
||||
// wait for executor
|
||||
asyncLp.shutdown();
|
||||
this.asyncFallback.shutdown();
|
||||
try {
|
||||
asyncLp.awaitTermination(30, TimeUnit.SECONDS);
|
||||
this.asyncFallback.awaitTermination(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public ExecutorService asyncFallback() {
|
||||
return this.asyncFallback;
|
||||
}
|
||||
|
||||
public Executor asyncBukkit() {
|
||||
return this.asyncBukkit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor sync() {
|
||||
return this.sync;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor async() {
|
||||
return this.async;
|
||||
}
|
||||
|
||||
public void setUseFallback(boolean useFallback) {
|
||||
this.useFallback = useFallback;
|
||||
}
|
||||
|
||||
private final class SyncExecutor implements Executor {
|
||||
@Override
|
||||
public void execute(@Nonnull Runnable runnable) {
|
||||
BukkitSchedulerAdapter.this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(BukkitSchedulerAdapter.this.plugin, runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private final class AsyncExecutor implements Executor {
|
||||
@Override
|
||||
public void execute(@Nonnull Runnable runnable) {
|
||||
if (BukkitSchedulerAdapter.this.useFallback || !BukkitSchedulerAdapter.this.plugin.isEnabled()) {
|
||||
BukkitSchedulerAdapter.this.asyncFallback.execute(runnable);
|
||||
} else {
|
||||
BukkitSchedulerAdapter.this.asyncBukkit.execute(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class BukkitAsyncExecutor implements Executor {
|
||||
@Override
|
||||
public void execute(@Nonnull Runnable runnable) {
|
||||
BukkitSchedulerAdapter.this.plugin.getServer().getScheduler().runTaskAsynchronously(BukkitSchedulerAdapter.this.plugin, runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FallbackAsyncExecutor extends ThreadPoolExecutor {
|
||||
private FallbackAsyncExecutor() {
|
||||
super(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), new ThreadFactoryBuilder().setNameFormat("luckperms-fallback-%d").build());
|
||||
}
|
||||
}
|
||||
|
||||
private static final class BukkitSchedulerTask implements SchedulerTask {
|
||||
private final BukkitTask task;
|
||||
|
||||
private BukkitSchedulerTask(BukkitTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
this.task.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,28 +25,33 @@
|
||||
|
||||
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.bukkit.compat.BukkitJsonMessageHandler;
|
||||
import me.lucko.luckperms.bukkit.compat.ReflectionUtil;
|
||||
import me.lucko.luckperms.bukkit.compat.SpigotJsonMessageHandler;
|
||||
import me.lucko.luckperms.common.commands.CommandManager;
|
||||
import me.lucko.luckperms.common.commands.sender.SenderFactory;
|
||||
import me.lucko.luckperms.common.constants.Constants;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.TextUtils;
|
||||
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
|
||||
import org.bukkit.command.BlockCommandSender;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.RemoteConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BukkitSenderFactory extends SenderFactory<CommandSender> {
|
||||
private final MessageHandler messageHandler;
|
||||
private final BukkitJsonMessageHandler bukkitHandler;
|
||||
private final SpigotJsonMessageHandler spigotHandler;
|
||||
|
||||
public BukkitSenderFactory(LuckPermsPlugin plugin) {
|
||||
super(plugin);
|
||||
messageHandler = new MessageHandler();
|
||||
this.bukkitHandler = new BukkitJsonMessageHandler();
|
||||
this.spigotHandler = isSpigot() ? new SpigotJsonMessageHandler() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +59,7 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
|
||||
if (sender instanceof Player) {
|
||||
return sender.getName();
|
||||
}
|
||||
return Constants.CONSOLE_NAME;
|
||||
return CommandManager.CONSOLE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,24 +67,40 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
|
||||
if (sender instanceof Player) {
|
||||
return ((Player) sender).getUniqueId();
|
||||
}
|
||||
return Constants.CONSOLE_UUID;
|
||||
return CommandManager.CONSOLE_UUID;
|
||||
}
|
||||
|
||||
@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));
|
||||
// we can safely send async for players and the console
|
||||
if (sender instanceof Player || sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
|
||||
sender.sendMessage(s);
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(s);
|
||||
// otherwise, send the message sync
|
||||
getPlugin().getScheduler().doSync(new SyncMessengerAgent(sender, s));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendMessage(CommandSender sender, Component message) {
|
||||
messageHandler.sendJsonMessage(sender, message);
|
||||
if (ReflectionUtil.isChatCompatible() && sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
String json = ComponentSerializers.JSON.serialize(message);
|
||||
|
||||
// Try Bukkit.
|
||||
if (this.bukkitHandler.sendJsonMessage(player, json)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try Spigot.
|
||||
if (this.spigotHandler != null && this.spigotHandler.sendJsonMessage(player, json)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to legacy format
|
||||
sendMessage(sender, TextUtils.toLegacy(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,14 +116,27 @@ public class BukkitSenderFactory extends SenderFactory<CommandSender> {
|
||||
return sender.hasPermission(node);
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
private static final class BlockMessageAgent implements Runnable {
|
||||
private final BlockCommandSender block;
|
||||
private static boolean isSpigot() {
|
||||
try {
|
||||
Class.forName("net.md_5.bungee.chat.ComponentSerializer");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SyncMessengerAgent implements Runnable {
|
||||
private final CommandSender sender;
|
||||
private final String message;
|
||||
|
||||
private SyncMessengerAgent(CommandSender sender, String message) {
|
||||
this.sender = sender;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
block.sendMessage(message);
|
||||
this.sender.sendMessage(this.message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.LuckPermsApi;
|
||||
import me.lucko.luckperms.api.platform.PlatformType;
|
||||
@@ -36,41 +34,45 @@ 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.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.model.permissible.LPPermissible;
|
||||
import me.lucko.luckperms.bukkit.model.permissible.PermissibleInjector;
|
||||
import me.lucko.luckperms.bukkit.model.permissible.PermissibleMonitoringInjector;
|
||||
import me.lucko.luckperms.bukkit.model.server.InjectorDefaultsMap;
|
||||
import me.lucko.luckperms.bukkit.model.server.InjectorPermissionMap;
|
||||
import me.lucko.luckperms.bukkit.model.server.InjectorSubscriptionMap;
|
||||
import me.lucko.luckperms.bukkit.model.server.LPDefaultsMap;
|
||||
import me.lucko.luckperms.bukkit.model.server.LPPermissionMap;
|
||||
import me.lucko.luckperms.bukkit.model.server.LPSubscriptionMap;
|
||||
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.api.ApiRegistrationUtil;
|
||||
import me.lucko.luckperms.common.api.LuckPermsApiProvider;
|
||||
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.CommandPermission;
|
||||
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.CommandPermission;
|
||||
import me.lucko.luckperms.common.contexts.ContextManager;
|
||||
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.dependencies.DependencyRegistry;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.ReflectionClassLoader;
|
||||
import me.lucko.luckperms.common.event.EventFactory;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceHandler;
|
||||
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.messaging.ExtendedMessagingService;
|
||||
import me.lucko.luckperms.common.managers.group.StandardGroupManager;
|
||||
import me.lucko.luckperms.common.managers.track.StandardTrackManager;
|
||||
import me.lucko.luckperms.common.managers.user.StandardUserManager;
|
||||
import me.lucko.luckperms.common.messaging.InternalMessagingService;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.Storage;
|
||||
@@ -81,13 +83,12 @@ 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.LoginHelper;
|
||||
import me.lucko.luckperms.common.utils.UuidCache;
|
||||
import me.lucko.luckperms.common.verbose.VerboseHandler;
|
||||
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
@@ -96,8 +97,6 @@ import org.bukkit.plugin.java.JavaPlugin;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -109,7 +108,6 @@ import java.util.stream.Stream;
|
||||
/**
|
||||
* LuckPerms implementation for the Bukkit API.
|
||||
*/
|
||||
@Getter
|
||||
public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
|
||||
private long startTime;
|
||||
@@ -117,23 +115,26 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
private BukkitCommandExecutor commandManager;
|
||||
private VaultHookManager vaultHookManager = null;
|
||||
private LuckPermsConfiguration configuration;
|
||||
private UserManager userManager;
|
||||
private GroupManager groupManager;
|
||||
private TrackManager trackManager;
|
||||
private StandardUserManager userManager;
|
||||
private StandardGroupManager groupManager;
|
||||
private StandardTrackManager trackManager;
|
||||
private Storage storage;
|
||||
private FileWatcher fileWatcher = null;
|
||||
private ExtendedMessagingService messagingService = null;
|
||||
private UuidCache uuidCache;
|
||||
private ApiProvider apiProvider;
|
||||
private InternalMessagingService messagingService = null;
|
||||
private LuckPermsApiProvider apiProvider;
|
||||
private EventFactory eventFactory;
|
||||
private Logger log;
|
||||
private DefaultsProvider defaultsProvider;
|
||||
private ChildPermissionProvider childPermissionProvider;
|
||||
private LPSubscriptionMap subscriptionMap;
|
||||
private LPPermissionMap permissionMap;
|
||||
private LPDefaultsMap defaultPermissionMap;
|
||||
private LocaleManager localeManager;
|
||||
private PluginClassLoader pluginClassLoader;
|
||||
private DependencyManager dependencyManager;
|
||||
private InheritanceHandler inheritanceHandler;
|
||||
private CachedStateManager cachedStateManager;
|
||||
private ContextManager<Player> contextManager;
|
||||
private CalculatorFactory calculatorFactory;
|
||||
private BufferedRequest<Void> updateTaskBuffer;
|
||||
private boolean started = false;
|
||||
private CountDownLatch enableLatch = new CountDownLatch(1);
|
||||
private VerboseHandler verboseHandler;
|
||||
private BukkitSenderFactory senderFactory;
|
||||
@@ -141,30 +142,35 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
private LogDispatcher logDispatcher;
|
||||
private Set<UUID> uniqueConnections = ConcurrentHashMap.newKeySet();
|
||||
|
||||
private boolean invalidVersion = false;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (checkInvalidVersion()) {
|
||||
this.invalidVersion = true;
|
||||
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());
|
||||
this.scheduler = new BukkitSchedulerAdapter(this);
|
||||
this.localeManager = new NoopLocaleManager();
|
||||
this.senderFactory = new BukkitSenderFactory(this);
|
||||
this.log = new SenderLogger(this, getConsoleSender());
|
||||
|
||||
DependencyManager.loadDependencies(this, Collections.singleton(Dependency.CAFFEINE));
|
||||
this.pluginClassLoader = new ReflectionClassLoader(this);
|
||||
this.dependencyManager = new DependencyManager(this);
|
||||
this.dependencyManager.loadDependencies(DependencyRegistry.GLOBAL_DEPENDENCIES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (checkInvalidVersion()) {
|
||||
if (this.invalidVersion) {
|
||||
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("==> https://ci.lucko.me/job/LuckPermsLegacy/");
|
||||
getLogger().severe("----------------------------------------------------------------------");
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
@@ -172,110 +178,124 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
|
||||
try {
|
||||
enable();
|
||||
started = true;
|
||||
} finally {
|
||||
// count down the latch when onEnable has been called
|
||||
// we don't care about the result here
|
||||
enableLatch.countDown();
|
||||
this.enableLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
private void enable() {
|
||||
startTime = System.currentTimeMillis();
|
||||
LuckPermsPlugin.sendStartupBanner(getConsoleSender(), this);
|
||||
verboseHandler = new VerboseHandler(scheduler.asyncBukkit(), getVersion());
|
||||
permissionVault = new PermissionVault(scheduler.asyncBukkit());
|
||||
logDispatcher = new LogDispatcher(this);
|
||||
this.startTime = System.currentTimeMillis();
|
||||
sendStartupBanner(getConsoleSender());
|
||||
this.verboseHandler = new VerboseHandler(this.scheduler.asyncBukkit());
|
||||
this.permissionVault = new PermissionVault(this.scheduler.asyncBukkit());
|
||||
this.logDispatcher = new LogDispatcher(this);
|
||||
|
||||
getLog().info("Loading configuration...");
|
||||
configuration = new AbstractConfiguration(this, new BukkitConfigAdapter(this));
|
||||
configuration.init();
|
||||
this.configuration = new AbstractConfiguration(this, new BukkitConfigAdapter(this, resolveConfig("config.yml")));
|
||||
this.configuration.loadAll();
|
||||
|
||||
Set<StorageType> storageTypes = StorageFactory.getRequiredTypes(this, StorageType.H2);
|
||||
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.syncLater(new BukkitProcessorsSetupTask(this), 1L);
|
||||
StorageFactory storageFactory = new StorageFactory(this);
|
||||
Set<StorageType> storageTypes = storageFactory.getRequiredTypes(StorageType.H2);
|
||||
this.dependencyManager.loadStorageDependencies(storageTypes);
|
||||
|
||||
// register events
|
||||
getServer().getPluginManager().registerEvents(new BukkitConnectionListener(this), this);
|
||||
BukkitConnectionListener connectionListener = new BukkitConnectionListener(this);
|
||||
getServer().getPluginManager().registerEvents(connectionListener, this);
|
||||
getServer().getPluginManager().registerEvents(new BukkitPlatformListener(this), this);
|
||||
|
||||
if (getConfiguration().get(ConfigKeys.WATCH_FILES)) {
|
||||
fileWatcher = new FileWatcher(this);
|
||||
getScheduler().asyncRepeating(fileWatcher, 30L);
|
||||
this.fileWatcher = new FileWatcher(this);
|
||||
getScheduler().asyncRepeating(this.fileWatcher, 30L);
|
||||
}
|
||||
|
||||
// initialise datastore
|
||||
storage = StorageFactory.getInstance(this, StorageType.H2);
|
||||
this.storage = storageFactory.getInstance(StorageType.H2);
|
||||
|
||||
// initialise messaging
|
||||
messagingService = new BukkitMessagingFactory(this).getInstance();
|
||||
this.messagingService = new BukkitMessagingFactory(this).getInstance();
|
||||
|
||||
// setup the update task buffer
|
||||
updateTaskBuffer = new UpdateTaskBuffer(this);
|
||||
this.updateTaskBuffer = new UpdateTaskBuffer(this);
|
||||
|
||||
// load locale
|
||||
localeManager = new SimpleLocaleManager();
|
||||
localeManager.tryLoad(this, new File(getDataFolder(), "lang.yml"));
|
||||
this.localeManager = new SimpleLocaleManager();
|
||||
this.localeManager.tryLoad(this, new File(getDataFolder(), "lang.yml"));
|
||||
|
||||
// register commands
|
||||
commandManager = new BukkitCommandExecutor(this);
|
||||
this.commandManager = new BukkitCommandExecutor(this);
|
||||
PluginCommand main = getServer().getPluginCommand("luckperms");
|
||||
main.setExecutor(commandManager);
|
||||
main.setTabCompleter(commandManager);
|
||||
main.setExecutor(this.commandManager);
|
||||
main.setTabCompleter(this.commandManager);
|
||||
main.setDescription("Manage permissions");
|
||||
main.setAliases(Arrays.asList("lp", "perm", "perms", "permission", "permissions"));
|
||||
|
||||
// load internal managers
|
||||
getLog().info("Loading internal permission managers...");
|
||||
uuidCache = new UuidCache(this);
|
||||
userManager = new GenericUserManager(this);
|
||||
groupManager = new GenericGroupManager(this);
|
||||
trackManager = new GenericTrackManager(this);
|
||||
calculatorFactory = new BukkitCalculatorFactory(this);
|
||||
cachedStateManager = new CachedStateManager();
|
||||
this.inheritanceHandler = new InheritanceHandler(this);
|
||||
this.userManager = new StandardUserManager(this);
|
||||
this.groupManager = new StandardGroupManager(this);
|
||||
this.trackManager = new StandardTrackManager(this);
|
||||
this.calculatorFactory = new BukkitCalculatorFactory(this);
|
||||
this.cachedStateManager = new CachedStateManager();
|
||||
|
||||
// setup context manager
|
||||
contextManager = new BukkitContextManager(this);
|
||||
contextManager.registerCalculator(new WorldCalculator(this));
|
||||
contextManager.registerStaticCalculator(new LuckPermsCalculator(getConfiguration()));
|
||||
this.contextManager = new BukkitContextManager(this);
|
||||
this.contextManager.registerCalculator(new WorldCalculator(this));
|
||||
this.contextManager.registerStaticCalculator(new LuckPermsCalculator(getConfiguration()));
|
||||
|
||||
// inject our own subscription map
|
||||
new SubscriptionMapInjector(this).run();
|
||||
// inject our own custom permission maps
|
||||
Runnable[] injectors = new Runnable[]{
|
||||
new InjectorSubscriptionMap(this),
|
||||
new InjectorPermissionMap(this),
|
||||
new InjectorDefaultsMap(this)
|
||||
};
|
||||
|
||||
// schedule another injection after all plugins have loaded - the entire pluginmanager instance
|
||||
// is replaced by some plugins :(
|
||||
scheduler.asyncLater(new SubscriptionMapInjector(this), 2L);
|
||||
for (Runnable injector : injectors) {
|
||||
injector.run();
|
||||
|
||||
// schedule another injection after all plugins have loaded
|
||||
// the entire pluginmanager instance is replaced by some plugins :(
|
||||
this.scheduler.asyncLater(injector, 1L);
|
||||
}
|
||||
|
||||
// inject verbose handlers into internal bukkit objects
|
||||
new PermissibleMonitoringInjector(this).run();
|
||||
|
||||
// Provide vault support
|
||||
tryVaultHook(false);
|
||||
|
||||
// register with the LP API
|
||||
apiProvider = new ApiProvider(this);
|
||||
ApiSingletonUtils.registerProvider(apiProvider);
|
||||
getServer().getServicesManager().register(LuckPermsApi.class, apiProvider, this, ServicePriority.Normal);
|
||||
this.apiProvider = new LuckPermsApiProvider(this);
|
||||
|
||||
// setup event factory
|
||||
this.eventFactory = new EventFactory(this, this.apiProvider);
|
||||
|
||||
ApiRegistrationUtil.registerProvider(this.apiProvider);
|
||||
getServer().getServicesManager().register(LuckPermsApi.class, this.apiProvider, this, ServicePriority.Normal);
|
||||
|
||||
|
||||
// schedule update tasks
|
||||
int mins = getConfiguration().get(ConfigKeys.SYNC_TIME);
|
||||
if (mins > 0) {
|
||||
long ticks = mins * 60 * 20;
|
||||
scheduler.asyncRepeating(() -> updateTaskBuffer.request(), ticks);
|
||||
this.scheduler.asyncRepeating(() -> this.updateTaskBuffer.request(), ticks);
|
||||
}
|
||||
scheduler.asyncLater(() -> updateTaskBuffer.request(), 40L);
|
||||
this.scheduler.asyncLater(() -> this.updateTaskBuffer.request(), 40L);
|
||||
|
||||
// run an update instantly.
|
||||
getLog().info("Performing initial data load...");
|
||||
new UpdateTask(this, true).run();
|
||||
try {
|
||||
new UpdateTask(this, true).run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
// register tasks
|
||||
scheduler.asyncRepeating(new ExpireTemporaryTask(this), 60L);
|
||||
scheduler.asyncRepeating(new CacheHousekeepingTask(this), 2400L);
|
||||
this.scheduler.asyncRepeating(new ExpireTemporaryTask(this), 60L);
|
||||
this.scheduler.asyncRepeating(new CacheHousekeepingTask(this), 2400L);
|
||||
|
||||
// register permissions
|
||||
try {
|
||||
@@ -283,59 +303,59 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
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));
|
||||
pm.addPermission(new 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)));
|
||||
this.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, () -> this.scheduler.setUseFallback(false));
|
||||
|
||||
// Load any online users (in the case of a reload)
|
||||
for (Player player : getServer().getOnlinePlayers()) {
|
||||
scheduler.doAsync(() -> {
|
||||
LoginHelper.loadUser(this, player.getUniqueId(), player.getName(), false);
|
||||
User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
|
||||
if (user != null) {
|
||||
scheduler.doSync(() -> {
|
||||
try {
|
||||
LPPermissible lpPermissible = new LPPermissible(player, user, this);
|
||||
Injector.inject(player, lpPermissible);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
});
|
||||
this.scheduler.doAsync(() -> {
|
||||
try {
|
||||
User user = connectionListener.loadUser(player.getUniqueId(), player.getName());
|
||||
if (user != null) {
|
||||
this.scheduler.doSync(() -> {
|
||||
try {
|
||||
LPPermissible lpPermissible = new LPPermissible(player, user, this);
|
||||
PermissibleInjector.inject(player, lpPermissible);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getLog().info("Successfully enabled. (took " + (System.currentTimeMillis() - startTime) + "ms)");
|
||||
getLog().info("Successfully enabled. (took " + (System.currentTimeMillis() - this.startTime) + "ms)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (checkInvalidVersion()) {
|
||||
if (this.invalidVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch back to the LP executor, the bukkit one won't allow new tasks
|
||||
scheduler.setUseBukkitAsync(false);
|
||||
// Switch back to the fallback executor, the bukkit one won't allow new tasks
|
||||
this.scheduler.setUseFallback(true);
|
||||
|
||||
started = false;
|
||||
|
||||
defaultsProvider.close();
|
||||
permissionVault.shutdown();
|
||||
verboseHandler.shutdown();
|
||||
this.permissionVault.shutdown();
|
||||
this.verboseHandler.shutdown();
|
||||
|
||||
// uninject from players
|
||||
for (Player player : getServer().getOnlinePlayers()) {
|
||||
try {
|
||||
Injector.unInject(player, false);
|
||||
PermissibleInjector.unInject(player, false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -344,37 +364,39 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
player.setOp(false);
|
||||
}
|
||||
|
||||
final User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId()));
|
||||
final User user = getUserManager().getIfLoaded(player.getUniqueId());
|
||||
if (user != null) {
|
||||
user.getUserData().invalidateCaches();
|
||||
user.getCachedData().invalidateCaches();
|
||||
getUserManager().unload(user);
|
||||
}
|
||||
}
|
||||
|
||||
// uninject subscription map
|
||||
SubscriptionMapInjector.uninject();
|
||||
// uninject custom maps
|
||||
InjectorSubscriptionMap.uninject();
|
||||
InjectorPermissionMap.uninject();
|
||||
InjectorDefaultsMap.uninject();
|
||||
|
||||
getLog().info("Closing storage...");
|
||||
storage.shutdown();
|
||||
this.storage.shutdown();
|
||||
|
||||
if (fileWatcher != null) {
|
||||
fileWatcher.close();
|
||||
if (this.fileWatcher != null) {
|
||||
this.fileWatcher.close();
|
||||
}
|
||||
|
||||
if (messagingService != null) {
|
||||
if (this.messagingService != null) {
|
||||
getLog().info("Closing messaging service...");
|
||||
messagingService.close();
|
||||
this.messagingService.close();
|
||||
}
|
||||
|
||||
ApiSingletonUtils.unregisterProvider();
|
||||
ApiRegistrationUtil.unregisterProvider();
|
||||
getServer().getServicesManager().unregisterAll(this);
|
||||
|
||||
if (vaultHookManager != null) {
|
||||
vaultHookManager.unhook(this);
|
||||
if (this.vaultHookManager != null) {
|
||||
this.vaultHookManager.unhook(this);
|
||||
}
|
||||
|
||||
getLog().info("Shutting down internal scheduler...");
|
||||
scheduler.shutdown();
|
||||
this.scheduler.shutdown();
|
||||
|
||||
// Bukkit will do this again when #onDisable completes, but we do it early to prevent NPEs elsewhere.
|
||||
getServer().getScheduler().cancelTasks(this);
|
||||
@@ -383,18 +405,18 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
|
||||
public void tryVaultHook(boolean force) {
|
||||
if (vaultHookManager != null) {
|
||||
if (this.vaultHookManager != null) {
|
||||
return; // already hooked
|
||||
}
|
||||
|
||||
try {
|
||||
if (force || getServer().getPluginManager().isPluginEnabled("Vault")) {
|
||||
vaultHookManager = new VaultHookManager();
|
||||
vaultHookManager.hook(this);
|
||||
this.vaultHookManager = new VaultHookManager();
|
||||
this.vaultHookManager.hook(this);
|
||||
getLog().info("Registered Vault permission & chat hook.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
vaultHookManager = null;
|
||||
this.vaultHookManager = null;
|
||||
getLog().severe("Error occurred whilst hooking into Vault.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -406,19 +428,62 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
|
||||
if (getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||
Map<String, Boolean> backing = user.getUserData().getPermissionData(contextManager.getApplicableContexts(player)).getImmutableBacking();
|
||||
Map<String, Boolean> backing = user.getCachedData().getPermissionData(this.contextManager.getApplicableContexts(player)).getImmutableBacking();
|
||||
boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false);
|
||||
player.setOp(op);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ExtendedMessagingService> getMessagingService() {
|
||||
return Optional.ofNullable(messagingService);
|
||||
private File resolveConfig(String file) {
|
||||
File configFile = new File(getDataFolder(), file);
|
||||
|
||||
if (!configFile.exists()) {
|
||||
getDataFolder().mkdirs();
|
||||
saveResource("config.yml", false);
|
||||
}
|
||||
|
||||
return configFile;
|
||||
}
|
||||
|
||||
public LPSubscriptionMap getSubscriptionMap() {
|
||||
return this.subscriptionMap;
|
||||
}
|
||||
|
||||
public void setSubscriptionMap(LPSubscriptionMap subscriptionMap) {
|
||||
this.subscriptionMap = subscriptionMap;
|
||||
}
|
||||
|
||||
public LPPermissionMap getPermissionMap() {
|
||||
return this.permissionMap;
|
||||
}
|
||||
|
||||
public void setPermissionMap(LPPermissionMap permissionMap) {
|
||||
this.permissionMap = permissionMap;
|
||||
}
|
||||
|
||||
public LPDefaultsMap getDefaultPermissionMap() {
|
||||
return this.defaultPermissionMap;
|
||||
}
|
||||
|
||||
public void setDefaultPermissionMap(LPDefaultsMap defaultPermissionMap) {
|
||||
this.defaultPermissionMap = defaultPermissionMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InternalMessagingService> getMessagingService() {
|
||||
return Optional.ofNullable(this.messagingService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessagingService(InternalMessagingService messagingService) {
|
||||
if (this.messagingService == null) {
|
||||
this.messagingService = messagingService;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<FileWatcher> getFileWatcher() {
|
||||
return Optional.ofNullable(fileWatcher);
|
||||
return Optional.ofNullable(this.fileWatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -432,7 +497,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
public String getServerBrand() {
|
||||
return getServer().getName();
|
||||
}
|
||||
|
||||
@@ -441,6 +506,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
return getServer().getVersion() + " - " + getServer().getBukkitVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
return getServer().getServerName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataDirectory() {
|
||||
return super.getDataFolder();
|
||||
@@ -453,7 +523,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
|
||||
@Override
|
||||
public Player getPlayer(User user) {
|
||||
return getServer().getPlayer(uuidCache.getExternalUUID(user.getUuid()));
|
||||
return getServer().getPlayer(user.getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -468,12 +538,12 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Contexts getContextForUser(User user) {
|
||||
public Optional<Contexts> getContextForUser(User user) {
|
||||
Player player = getPlayer(user);
|
||||
if (player == null) {
|
||||
return null;
|
||||
return Optional.empty();
|
||||
}
|
||||
return contextManager.getApplicableContexts(player);
|
||||
return Optional.of(this.contextManager.getApplicableContexts(player));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -510,15 +580,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
return getSenderFactory().wrap(getServer().getConsoleSender());
|
||||
}
|
||||
|
||||
@Override
|
||||
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());
|
||||
return map;
|
||||
}
|
||||
|
||||
private static boolean checkInvalidVersion() {
|
||||
try {
|
||||
Class.forName("com.google.gson.JsonElement");
|
||||
@@ -527,4 +588,131 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return this.startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitSchedulerAdapter getScheduler() {
|
||||
return this.scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitCommandExecutor getCommandManager() {
|
||||
return this.commandManager;
|
||||
}
|
||||
|
||||
public VaultHookManager getVaultHookManager() {
|
||||
return this.vaultHookManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuckPermsConfiguration getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardUserManager getUserManager() {
|
||||
return this.userManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardGroupManager getGroupManager() {
|
||||
return this.groupManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StandardTrackManager getTrackManager() {
|
||||
return this.trackManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Storage getStorage() {
|
||||
return this.storage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuckPermsApiProvider getApiProvider() {
|
||||
return this.apiProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventFactory getEventFactory() {
|
||||
return this.eventFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLog() {
|
||||
return this.log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocaleManager getLocaleManager() {
|
||||
return this.localeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginClassLoader getPluginClassLoader() {
|
||||
return this.pluginClassLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyManager getDependencyManager() {
|
||||
return this.dependencyManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedStateManager getCachedStateManager() {
|
||||
return this.cachedStateManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextManager<Player> getContextManager() {
|
||||
return this.contextManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InheritanceHandler getInheritanceHandler() {
|
||||
return this.inheritanceHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatorFactory getCalculatorFactory() {
|
||||
return this.calculatorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedRequest<Void> getUpdateTaskBuffer() {
|
||||
return this.updateTaskBuffer;
|
||||
}
|
||||
|
||||
public CountDownLatch getEnableLatch() {
|
||||
return this.enableLatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VerboseHandler getVerboseHandler() {
|
||||
return this.verboseHandler;
|
||||
}
|
||||
|
||||
public BukkitSenderFactory getSenderFactory() {
|
||||
return this.senderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionVault getPermissionVault() {
|
||||
return this.permissionVault;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogDispatcher getLogDispatcher() {
|
||||
return this.logDispatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getUniqueConnections() {
|
||||
return this.uniqueConnections;
|
||||
}
|
||||
}
|
||||
+13
-27
@@ -25,8 +25,6 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.calculators;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
@@ -37,53 +35,41 @@ import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
|
||||
import me.lucko.luckperms.common.calculators.PermissionCalculator;
|
||||
import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.processors.MapProcessor;
|
||||
import me.lucko.luckperms.common.processors.PermissionProcessor;
|
||||
import me.lucko.luckperms.common.processors.RegexProcessor;
|
||||
import me.lucko.luckperms.common.processors.WildcardProcessor;
|
||||
import me.lucko.luckperms.common.references.HolderType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
public BukkitCalculatorFactory(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionCalculator build(Contexts contexts, User user) {
|
||||
public PermissionCalculator build(Contexts contexts, PermissionCalculatorMetadata metadata) {
|
||||
ImmutableList.Builder<PermissionProcessor> processors = ImmutableList.builder();
|
||||
|
||||
processors.add(new MapProcessor());
|
||||
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_CHILD_PERMISSIONS)) {
|
||||
processors.add(new ChildProcessor(plugin.getChildPermissionProvider()));
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_CHILD_PERMISSIONS)) {
|
||||
processors.add(new ChildProcessor(this.plugin));
|
||||
}
|
||||
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) {
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) {
|
||||
processors.add(new RegexProcessor());
|
||||
}
|
||||
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) {
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) {
|
||||
processors.add(new WildcardProcessor());
|
||||
}
|
||||
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
processors.add(new DefaultsProcessor(contexts.isOp(), plugin.getDefaultsProvider()));
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS) && metadata.getHolderType() == HolderType.USER) {
|
||||
processors.add(new DefaultsProcessor(this.plugin, contexts.isOp()));
|
||||
}
|
||||
|
||||
PermissionCalculatorMetadata meta = PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts());
|
||||
return registerCalculator(new PermissionCalculator(plugin, meta, processors.build()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getActiveProcessors() {
|
||||
ImmutableList.Builder<String> ret = ImmutableList.builder();
|
||||
ret.add("Map");
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_CHILD_PERMISSIONS)) ret.add("Child");
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) ret.add("Attachment");
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) ret.add("Regex");
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) ret.add("Wildcard");
|
||||
if (plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) ret.add("Defaults");
|
||||
return ret.build();
|
||||
return registerCalculator(new PermissionCalculator(this.plugin, metadata, processors.build()));
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* 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.bukkit.compat;
|
||||
|
||||
import me.lucko.luckperms.common.utils.TextUtils;
|
||||
|
||||
import net.kyori.text.Component;
|
||||
import net.kyori.text.serializer.ComponentSerializers;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Sends a json message to a command sender.
|
||||
*/
|
||||
public class MessageHandler {
|
||||
private final BukkitJsonMessageHandler bukkitHandler;
|
||||
private final SpigotJsonMessageHandler spigotHandler;
|
||||
|
||||
public MessageHandler() {
|
||||
bukkitHandler = new BukkitJsonMessageHandler();
|
||||
spigotHandler = isSpigot() ? new SpigotJsonMessageHandler() : null;
|
||||
}
|
||||
|
||||
public void sendJsonMessage(CommandSender sender, Component message) {
|
||||
if (ReflectionUtil.isChatCompatible() && sender instanceof Player) {
|
||||
Player player = (Player) sender;
|
||||
String json = ComponentSerializers.JSON.serialize(message);
|
||||
|
||||
// Try Bukkit.
|
||||
if (bukkitHandler.sendJsonMessage(player, json)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try Spigot.
|
||||
if (spigotHandler != null && spigotHandler.sendJsonMessage(player, json)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to Bukkit
|
||||
sender.sendMessage(TextUtils.toLegacy(message));
|
||||
}
|
||||
|
||||
private static boolean isSpigot() {
|
||||
try {
|
||||
Class.forName("net.md_5.bungee.chat.ComponentSerializer");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,12 +25,9 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.compat;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
@UtilityClass
|
||||
public class ReflectionUtil {
|
||||
public final class ReflectionUtil {
|
||||
private static final String SERVER_VERSION = _getServerVersion();
|
||||
private static final boolean CHAT_COMPATIBLE = !SERVER_VERSION.startsWith(".v1_7_");
|
||||
|
||||
@@ -48,7 +45,7 @@ public class ReflectionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getServerVersion() {
|
||||
private static String getServerVersion() {
|
||||
return SERVER_VERSION;
|
||||
}
|
||||
|
||||
@@ -71,4 +68,7 @@ public class ReflectionUtil {
|
||||
public static Class<?> obcClass(String className) throws ClassNotFoundException {
|
||||
return Class.forName(obc(className));
|
||||
}
|
||||
|
||||
private ReflectionUtil() {}
|
||||
|
||||
}
|
||||
@@ -42,11 +42,11 @@ public class BukkitContextManager extends AbstractContextManager<Player> {
|
||||
public Contexts formContexts(Player subject, ImmutableContextSet contextSet) {
|
||||
return new Contexts(
|
||||
contextSet,
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
this.plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_PERMS),
|
||||
this.plugin.getConfiguration().get(ConfigKeys.INCLUDING_GLOBAL_WORLD_PERMS),
|
||||
true,
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
this.plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_GROUPS),
|
||||
this.plugin.getConfiguration().get(ConfigKeys.APPLYING_GLOBAL_WORLD_GROUPS),
|
||||
subject.isOp()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.contexts;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.context.ContextCalculator;
|
||||
import me.lucko.luckperms.api.context.MutableContextSet;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
@@ -34,18 +33,22 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class WorldCalculator implements ContextCalculator<Player> {
|
||||
private static final String WORLD_KEY = "world";
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class WorldCalculator implements ContextCalculator<Player> {
|
||||
private final LuckPermsPlugin plugin;
|
||||
|
||||
public WorldCalculator(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MutableContextSet giveApplicableContext(Player subject, MutableContextSet accumulator) {
|
||||
public MutableContextSet giveApplicableContext(@Nonnull Player subject, @Nonnull MutableContextSet accumulator) {
|
||||
String world = subject.getWorld().getName().toLowerCase();
|
||||
while (!accumulator.has(WORLD_KEY, world)) {
|
||||
accumulator.add(WORLD_KEY, world);
|
||||
world = plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).getOrDefault(world, world).toLowerCase();
|
||||
while (!accumulator.has(Contexts.WORLD_KEY, world)) {
|
||||
accumulator.add(Contexts.WORLD_KEY, world);
|
||||
world = this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).getOrDefault(world, world).toLowerCase();
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
|
||||
+46
-59
@@ -25,15 +25,13 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.listeners;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.bukkit.model.Injector;
|
||||
import me.lucko.luckperms.bukkit.model.LPPermissible;
|
||||
import me.lucko.luckperms.bukkit.model.permissible.LPPermissible;
|
||||
import me.lucko.luckperms.bukkit.model.permissible.PermissibleInjector;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.utils.LoginHelper;
|
||||
import me.lucko.luckperms.common.utils.AbstractLoginListener;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -49,13 +47,17 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitConnectionListener implements Listener {
|
||||
public class BukkitConnectionListener extends AbstractLoginListener implements Listener {
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
private final Set<UUID> deniedAsyncLogin = Collections.synchronizedSet(new HashSet<>());
|
||||
private final Set<UUID> deniedLogin = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
public BukkitConnectionListener(LPBukkitPlugin plugin) {
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) {
|
||||
/* Called when the player first attempts a connection with the server.
|
||||
@@ -64,31 +66,16 @@ public class BukkitConnectionListener implements Listener {
|
||||
/* wait for the plugin to enable. because these events are fired async, they can be called before
|
||||
the plugin has enabled. */
|
||||
try {
|
||||
plugin.getEnableLatch().await(30, TimeUnit.SECONDS);
|
||||
this.plugin.getEnableLatch().await(60, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||
plugin.getLog().info("Processing pre-login for " + e.getUniqueId() + " - " + e.getName());
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||
this.plugin.getLog().info("Processing pre-login for " + e.getUniqueId() + " - " + e.getName());
|
||||
}
|
||||
|
||||
/* there was an issue connecting to the DB, performing file i/o, etc.
|
||||
we don't let players join in this case, because it means they can connect to the server without their permissions data.
|
||||
some server admins rely on negating perms to stop users from causing damage etc, so it's really important that
|
||||
this data is loaded. */
|
||||
if (!plugin.isStarted() || !plugin.getStorage().isAcceptingLogins()) {
|
||||
|
||||
// log that the user tried to login, but was denied at this stage.
|
||||
deniedAsyncLogin.add(e.getUniqueId());
|
||||
|
||||
// actually deny the connection.
|
||||
plugin.getLog().warn("Permissions storage is not loaded. Denying connection from: " + e.getUniqueId() + " - " + e.getName());
|
||||
e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.asString(plugin.getLocaleManager()));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getUniqueConnections().add(e.getUniqueId());
|
||||
this.plugin.getUniqueConnections().add(e.getUniqueId());
|
||||
|
||||
/* Actually process the login for the connection.
|
||||
We do this here to delay the login until the data is ready.
|
||||
@@ -100,14 +87,15 @@ public class BukkitConnectionListener implements Listener {
|
||||
- creating a user instance in the UserManager for this connection.
|
||||
- setting up cached data. */
|
||||
try {
|
||||
User user = LoginHelper.loadUser(plugin, e.getUniqueId(), e.getName(), false);
|
||||
plugin.getApiProvider().getEventFactory().handleUserLoginProcess(e.getUniqueId(), e.getName(), user);
|
||||
User user = loadUser(e.getUniqueId(), e.getName());
|
||||
this.plugin.getEventFactory().handleUserLoginProcess(e.getUniqueId(), e.getName(), user);
|
||||
} catch (Exception ex) {
|
||||
this.plugin.getLog().severe("Exception occurred whilst loading data for " + e.getUniqueId() + " - " + e.getName());
|
||||
ex.printStackTrace();
|
||||
|
||||
// there was some error loading data. deny the connection
|
||||
deniedAsyncLogin.add(e.getUniqueId());
|
||||
e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.asString(plugin.getLocaleManager()));
|
||||
// deny the connection
|
||||
this.deniedAsyncLogin.add(e.getUniqueId());
|
||||
e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.asString(this.plugin.getLocaleManager()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,22 +105,14 @@ public class BukkitConnectionListener implements Listener {
|
||||
If the connection was cancelled here, we need to do something to clean up the data that was loaded. */
|
||||
|
||||
// Check to see if this connection was denied at LOW.
|
||||
if (deniedAsyncLogin.remove(e.getUniqueId())) {
|
||||
if (this.deniedAsyncLogin.remove(e.getUniqueId())) {
|
||||
// their data was never loaded at LOW priority, now check to see if they have been magically allowed since then.
|
||||
|
||||
// This is a problem, as they were denied at low priority, but are now being allowed.
|
||||
if (e.getLoginResult() == AsyncPlayerPreLoginEvent.Result.ALLOWED) {
|
||||
plugin.getLog().severe("Player connection was re-allowed for " + e.getUniqueId());
|
||||
this.plugin.getLog().severe("Player connection was re-allowed for " + e.getUniqueId());
|
||||
e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, "");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Login event was cancelled by another plugin, but it wasn't cancelled when we handled it at LOW
|
||||
if (e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
|
||||
// Schedule cleanup of this user.
|
||||
plugin.getUserManager().scheduleUnload(e.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,18 +123,18 @@ public class BukkitConnectionListener implements Listener {
|
||||
|
||||
final Player player = e.getPlayer();
|
||||
|
||||
if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||
plugin.getLog().info("Processing login for " + player.getUniqueId() + " - " + player.getName());
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
|
||||
this.plugin.getLog().info("Processing login for " + player.getUniqueId() + " - " + player.getName());
|
||||
}
|
||||
|
||||
final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(player.getUniqueId()));
|
||||
final User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
|
||||
|
||||
/* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
|
||||
if (user == null) {
|
||||
deniedLogin.add(e.getPlayer().getUniqueId());
|
||||
this.deniedLogin.add(e.getPlayer().getUniqueId());
|
||||
|
||||
plugin.getLog().warn("User " + player.getUniqueId() + " - " + player.getName() + " doesn't have data pre-loaded. - denying login.");
|
||||
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.asString(plugin.getLocaleManager()));
|
||||
this.plugin.getLog().warn("User " + player.getUniqueId() + " - " + player.getName() + " doesn't have data pre-loaded. - denying login.");
|
||||
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.asString(this.plugin.getLocaleManager()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -162,16 +142,16 @@ public class BukkitConnectionListener implements Listener {
|
||||
// Care should be taken at this stage to ensure that async tasks which manipulate bukkit data check that the player is still online.
|
||||
try {
|
||||
// Make a new permissible for the user
|
||||
LPPermissible lpPermissible = new LPPermissible(player, user, plugin);
|
||||
LPPermissible lpPermissible = new LPPermissible(player, user, this.plugin);
|
||||
|
||||
// Inject into the player
|
||||
Injector.inject(player, lpPermissible);
|
||||
PermissibleInjector.inject(player, lpPermissible);
|
||||
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
plugin.refreshAutoOp(user, player);
|
||||
this.plugin.refreshAutoOp(user, player);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
@@ -181,25 +161,23 @@ public class BukkitConnectionListener implements Listener {
|
||||
|
||||
// Check to see if this connection was denied at LOW. Even if it was denied at LOW, their data will still be present.
|
||||
boolean denied = false;
|
||||
if (deniedLogin.remove(e.getPlayer().getUniqueId())) {
|
||||
if (this.deniedLogin.remove(e.getPlayer().getUniqueId())) {
|
||||
denied = true;
|
||||
|
||||
// This is a problem, as they were denied at low priority, but are now being allowed.
|
||||
if (e.getResult() == PlayerLoginEvent.Result.ALLOWED) {
|
||||
plugin.getLog().severe("Player connection was re-allowed for " + e.getPlayer().getUniqueId());
|
||||
this.plugin.getLog().severe("Player connection was re-allowed for " + e.getPlayer().getUniqueId());
|
||||
e.disallow(PlayerLoginEvent.Result.KICK_OTHER, "");
|
||||
}
|
||||
}
|
||||
|
||||
// Login event was cancelled by another plugin since we first loaded their data
|
||||
if (denied || e.getResult() != PlayerLoginEvent.Result.ALLOWED) {
|
||||
// Schedule cleanup of this user.
|
||||
plugin.getUserManager().scheduleUnload(e.getPlayer().getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
// everything is going well. login was processed ok, this is just to refresh auto-op status.
|
||||
plugin.refreshAutoOp(plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer());
|
||||
this.plugin.refreshAutoOp(this.plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer());
|
||||
}
|
||||
|
||||
// Wait until the last priority to unload, so plugins can still perform permission checks on this event
|
||||
@@ -209,18 +187,27 @@ public class BukkitConnectionListener implements Listener {
|
||||
|
||||
// Remove the custom permissible
|
||||
try {
|
||||
Injector.unInject(player, true);
|
||||
PermissibleInjector.unInject(player, true);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
// Handle auto op
|
||||
if (plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) {
|
||||
player.setOp(false);
|
||||
}
|
||||
|
||||
// Request that the users data is unloaded.
|
||||
plugin.getUserManager().scheduleUnload(player.getUniqueId());
|
||||
// Register with the housekeeper, so the User's instance will stick
|
||||
// around for a bit after they disconnect
|
||||
this.plugin.getUserManager().getHouseKeeper().registerUsage(player.getUniqueId());
|
||||
|
||||
// force a clear of transient nodes
|
||||
this.plugin.getScheduler().doAsync(() -> {
|
||||
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
|
||||
if (user != null) {
|
||||
user.clearTransientNodes();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+9
-8
@@ -25,8 +25,6 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.listeners;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.locale.Message;
|
||||
@@ -42,10 +40,13 @@ import org.bukkit.event.server.PluginEnableEvent;
|
||||
import org.bukkit.event.server.RemoteServerCommandEvent;
|
||||
import org.bukkit.event.server.ServerCommandEvent;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitPlatformListener implements Listener {
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
public BukkitPlatformListener(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
|
||||
handleCommand(e.getPlayer(), e.getMessage().toLowerCase(), e);
|
||||
@@ -66,7 +67,7 @@ public class BukkitPlatformListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
|
||||
if (this.plugin.getConfiguration().get(ConfigKeys.OPS_ENABLED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,21 +81,21 @@ public class BukkitPlatformListener implements Listener {
|
||||
|
||||
if (s.equals("op") || s.startsWith("op ") || s.equals("deop") || s.startsWith("deop ")) {
|
||||
event.setCancelled(true);
|
||||
sender.sendMessage(Message.OP_DISABLED.asString(plugin.getLocaleManager()));
|
||||
sender.sendMessage(Message.OP_DISABLED.asString(this.plugin.getLocaleManager()));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPluginEnable(PluginEnableEvent e) {
|
||||
if (e.getPlugin().getName().equalsIgnoreCase("Vault")) {
|
||||
plugin.tryVaultHook(true);
|
||||
this.plugin.tryVaultHook(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onWorldChange(PlayerChangedWorldEvent e) {
|
||||
plugin.getContextManager().invalidateCache(e.getPlayer());
|
||||
plugin.refreshAutoOp(plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer());
|
||||
this.plugin.getContextManager().invalidateCache(e.getPlayer());
|
||||
this.plugin.refreshAutoOp(this.plugin.getUserManager().getIfLoaded(e.getPlayer().getUniqueId()), e.getPlayer());
|
||||
}
|
||||
|
||||
}
|
||||
+52
-8
@@ -25,31 +25,75 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.messaging;
|
||||
|
||||
import me.lucko.luckperms.api.messenger.IncomingMessageConsumer;
|
||||
import me.lucko.luckperms.api.messenger.Messenger;
|
||||
import me.lucko.luckperms.api.messenger.MessengerProvider;
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
|
||||
import me.lucko.luckperms.common.messaging.InternalMessagingService;
|
||||
import me.lucko.luckperms.common.messaging.LuckPermsMessagingService;
|
||||
import me.lucko.luckperms.common.messaging.MessagingFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BukkitMessagingFactory extends MessagingFactory<LPBukkitPlugin> {
|
||||
public BukkitMessagingFactory(LPBukkitPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ExtendedMessagingService getServiceFor(String messagingType) {
|
||||
protected InternalMessagingService getServiceFor(String messagingType) {
|
||||
if (messagingType.equals("bungee")) {
|
||||
BungeeMessagingService bungeeMessaging = new BungeeMessagingService(getPlugin());
|
||||
bungeeMessaging.init();
|
||||
return bungeeMessaging;
|
||||
try {
|
||||
return new LuckPermsMessagingService(getPlugin(), new BungeeMessengerProvider());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (messagingType.equals("lilypad")) {
|
||||
if (getPlugin().getServer().getPluginManager().getPlugin("LilyPad-Connect") == null) {
|
||||
getPlugin().getLog().warn("LilyPad-Connect plugin not present.");
|
||||
} else {
|
||||
LilyPadMessagingService lilyPadMessaging = new LilyPadMessagingService(getPlugin());
|
||||
lilyPadMessaging.init();
|
||||
return lilyPadMessaging;
|
||||
try {
|
||||
return new LuckPermsMessagingService(getPlugin(), new LilyPadMessengerProvider());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.getServiceFor(messagingType);
|
||||
}
|
||||
|
||||
private class BungeeMessengerProvider implements MessengerProvider {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Bungee";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer) {
|
||||
BungeeMessenger bungeeMessaging = new BungeeMessenger(getPlugin(), incomingMessageConsumer);
|
||||
bungeeMessaging.init();
|
||||
return bungeeMessaging;
|
||||
}
|
||||
}
|
||||
|
||||
private class LilyPadMessengerProvider implements MessengerProvider {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LilyPad";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Messenger obtain(@Nonnull IncomingMessageConsumer incomingMessageConsumer) {
|
||||
LilyPadMessenger lilyPadMessaging = new LilyPadMessenger(getPlugin(), incomingMessageConsumer);
|
||||
lilyPadMessaging.init();
|
||||
return lilyPadMessaging;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-19
@@ -30,9 +30,10 @@ import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import me.lucko.luckperms.api.messenger.IncomingMessageConsumer;
|
||||
import me.lucko.luckperms.api.messenger.Messenger;
|
||||
import me.lucko.luckperms.api.messenger.message.OutgoingMessage;
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.messaging.AbstractMessagingService;
|
||||
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
@@ -40,48 +41,53 @@ import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ExtendedMessagingService} using the plugin messaging channels.
|
||||
*/
|
||||
public class BungeeMessagingService extends AbstractMessagingService implements PluginMessageListener {
|
||||
private final LPBukkitPlugin plugin;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public BungeeMessagingService(LPBukkitPlugin plugin) {
|
||||
super(plugin, "Bungee");
|
||||
/**
|
||||
* An implementation of {@link Messenger} using the plugin messaging channels.
|
||||
*/
|
||||
public class BungeeMessenger implements Messenger, PluginMessageListener {
|
||||
private static final String CHANNEL = "lpuc";
|
||||
|
||||
private final LPBukkitPlugin plugin;
|
||||
private final IncomingMessageConsumer consumer;
|
||||
|
||||
public BungeeMessenger(LPBukkitPlugin plugin, IncomingMessageConsumer consumer) {
|
||||
this.plugin = plugin;
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, CHANNEL);
|
||||
plugin.getServer().getMessenger().registerIncomingPluginChannel(plugin, CHANNEL, this);
|
||||
this.plugin.getServer().getMessenger().registerOutgoingPluginChannel(this.plugin, CHANNEL);
|
||||
this.plugin.getServer().getMessenger().registerIncomingPluginChannel(this.plugin, CHANNEL, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
plugin.getServer().getMessenger().unregisterIncomingPluginChannel(plugin, CHANNEL);
|
||||
plugin.getServer().getMessenger().unregisterOutgoingPluginChannel(plugin, CHANNEL);
|
||||
this.plugin.getServer().getMessenger().unregisterIncomingPluginChannel(this.plugin, CHANNEL);
|
||||
this.plugin.getServer().getMessenger().unregisterOutgoingPluginChannel(this.plugin, CHANNEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendMessage(String message) {
|
||||
public void sendOutgoingMessage(@Nonnull OutgoingMessage outgoingMessage) {
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Collection<? extends Player> players = plugin.getServer().getOnlinePlayers();
|
||||
Collection<? extends Player> players = BungeeMessenger.this.plugin.getServer().getOnlinePlayers();
|
||||
Player p = Iterables.getFirst(players, null);
|
||||
if (p == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF(message);
|
||||
out.writeUTF(outgoingMessage.asEncodedString());
|
||||
|
||||
byte[] data = out.toByteArray();
|
||||
|
||||
p.sendPluginMessage(plugin, CHANNEL, data);
|
||||
p.sendPluginMessage(BungeeMessenger.this.plugin, CHANNEL, data);
|
||||
cancel();
|
||||
}
|
||||
}.runTaskTimer(plugin, 1L, 100L);
|
||||
}.runTaskTimer(this.plugin, 1L, 100L);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,6 +99,6 @@ public class BungeeMessagingService extends AbstractMessagingService implements
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
|
||||
String msg = in.readUTF();
|
||||
|
||||
onMessage(msg, null);
|
||||
this.consumer.consumeIncomingMessageAsString(msg);
|
||||
}
|
||||
}
|
||||
+21
-15
@@ -25,9 +25,10 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.messaging;
|
||||
|
||||
import me.lucko.luckperms.api.messenger.IncomingMessageConsumer;
|
||||
import me.lucko.luckperms.api.messenger.Messenger;
|
||||
import me.lucko.luckperms.api.messenger.message.OutgoingMessage;
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.common.messaging.AbstractMessagingService;
|
||||
import me.lucko.luckperms.common.messaging.ExtendedMessagingService;
|
||||
|
||||
import lilypad.client.connect.api.Connect;
|
||||
import lilypad.client.connect.api.event.EventListener;
|
||||
@@ -38,41 +39,47 @@ import lilypad.client.connect.api.request.impl.MessageRequest;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collections;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ExtendedMessagingService} using LilyPad.
|
||||
* An implementation of {@link Messenger} using LilyPad.
|
||||
*/
|
||||
public class LilyPadMessagingService extends AbstractMessagingService {
|
||||
public class LilyPadMessenger implements Messenger {
|
||||
private static final String CHANNEL = "lpuc";
|
||||
|
||||
private final LPBukkitPlugin plugin;
|
||||
private final IncomingMessageConsumer consumer;
|
||||
|
||||
private Connect connect;
|
||||
|
||||
public LilyPadMessagingService(LPBukkitPlugin plugin) {
|
||||
super(plugin, "LilyPad");
|
||||
public LilyPadMessenger(LPBukkitPlugin plugin, IncomingMessageConsumer consumer) {
|
||||
this.plugin = plugin;
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
connect = plugin.getServer().getServicesManager().getRegistration(Connect.class).getProvider();
|
||||
connect.registerEvents(this);
|
||||
this.connect = this.plugin.getServer().getServicesManager().getRegistration(Connect.class).getProvider();
|
||||
this.connect.registerEvents(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
connect.unregisterEvents(this);
|
||||
this.connect.unregisterEvents(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendMessage(String message) {
|
||||
public void sendOutgoingMessage(@Nonnull OutgoingMessage outgoingMessage) {
|
||||
MessageRequest request;
|
||||
|
||||
try {
|
||||
request = new MessageRequest(Collections.emptyList(), CHANNEL, message);
|
||||
request = new MessageRequest(Collections.emptyList(), CHANNEL, outgoingMessage.asEncodedString());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
connect.request(request);
|
||||
this.connect.request(request);
|
||||
} catch (RequestException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -80,7 +87,7 @@ public class LilyPadMessagingService extends AbstractMessagingService {
|
||||
|
||||
@EventListener
|
||||
public void onMessage(MessageEvent event) {
|
||||
plugin.getScheduler().doAsync(() -> {
|
||||
this.plugin.getScheduler().doAsync(() -> {
|
||||
try {
|
||||
String channel = event.getChannel();
|
||||
|
||||
@@ -89,8 +96,7 @@ public class LilyPadMessagingService extends AbstractMessagingService {
|
||||
}
|
||||
|
||||
String message = event.getMessageAsString();
|
||||
|
||||
onMessage(message, null);
|
||||
this.consumer.consumeIncomingMessageAsString(message);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.bukkit.migration;
|
||||
|
||||
import me.lucko.luckperms.common.logging.ProgressLogger;
|
||||
import me.lucko.luckperms.common.utils.Uuids;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class BukkitMigrationUtils {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static UUID lookupUuid(ProgressLogger log, String s) {
|
||||
UUID uuid = Uuids.parseNullable(s);
|
||||
if (uuid == null) {
|
||||
try {
|
||||
uuid = Bukkit.getOfflinePlayer(s).getUniqueId();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (uuid == null) {
|
||||
log.logErr("Unable to get a UUID for user identifier: " + s);
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
private BukkitMigrationUtils() {}
|
||||
|
||||
}
|
||||
+29
-43
@@ -27,14 +27,12 @@ package me.lucko.luckperms.bukkit.migration;
|
||||
|
||||
import de.bananaco.bpermissions.api.Calculable;
|
||||
import de.bananaco.bpermissions.api.CalculableType;
|
||||
import de.bananaco.bpermissions.api.Group;
|
||||
import de.bananaco.bpermissions.api.Permission;
|
||||
import de.bananaco.bpermissions.api.World;
|
||||
import de.bananaco.bpermissions.api.WorldManager;
|
||||
|
||||
import me.lucko.luckperms.api.ChatMetaType;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.common.commands.CommandException;
|
||||
import me.lucko.luckperms.common.commands.CommandResult;
|
||||
import me.lucko.luckperms.common.commands.abstraction.SubCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.migration.MigrationUtils;
|
||||
@@ -42,13 +40,14 @@ import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.locale.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.logging.ProgressLogger;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.NodeFactory;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.utils.SafeIteration;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
@@ -59,16 +58,16 @@ import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static me.lucko.luckperms.common.constants.CommandPermission.MIGRATION;
|
||||
import static me.lucko.luckperms.common.commands.CommandPermission.MIGRATION;
|
||||
|
||||
public class MigrationBPermissions extends SubCommand<Object> {
|
||||
private static Field uConfigField;
|
||||
private static final Field UCONFIG_FIELD;
|
||||
static {
|
||||
try {
|
||||
uConfigField = Class.forName("de.bananaco.bpermissions.imp.YamlWorld").getDeclaredField("uconfig");
|
||||
uConfigField.setAccessible(true);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
UCONFIG_FIELD = Class.forName("de.bananaco.bpermissions.imp.YamlWorld").getDeclaredField("uconfig");
|
||||
UCONFIG_FIELD.setAccessible(true);
|
||||
} catch (ClassNotFoundException | NoSuchFieldException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +76,7 @@ public class MigrationBPermissions extends SubCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
ProgressLogger log = new ProgressLogger("bPermissions");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
@@ -96,7 +95,7 @@ public class MigrationBPermissions extends SubCommand<Object> {
|
||||
|
||||
YamlConfiguration yamlWorldUsers = null;
|
||||
try {
|
||||
yamlWorldUsers = (YamlConfiguration) uConfigField.get(world);
|
||||
yamlWorldUsers = (YamlConfiguration) UCONFIG_FIELD.get(world);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
@@ -125,21 +124,21 @@ public class MigrationBPermissions extends SubCommand<Object> {
|
||||
|
||||
// Migrate one world at a time.
|
||||
log.log("Starting world migration.");
|
||||
for (World world : worldManager.getAllWorlds()) {
|
||||
SafeIteration.iterate(worldManager.getAllWorlds(), world -> {
|
||||
log.log("Migrating world: " + world.getName());
|
||||
|
||||
// Migrate all groups
|
||||
log.log("Starting group migration in world " + world.getName() + ".");
|
||||
AtomicInteger groupCount = new AtomicInteger(0);
|
||||
for (Calculable group : world.getAll(CalculableType.GROUP)) {
|
||||
|
||||
SafeIteration.iterate(world.getAll(CalculableType.GROUP), group -> {
|
||||
String groupName = MigrationUtils.standardizeName(group.getName());
|
||||
if (group.getName().equalsIgnoreCase(world.getDefaultGroup())) {
|
||||
groupName = "default";
|
||||
groupName = NodeFactory.DEFAULT_GROUP_NAME;
|
||||
}
|
||||
|
||||
// Make a LuckPerms group for the one being migrated.
|
||||
plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
me.lucko.luckperms.common.model.Group lpGroup = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
Group lpGroup = plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
|
||||
MigrationUtils.setGroupWeight(lpGroup, group.getPriority());
|
||||
migrateHolder(world, group, lpGroup);
|
||||
@@ -147,35 +146,22 @@ public class MigrationBPermissions extends SubCommand<Object> {
|
||||
plugin.getStorage().saveGroup(lpGroup);
|
||||
|
||||
log.logAllProgress("Migrated {} groups so far.", groupCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + groupCount.get() + " groups in world " + world.getName() + ".");
|
||||
|
||||
|
||||
// Migrate all users
|
||||
log.log("Starting user migration in world " + world.getName() + ".");
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
for (Calculable user : world.getAll(CalculableType.USER)) {
|
||||
SafeIteration.iterate(world.getAll(CalculableType.USER), user -> {
|
||||
// There is no mention of UUIDs in the API. I assume that name = uuid. idk?
|
||||
UUID uuid = null;
|
||||
try {
|
||||
uuid = UUID.fromString(user.getName());
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
//noinspection deprecation
|
||||
uuid = Bukkit.getOfflinePlayer(user.getName()).getUniqueId();
|
||||
} catch (Exception ex) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
UUID uuid = BukkitMigrationUtils.lookupUuid(log, user.getName());
|
||||
if (uuid == null) {
|
||||
log.logErr("Unable to migrate user " + user.getName() + ". Cannot to get UUID.");
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a LuckPerms user for the one being migrated.
|
||||
plugin.getStorage().loadUser(uuid, null).join();
|
||||
User lpUser = plugin.getUserManager().getIfLoaded(uuid);
|
||||
User lpUser = plugin.getStorage().loadUser(uuid, null).join();
|
||||
|
||||
migrateHolder(world, user, lpUser);
|
||||
|
||||
@@ -183,10 +169,10 @@ public class MigrationBPermissions extends SubCommand<Object> {
|
||||
plugin.getUserManager().cleanup(lpUser);
|
||||
|
||||
log.logProgress("Migrated {} users so far.", userCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
|
||||
log.log("Migrated " + userCount.get() + " users in world " + world.getName() + ".");
|
||||
}
|
||||
});
|
||||
|
||||
log.log("Success! Migration complete.");
|
||||
return CommandResult.SUCCESS;
|
||||
@@ -211,14 +197,14 @@ public class MigrationBPermissions extends SubCommand<Object> {
|
||||
}
|
||||
|
||||
// Migrate any inherited groups
|
||||
for (Group parent : c.getGroups()) {
|
||||
c.getGroups().forEach(parent -> {
|
||||
String parentName = MigrationUtils.standardizeName(parent.getName());
|
||||
if (parent.getName().equalsIgnoreCase(world.getDefaultGroup())) {
|
||||
parentName = "default";
|
||||
parentName = NodeFactory.DEFAULT_GROUP_NAME;
|
||||
}
|
||||
|
||||
holder.setPermission(NodeFactory.make("group." + parentName, true, "global", world.getName()));
|
||||
}
|
||||
holder.setPermission(NodeFactory.make(NodeFactory.groupNode(parentName), true, "global", world.getName()));
|
||||
});
|
||||
|
||||
// Migrate existing meta
|
||||
for (Map.Entry<String, String> meta : c.getMeta().entrySet()) {
|
||||
@@ -226,13 +212,13 @@ public class MigrationBPermissions extends SubCommand<Object> {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (meta.getKey().equalsIgnoreCase("prefix") || meta.getKey().equalsIgnoreCase("suffix")) {
|
||||
if (meta.getKey().equalsIgnoreCase(NodeFactory.PREFIX_KEY) || meta.getKey().equalsIgnoreCase(NodeFactory.SUFFIX_KEY)) {
|
||||
ChatMetaType type = ChatMetaType.valueOf(meta.getKey().toUpperCase());
|
||||
holder.setPermission(NodeFactory.makeChatMetaNode(type, c.getPriority(), meta.getValue()).setWorld(world.getName()).build());
|
||||
holder.setPermission(NodeFactory.buildChatMetaNode(type, c.getPriority(), meta.getValue()).setWorld(world.getName()).build());
|
||||
continue;
|
||||
}
|
||||
|
||||
holder.setPermission(NodeFactory.makeMetaNode(meta.getKey(), meta.getValue()).setWorld(world.getName()).build());
|
||||
holder.setPermission(NodeFactory.buildMetaNode(meta.getKey(), meta.getValue()).setWorld(world.getName()).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
-81
@@ -28,23 +28,23 @@ package me.lucko.luckperms.bukkit.migration;
|
||||
import me.lucko.luckperms.api.ChatMetaType;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.common.commands.CommandException;
|
||||
import me.lucko.luckperms.common.commands.CommandPermission;
|
||||
import me.lucko.luckperms.common.commands.CommandResult;
|
||||
import me.lucko.luckperms.common.commands.abstraction.SubCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.constants.CommandPermission;
|
||||
import me.lucko.luckperms.common.locale.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.logging.ProgressLogger;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.NodeFactory;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.utils.SafeIteration;
|
||||
|
||||
import org.anjocaido.groupmanager.GlobalGroups;
|
||||
import org.anjocaido.groupmanager.GroupManager;
|
||||
import org.anjocaido.groupmanager.data.Group;
|
||||
import org.anjocaido.groupmanager.data.User;
|
||||
import org.anjocaido.groupmanager.dataholder.WorldDataHolder;
|
||||
import org.anjocaido.groupmanager.dataholder.worlds.WorldsHolder;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -66,7 +66,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
ProgressLogger log = new ProgressLogger("GroupManager");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
@@ -93,31 +93,22 @@ public class MigrationGroupManager extends SubCommand<Object> {
|
||||
GlobalGroups gg = GroupManager.getGlobalGroups();
|
||||
|
||||
AtomicInteger globalGroupCount = new AtomicInteger(0);
|
||||
for (Group g : gg.getGroupList()) {
|
||||
SafeIteration.iterate(gg.getGroupList(), g -> {
|
||||
String groupName = MigrationUtils.standardizeName(g.getName());
|
||||
|
||||
plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
me.lucko.luckperms.common.model.Group group = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
Group group = plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
|
||||
for (String node : g.getPermissionList()) {
|
||||
if (node.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.isEmpty()) continue;
|
||||
group.setPermission(MigrationUtils.parseNode(node, true).build());
|
||||
}
|
||||
|
||||
for (String s : g.getInherits()) {
|
||||
if (s.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
group.setPermission(NodeFactory.make("group." + MigrationUtils.standardizeName(s)));
|
||||
if (s.isEmpty()) continue;
|
||||
group.setPermission(NodeFactory.make(NodeFactory.groupNode(MigrationUtils.standardizeName(s))));
|
||||
}
|
||||
|
||||
plugin.getStorage().saveGroup(group);
|
||||
log.logAllProgress("Migrated {} groups so far.", globalGroupCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + globalGroupCount.get() + " global groups");
|
||||
|
||||
// Collect data
|
||||
@@ -129,77 +120,56 @@ public class MigrationGroupManager extends SubCommand<Object> {
|
||||
|
||||
// Collect data for all users and groups.
|
||||
log.log("Collecting user and group data.");
|
||||
for (String world : worlds) {
|
||||
world = world.toLowerCase();
|
||||
SafeIteration.iterate(worlds, String::toLowerCase, world -> {
|
||||
log.log("Querying world " + world);
|
||||
|
||||
WorldDataHolder wdh = wh.getWorldData(world);
|
||||
|
||||
AtomicInteger groupWorldCount = new AtomicInteger(0);
|
||||
for (Group group : wdh.getGroupList()) {
|
||||
SafeIteration.iterate(wdh.getGroupList(), group -> {
|
||||
String groupName = MigrationUtils.standardizeName(group.getName());
|
||||
|
||||
groups.putIfAbsent(groupName, new HashSet<>());
|
||||
|
||||
for (String node : group.getPermissionList()) {
|
||||
if (node.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.isEmpty()) continue;
|
||||
groups.get(groupName).add(MigrationUtils.parseNode(node, true).setWorld(worldMappingFunc.apply(world)).build());
|
||||
}
|
||||
|
||||
for (String s : group.getInherits()) {
|
||||
if (s.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
groups.get(groupName).add(NodeFactory.make("group." + MigrationUtils.standardizeName(s), true, null, worldMappingFunc.apply(world)));
|
||||
if (s.isEmpty()) continue;
|
||||
groups.get(groupName).add(NodeFactory.make(NodeFactory.groupNode(MigrationUtils.standardizeName(s)), true, null, worldMappingFunc.apply(world)));
|
||||
}
|
||||
|
||||
|
||||
String[] metaKeys = group.getVariables().getVarKeyList();
|
||||
for (String key : metaKeys) {
|
||||
String value = group.getVariables().getVarString(key);
|
||||
key = key.toLowerCase();
|
||||
if (key.isEmpty() || value.isEmpty()) continue;
|
||||
if (key.equals("build")) continue;
|
||||
|
||||
if (key.isEmpty() || value.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.equals("build")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.equals("prefix") || key.equals("suffix")) {
|
||||
if (key.equals(NodeFactory.PREFIX_KEY) || key.equals(NodeFactory.SUFFIX_KEY)) {
|
||||
ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase());
|
||||
groups.get(groupName).add(NodeFactory.makeChatMetaNode(type, 50, value).setWorld(worldMappingFunc.apply(world)).build());
|
||||
groups.get(groupName).add(NodeFactory.buildChatMetaNode(type, 50, value).setWorld(worldMappingFunc.apply(world)).build());
|
||||
} else {
|
||||
groups.get(groupName).add(NodeFactory.makeMetaNode(key, value).setWorld(worldMappingFunc.apply(world)).build());
|
||||
groups.get(groupName).add(NodeFactory.buildMetaNode(key, value).setWorld(worldMappingFunc.apply(world)).build());
|
||||
}
|
||||
}
|
||||
|
||||
log.logAllProgress("Migrated {} groups so far in world " + world, groupWorldCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + groupWorldCount.get() + " groups in world " + world);
|
||||
|
||||
AtomicInteger userWorldCount = new AtomicInteger(0);
|
||||
for (User user : wdh.getUserList()) {
|
||||
UUID uuid;
|
||||
try {
|
||||
uuid = UUID.fromString(user.getUUID());
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.logErr("Could not parse UUID for user: " + user.getUUID());
|
||||
continue;
|
||||
SafeIteration.iterate(wdh.getUserList(), user -> {
|
||||
UUID uuid = BukkitMigrationUtils.lookupUuid(log, user.getUUID());
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
users.putIfAbsent(uuid, new HashSet<>());
|
||||
|
||||
for (String node : user.getPermissionList()) {
|
||||
if (node.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.isEmpty()) continue;
|
||||
users.get(uuid).add(MigrationUtils.parseNode(node, true).setWorld(worldMappingFunc.apply(world)).build());
|
||||
}
|
||||
|
||||
@@ -207,7 +177,7 @@ public class MigrationGroupManager extends SubCommand<Object> {
|
||||
String finalWorld = worldMappingFunc.apply(world);
|
||||
users.get(uuid).addAll(user.subGroupListStringCopy().stream()
|
||||
.filter(n -> !n.isEmpty())
|
||||
.map(n -> "group." + MigrationUtils.standardizeName(n))
|
||||
.map(n -> NodeFactory.groupNode(MigrationUtils.standardizeName(n)))
|
||||
.map(n -> NodeFactory.make(n, true, null, finalWorld))
|
||||
.collect(Collectors.toSet())
|
||||
);
|
||||
@@ -219,36 +189,29 @@ public class MigrationGroupManager extends SubCommand<Object> {
|
||||
for (String key : metaKeys) {
|
||||
String value = user.getVariables().getVarString(key);
|
||||
key = key.toLowerCase();
|
||||
if (key.isEmpty() || value.isEmpty()) continue;
|
||||
if (key.equals("build")) continue;
|
||||
|
||||
if (key.isEmpty() || value.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.equals("build")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.equals("prefix") || key.equals("suffix")) {
|
||||
if (key.equals(NodeFactory.PREFIX_KEY) || key.equals(NodeFactory.SUFFIX_KEY)) {
|
||||
ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase());
|
||||
users.get(uuid).add(NodeFactory.makeChatMetaNode(type, 100, value).setWorld(worldMappingFunc.apply(world)).build());
|
||||
users.get(uuid).add(NodeFactory.buildChatMetaNode(type, 100, value).setWorld(worldMappingFunc.apply(world)).build());
|
||||
} else {
|
||||
users.get(uuid).add(NodeFactory.makeMetaNode(key, value).setWorld(worldMappingFunc.apply(world)).build());
|
||||
users.get(uuid).add(NodeFactory.buildMetaNode(key, value).setWorld(worldMappingFunc.apply(world)).build());
|
||||
}
|
||||
}
|
||||
|
||||
log.logProgress("Migrated {} users so far in world " + world, userWorldCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + userWorldCount.get() + " users in world " + world);
|
||||
}
|
||||
});
|
||||
|
||||
log.log("All data has now been processed, now starting the import process.");
|
||||
log.log("Found a total of " + users.size() + " users and " + groups.size() + " groups.");
|
||||
|
||||
log.log("Starting group migration.");
|
||||
AtomicInteger groupCount = new AtomicInteger(0);
|
||||
for (Map.Entry<String, Set<Node>> e : groups.entrySet()) {
|
||||
plugin.getStorage().createAndLoadGroup(e.getKey(), CreationCause.INTERNAL).join();
|
||||
me.lucko.luckperms.common.model.Group group = plugin.getGroupManager().getIfLoaded(e.getKey());
|
||||
SafeIteration.iterate(groups.entrySet(), e -> {
|
||||
Group group = plugin.getStorage().createAndLoadGroup(e.getKey(), CreationCause.INTERNAL).join();
|
||||
|
||||
for (Node node : e.getValue()) {
|
||||
group.setPermission(node);
|
||||
@@ -256,14 +219,13 @@ public class MigrationGroupManager extends SubCommand<Object> {
|
||||
|
||||
plugin.getStorage().saveGroup(group);
|
||||
log.logAllProgress("Migrated {} groups so far.", groupCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + groupCount.get() + " groups");
|
||||
|
||||
log.log("Starting user migration.");
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
for (Map.Entry<UUID, Set<Node>> e : users.entrySet()) {
|
||||
plugin.getStorage().loadUser(e.getKey(), null).join();
|
||||
me.lucko.luckperms.common.model.User user = plugin.getUserManager().getIfLoaded(e.getKey());
|
||||
SafeIteration.iterate(users.entrySet(), e -> {
|
||||
User user = plugin.getStorage().loadUser(e.getKey(), null).join();
|
||||
|
||||
for (Node node : e.getValue()) {
|
||||
user.setPermission(node);
|
||||
@@ -271,15 +233,15 @@ public class MigrationGroupManager extends SubCommand<Object> {
|
||||
|
||||
String primaryGroup = primaryGroups.get(e.getKey());
|
||||
if (primaryGroup != null && !primaryGroup.isEmpty()) {
|
||||
user.setPermission(NodeFactory.make("group." + primaryGroup));
|
||||
user.setPermission(NodeFactory.buildGroupNode(primaryGroup).build());
|
||||
user.getPrimaryGroup().setStoredValue(primaryGroup);
|
||||
user.unsetPermission(NodeFactory.make("group.default"));
|
||||
user.unsetPermission(NodeFactory.buildGroupNode(NodeFactory.DEFAULT_GROUP_NAME).build());
|
||||
}
|
||||
|
||||
plugin.getStorage().saveUser(user);
|
||||
plugin.getUserManager().cleanup(user);
|
||||
log.logProgress("Migrated {} users so far.", userCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
|
||||
log.log("Migrated " + userCount.get() + " users.");
|
||||
log.log("Success! Migration complete.");
|
||||
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.bukkit.migration;
|
||||
|
||||
import com.platymuus.bukkit.permissions.PermissionsPlugin;
|
||||
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.common.commands.CommandPermission;
|
||||
import me.lucko.luckperms.common.commands.CommandResult;
|
||||
import me.lucko.luckperms.common.commands.abstraction.SubCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.locale.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.logging.ProgressLogger;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.NodeFactory;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.utils.SafeIteration;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class MigrationPermissionsBukkit extends SubCommand<Object> {
|
||||
public MigrationPermissionsBukkit(LocaleManager locale) {
|
||||
super(CommandSpec.MIGRATION_COMMAND.spec(locale), "permissionsbukkit", CommandPermission.MIGRATION, Predicates.alwaysFalse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
ProgressLogger log = new ProgressLogger("PermissionsBukkit");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
|
||||
log.log("Starting.");
|
||||
|
||||
if (!Bukkit.getPluginManager().isPluginEnabled("PermissionsBukkit")) {
|
||||
log.logErr("Plugin not loaded.");
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
|
||||
PermissionsPlugin permissionsBukkit = (PermissionsPlugin) Bukkit.getPluginManager().getPlugin("PermissionsBukkit");
|
||||
FileConfiguration config = permissionsBukkit.getConfig();
|
||||
|
||||
// Migrate all groups
|
||||
log.log("Starting group migration.");
|
||||
AtomicInteger groupCount = new AtomicInteger(0);
|
||||
|
||||
ConfigurationSection groupsSection = config.getConfigurationSection("groups");
|
||||
|
||||
SafeIteration.iterate(groupsSection.getKeys(false), key -> {
|
||||
final String groupName = MigrationUtils.standardizeName(key);
|
||||
Group lpGroup = plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
|
||||
// migrate data
|
||||
if (groupsSection.isConfigurationSection(key)) {
|
||||
migrate(lpGroup, groupsSection.getConfigurationSection(key));
|
||||
}
|
||||
|
||||
plugin.getStorage().saveGroup(lpGroup).join();
|
||||
log.logAllProgress("Migrated {} groups so far.", groupCount.incrementAndGet());
|
||||
});
|
||||
log.log("Migrated " + groupCount.get() + " groups");
|
||||
|
||||
// Migrate all users
|
||||
log.log("Starting user migration.");
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
|
||||
ConfigurationSection usersSection = config.getConfigurationSection("users");
|
||||
|
||||
SafeIteration.iterate(usersSection.getKeys(false), key -> {
|
||||
UUID uuid = BukkitMigrationUtils.lookupUuid(log, key);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
User lpUser = plugin.getStorage().loadUser(uuid, null).join();
|
||||
|
||||
// migrate data
|
||||
if (usersSection.isConfigurationSection(key)) {
|
||||
migrate(lpUser, usersSection.getConfigurationSection(key));
|
||||
}
|
||||
|
||||
plugin.getUserManager().cleanup(lpUser);
|
||||
plugin.getStorage().saveUser(lpUser);
|
||||
log.logProgress("Migrated {} users so far.", userCount.incrementAndGet());
|
||||
});
|
||||
|
||||
log.log("Migrated " + userCount.get() + " users.");
|
||||
log.log("Success! Migration complete.");
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
private static void migrate(PermissionHolder holder, ConfigurationSection data) {
|
||||
// migrate permissions
|
||||
if (data.isConfigurationSection("permissions")) {
|
||||
ConfigurationSection permsSection = data.getConfigurationSection("permissions");
|
||||
for (String perm : permsSection.getKeys(false)) {
|
||||
boolean value = permsSection.getBoolean(perm);
|
||||
holder.setPermission(MigrationUtils.parseNode(perm, value).build());
|
||||
}
|
||||
}
|
||||
|
||||
if (data.isConfigurationSection("worlds")) {
|
||||
ConfigurationSection worldSection = data.getConfigurationSection("worlds");
|
||||
for (String world : worldSection.getKeys(false)) {
|
||||
if (worldSection.isConfigurationSection(world)) {
|
||||
ConfigurationSection permsSection = worldSection.getConfigurationSection(world);
|
||||
for (String perm : permsSection.getKeys(false)) {
|
||||
boolean value = permsSection.getBoolean(perm);
|
||||
holder.setPermission(MigrationUtils.parseNode(perm, value).setWorld(world).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// migrate parents
|
||||
if (data.isList("groups")) {
|
||||
List<String> groups = data.getStringList("groups");
|
||||
for (String group : groups) {
|
||||
holder.setPermission(NodeFactory.buildGroupNode(MigrationUtils.standardizeName(group)).build());
|
||||
}
|
||||
}
|
||||
if (data.isList("inheritance")) {
|
||||
List<String> groups = data.getStringList("inheritance");
|
||||
for (String group : groups) {
|
||||
holder.setPermission(NodeFactory.buildGroupNode(MigrationUtils.standardizeName(group)).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+164
-99
@@ -25,46 +25,73 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.migration;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.common.commands.CommandException;
|
||||
import me.lucko.luckperms.common.commands.CommandPermission;
|
||||
import me.lucko.luckperms.common.commands.CommandResult;
|
||||
import me.lucko.luckperms.common.commands.abstraction.SubCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.constants.CommandPermission;
|
||||
import me.lucko.luckperms.common.locale.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.logging.ProgressLogger;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.PermissionHolder;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.NodeFactory;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.utils.SafeIteration;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import ru.tehkode.permissions.NativeInterface;
|
||||
import ru.tehkode.permissions.PermissionEntity;
|
||||
import ru.tehkode.permissions.PermissionGroup;
|
||||
import ru.tehkode.permissions.PermissionManager;
|
||||
import ru.tehkode.permissions.PermissionUser;
|
||||
import ru.tehkode.permissions.PermissionsData;
|
||||
import ru.tehkode.permissions.bukkit.PermissionsEx;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MigrationPermissionsEx extends SubCommand<Object> {
|
||||
private static final Method GET_DATA_METHOD;
|
||||
private static final Field TIMED_PERMISSIONS_FIELD;
|
||||
private static final Field TIMED_PERMISSIONS_TIME_FIELD;
|
||||
static {
|
||||
try {
|
||||
GET_DATA_METHOD = PermissionEntity.class.getDeclaredMethod("getData");
|
||||
GET_DATA_METHOD.setAccessible(true);
|
||||
|
||||
TIMED_PERMISSIONS_FIELD = PermissionEntity.class.getDeclaredField("timedPermissions");
|
||||
TIMED_PERMISSIONS_FIELD.setAccessible(true);
|
||||
|
||||
TIMED_PERMISSIONS_TIME_FIELD = PermissionEntity.class.getDeclaredField("timedPermissionsTime");
|
||||
TIMED_PERMISSIONS_TIME_FIELD.setAccessible(true);
|
||||
} catch (NoSuchMethodException | NoSuchFieldException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public MigrationPermissionsEx(LocaleManager locale) {
|
||||
super(CommandSpec.MIGRATION_COMMAND.spec(locale), "permissionsex", CommandPermission.MIGRATION, Predicates.alwaysFalse());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
ProgressLogger log = new ProgressLogger("PermissionsEx");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
@@ -79,137 +106,164 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
|
||||
PermissionsEx pex = (PermissionsEx) Bukkit.getPluginManager().getPlugin("PermissionsEx");
|
||||
PermissionManager manager = pex.getPermissionsManager();
|
||||
|
||||
NativeInterface ni;
|
||||
try {
|
||||
Field f = manager.getClass().getDeclaredField("nativeI");
|
||||
f.setAccessible(true);
|
||||
ni = (NativeInterface) f.get(manager);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
log.log("Calculating group weightings.");
|
||||
int maxWeight = 0;
|
||||
int i = 0;
|
||||
for (PermissionGroup group : manager.getGroupList()) {
|
||||
maxWeight = Math.max(maxWeight, group.getRank());
|
||||
i = Math.max(i, group.getRank());
|
||||
}
|
||||
maxWeight += 5;
|
||||
int maxWeight = i + 5;
|
||||
|
||||
// Migrate all groups.
|
||||
log.log("Starting group migration.");
|
||||
AtomicInteger groupCount = new AtomicInteger(0);
|
||||
for (PermissionGroup group : manager.getGroupList()) {
|
||||
Set<String> ladders = new HashSet<>();
|
||||
SafeIteration.iterate(manager.getGroupList(), group -> {
|
||||
int groupWeight = maxWeight - group.getRank();
|
||||
|
||||
final String groupName = MigrationUtils.standardizeName(group.getName());
|
||||
plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
Group lpGroup = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
Group lpGroup = plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
|
||||
MigrationUtils.setGroupWeight(lpGroup, groupWeight);
|
||||
|
||||
// migrate data
|
||||
migrateEntity(group, lpGroup, groupWeight);
|
||||
|
||||
plugin.getStorage().saveGroup(lpGroup);
|
||||
// remember known ladders
|
||||
if (group.isRanked()) {
|
||||
ladders.add(group.getRankLadder().toLowerCase());
|
||||
}
|
||||
|
||||
plugin.getStorage().saveGroup(lpGroup).join();
|
||||
log.logAllProgress("Migrated {} groups so far.", groupCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + groupCount.get() + " groups");
|
||||
|
||||
// Migrate all ladders/tracks.
|
||||
log.log("Starting tracks migration.");
|
||||
for (String rankLadder : ladders) {
|
||||
Track track = plugin.getStorage().createAndLoadTrack(rankLadder, CreationCause.INTERNAL).join();
|
||||
|
||||
// Get a list of all groups in a ladder
|
||||
List<String> ladder = manager.getRankLadder(rankLadder).entrySet().stream()
|
||||
.sorted(Comparator.<Map.Entry<Integer, PermissionGroup>>comparingInt(Map.Entry::getKey).reversed())
|
||||
.map(e -> MigrationUtils.standardizeName(e.getValue().getName()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
track.setGroups(ladder);
|
||||
plugin.getStorage().saveTrack(track);
|
||||
}
|
||||
log.log("Migrated " + ladders.size() + " tracks");
|
||||
|
||||
// Migrate all users
|
||||
log.log("Starting user migration.");
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
|
||||
// Increment the max weight from the group migrations. All user meta should override.
|
||||
maxWeight += 5;
|
||||
|
||||
for (PermissionUser user : manager.getUsers()) {
|
||||
UUID u;
|
||||
try {
|
||||
u = UUID.fromString(user.getIdentifier());
|
||||
} catch (IllegalArgumentException e) {
|
||||
u = ni.nameToUUID(user.getIdentifier());
|
||||
if (u == null) {
|
||||
try {
|
||||
u = Bukkit.getOfflinePlayer(user.getIdentifier()).getUniqueId();
|
||||
} catch (Exception ex) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
int userWeight = maxWeight + 5;
|
||||
|
||||
SafeIteration.iterate(manager.getUsers(), user -> {
|
||||
UUID u = BukkitMigrationUtils.lookupUuid(log, user.getIdentifier());
|
||||
if (u == null) {
|
||||
log.logErr("Unable to get a UUID for user identifier: " + user.getIdentifier());
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
// load in a user instance
|
||||
plugin.getStorage().loadUser(u, user.getName()).join();
|
||||
User lpUser = plugin.getUserManager().getIfLoaded(u);
|
||||
User lpUser = plugin.getStorage().loadUser(u, user.getName()).join();
|
||||
|
||||
// migrate data
|
||||
migrateEntity(user, lpUser, maxWeight);
|
||||
|
||||
// Lowest rank is the highest group #logic
|
||||
String primary = null;
|
||||
int weight = Integer.MAX_VALUE;
|
||||
for (PermissionGroup group : user.getOwnParents()) {
|
||||
if (group.getRank() < weight) {
|
||||
primary = group.getName();
|
||||
weight = group.getRank();
|
||||
}
|
||||
}
|
||||
|
||||
if (primary != null && !primary.isEmpty() && !primary.equalsIgnoreCase("default")) {
|
||||
lpUser.setPermission(NodeFactory.make("group." + primary.toLowerCase()));
|
||||
lpUser.getPrimaryGroup().setStoredValue(primary);
|
||||
lpUser.unsetPermission(NodeFactory.make("group.default"));
|
||||
}
|
||||
migrateEntity(user, lpUser, userWeight);
|
||||
|
||||
plugin.getUserManager().cleanup(lpUser);
|
||||
plugin.getStorage().saveUser(lpUser);
|
||||
log.logProgress("Migrated {} users so far.", userCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
|
||||
log.log("Migrated " + userCount.get() + " users.");
|
||||
log.log("Success! Migration complete.");
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
private static Map<String, List<String>> getPermanentPermissions(PermissionEntity entity) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
PermissionsData data = (PermissionsData) GET_DATA_METHOD.invoke(entity);
|
||||
return data.getPermissionsMap();
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void migrateEntity(PermissionEntity entity, PermissionHolder holder, int weight) {
|
||||
// migrate permissions
|
||||
Map<String, List<String>> permissions = entity.getAllPermissions();
|
||||
for (Map.Entry<String, List<String>> worldData : permissions.entrySet()) {
|
||||
String world = worldData.getKey();
|
||||
if (world != null && (world.equals("") || world.equals("*"))) {
|
||||
world = null;
|
||||
}
|
||||
if (world != null) {
|
||||
world = world.toLowerCase();
|
||||
}
|
||||
|
||||
// migrate permanent permissions
|
||||
for (Map.Entry<String, List<String>> worldData : getPermanentPermissions(entity).entrySet()) {
|
||||
String world = standardizeWorld(worldData.getKey());
|
||||
for (String node : worldData.getValue()) {
|
||||
if (node.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.isEmpty()) continue;
|
||||
holder.setPermission(MigrationUtils.parseNode(node, true).setWorld(world).build());
|
||||
}
|
||||
}
|
||||
|
||||
// migrate temporary permissions
|
||||
Map<String, List<String>> timedPermissions;
|
||||
Map<String, Long> timedPermissionsTime;
|
||||
|
||||
try {
|
||||
//noinspection unchecked
|
||||
timedPermissions = (Map<String, List<String>>) TIMED_PERMISSIONS_FIELD.get(entity);
|
||||
//noinspection unchecked
|
||||
timedPermissionsTime = (Map<String, Long>) TIMED_PERMISSIONS_TIME_FIELD.get(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<String>> worldData : timedPermissions.entrySet()) {
|
||||
String world = standardizeWorld(worldData.getKey());
|
||||
for (String node : worldData.getValue()) {
|
||||
if (node.isEmpty()) continue;
|
||||
long expiry = timedPermissionsTime.getOrDefault(Strings.nullToEmpty(world) + ":" + node, 0L);
|
||||
holder.setPermission(MigrationUtils.parseNode(node, true).setWorld(world).setExpiry(expiry).build());
|
||||
}
|
||||
}
|
||||
|
||||
// migrate parents
|
||||
Map<String, List<PermissionGroup>> parents = entity.getAllParents();
|
||||
for (Map.Entry<String, List<PermissionGroup>> worldData : parents.entrySet()) {
|
||||
String world = worldData.getKey();
|
||||
if (world != null && (world.equals("") || world.equals("*"))) {
|
||||
world = null;
|
||||
}
|
||||
if (world != null) {
|
||||
world = world.toLowerCase();
|
||||
}
|
||||
for (Map.Entry<String, List<PermissionGroup>> worldData : entity.getAllParents().entrySet()) {
|
||||
String world = standardizeWorld(worldData.getKey());
|
||||
|
||||
// keep track of primary group
|
||||
String primary = null;
|
||||
int primaryWeight = Integer.MAX_VALUE;
|
||||
|
||||
for (PermissionGroup parent : worldData.getValue()) {
|
||||
holder.setPermission(NodeFactory.newBuilder("group." + MigrationUtils.standardizeName(parent.getName())).setWorld(world).build());
|
||||
String parentName = parent.getName();
|
||||
long expiry = 0L;
|
||||
|
||||
// check for temporary parent
|
||||
if (entity instanceof PermissionUser) {
|
||||
String expiryOption = entity.getOption("group-" + parentName + "-until", world);
|
||||
if (expiryOption != null) {
|
||||
try {
|
||||
expiry = Long.parseLong(expiryOption);
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
holder.setPermission(NodeFactory.buildGroupNode(MigrationUtils.standardizeName(parentName)).setWorld(world).setExpiry(expiry).build());
|
||||
|
||||
// migrate primary groups
|
||||
if (world == null && holder instanceof User && expiry == 0) {
|
||||
if (parent.getRank() < primaryWeight) {
|
||||
primary = parent.getName();
|
||||
primaryWeight = parent.getRank();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (primary != null && !primary.isEmpty() && !primary.equalsIgnoreCase(NodeFactory.DEFAULT_GROUP_NAME)) {
|
||||
User user = ((User) holder);
|
||||
user.getPrimaryGroup().setStoredValue(primary);
|
||||
user.unsetPermission(NodeFactory.buildGroupNode(NodeFactory.DEFAULT_GROUP_NAME).build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,36 +272,47 @@ public class MigrationPermissionsEx extends SubCommand<Object> {
|
||||
String suffix = entity.getOwnSuffix();
|
||||
|
||||
if (prefix != null && !prefix.isEmpty()) {
|
||||
holder.setPermission(NodeFactory.makePrefixNode(weight, prefix).build());
|
||||
holder.setPermission(NodeFactory.buildPrefixNode(weight, prefix).build());
|
||||
}
|
||||
|
||||
if (suffix != null && !suffix.isEmpty()) {
|
||||
holder.setPermission(NodeFactory.makeSuffixNode(weight, suffix).build());
|
||||
holder.setPermission(NodeFactory.buildSuffixNode(weight, suffix).build());
|
||||
}
|
||||
|
||||
// migrate options
|
||||
Map<String, Map<String, String>> options = entity.getAllOptions();
|
||||
for (Map.Entry<String, Map<String, String>> worldData : options.entrySet()) {
|
||||
String world = worldData.getKey();
|
||||
if (world != null && (world.isEmpty() || world.equals("*"))) {
|
||||
world = null;
|
||||
}
|
||||
if (world != null) {
|
||||
world = world.toLowerCase();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Map<String, String>> worldData : entity.getAllOptions().entrySet()) {
|
||||
String world = standardizeWorld(worldData.getKey());
|
||||
for (Map.Entry<String, String> opt : worldData.getValue().entrySet()) {
|
||||
if (opt.getKey() == null || opt.getKey().isEmpty() || opt.getValue() == null || opt.getValue().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = opt.getKey().toLowerCase();
|
||||
if (key.equals("prefix") || key.equals("suffix") || key.equals("weight") || key.equals("rank") || key.equals("name") || key.equals("username")) {
|
||||
boolean ignore = key.equals(NodeFactory.PREFIX_KEY) ||
|
||||
key.equals(NodeFactory.SUFFIX_KEY) ||
|
||||
key.equals(NodeFactory.WEIGHT_KEY) ||
|
||||
key.equals("rank") ||
|
||||
key.equals("rank-ladder") ||
|
||||
key.equals("name") ||
|
||||
key.equals("username") ||
|
||||
(key.startsWith("group-") && key.endsWith("-until"));
|
||||
|
||||
if (ignore) {
|
||||
continue;
|
||||
}
|
||||
|
||||
holder.setPermission(NodeFactory.makeMetaNode(opt.getKey(), opt.getValue()).setWorld(world).build());
|
||||
holder.setPermission(NodeFactory.buildMetaNode(opt.getKey(), opt.getValue()).setWorld(world).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String standardizeWorld(String world) {
|
||||
if (world != null && (world.isEmpty() || world.equals("*"))) {
|
||||
world = null;
|
||||
}
|
||||
if (world != null) {
|
||||
world = world.toLowerCase();
|
||||
}
|
||||
return world;
|
||||
}
|
||||
}
|
||||
+84
-48
@@ -25,24 +25,21 @@
|
||||
|
||||
package me.lucko.luckperms.bukkit.migration;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import com.github.cheesesoftware.PowerfulPermsAPI.CachedGroup;
|
||||
import com.github.cheesesoftware.PowerfulPermsAPI.Group;
|
||||
import com.github.cheesesoftware.PowerfulPermsAPI.Permission;
|
||||
import com.github.cheesesoftware.PowerfulPermsAPI.PermissionManager;
|
||||
import com.github.cheesesoftware.PowerfulPermsAPI.PowerfulPermsPlugin;
|
||||
import com.github.gustav9797.PowerfulPermsAPI.CachedGroup;
|
||||
import com.github.gustav9797.PowerfulPermsAPI.Group;
|
||||
import com.github.gustav9797.PowerfulPermsAPI.Permission;
|
||||
import com.github.gustav9797.PowerfulPermsAPI.PermissionManager;
|
||||
import com.github.gustav9797.PowerfulPermsAPI.PowerfulPermsPlugin;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.common.commands.CommandException;
|
||||
import me.lucko.luckperms.common.commands.CommandResult;
|
||||
import me.lucko.luckperms.common.commands.abstraction.SubCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
||||
import me.lucko.luckperms.common.locale.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.logging.ProgressLogger;
|
||||
@@ -51,8 +48,8 @@ import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.NodeFactory;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.StorageType;
|
||||
import me.lucko.luckperms.common.utils.HikariSupplier;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.utils.SafeIteration;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
@@ -60,16 +57,18 @@ import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static me.lucko.luckperms.common.constants.CommandPermission.MIGRATION;
|
||||
import static me.lucko.luckperms.common.commands.CommandPermission.MIGRATION;
|
||||
|
||||
// Only supports the latest versions of the PP API. (it seems to change randomly almost every release)
|
||||
public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
@@ -78,7 +77,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
ProgressLogger log = new ProgressLogger("PowerfulPerms");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
@@ -95,7 +94,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
|
||||
if (type == null || type != StorageType.MYSQL) {
|
||||
// We need to load the Hikari/MySQL stuff.
|
||||
DependencyManager.loadStorageDependencies(plugin, ImmutableSet.of(StorageType.MYSQL));
|
||||
plugin.getDependencyManager().loadStorageDependencies(ImmutableSet.of(StorageType.MYSQL));
|
||||
}
|
||||
|
||||
String address = args.get(0);
|
||||
@@ -113,7 +112,6 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
|
||||
try (Connection c = hikari.getConnection()) {
|
||||
DatabaseMetaData meta = c.getMetaData();
|
||||
|
||||
try (ResultSet rs = meta.getTables(null, null, dbTable, null)) {
|
||||
if (!rs.next()) {
|
||||
log.log("Error - Couldn't find table.");
|
||||
@@ -125,7 +123,6 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
try (Connection c = hikari.getConnection()) {
|
||||
try (PreparedStatement ps = c.prepareStatement("SELECT COLUMN_NAME, COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=?")) {
|
||||
ps.setString(1, dbTable);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
log.log("Found table: " + dbTable);
|
||||
while (rs.next()) {
|
||||
@@ -133,7 +130,6 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try (PreparedStatement ps = c.prepareStatement("SELECT `uuid` FROM " + dbTable)) {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
@@ -158,17 +154,16 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
|
||||
Collection<Group> groups = pm.getGroups().values();
|
||||
|
||||
int maxWeight = 0;
|
||||
AtomicInteger maxWeight = new AtomicInteger(0);
|
||||
|
||||
// Groups first.
|
||||
log.log("Starting group migration.");
|
||||
AtomicInteger groupCount = new AtomicInteger(0);
|
||||
for (Group g : groups) {
|
||||
maxWeight = Math.max(maxWeight, g.getRank());
|
||||
SafeIteration.iterate(groups, g -> {
|
||||
maxWeight.set(Math.max(maxWeight.get(), g.getRank()));
|
||||
|
||||
final String groupName = MigrationUtils.standardizeName(g.getName());
|
||||
plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
final me.lucko.luckperms.common.model.Group group = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
String groupName = MigrationUtils.standardizeName(g.getName());
|
||||
me.lucko.luckperms.common.model.Group group = plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
|
||||
MigrationUtils.setGroupWeight(group, g.getRank());
|
||||
|
||||
@@ -177,14 +172,12 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
}
|
||||
|
||||
for (Group parent : g.getParents()) {
|
||||
group.setPermission(NodeFactory.make("group." + parent.getName().toLowerCase(), true));
|
||||
group.setPermission(NodeFactory.buildGroupNode(parent.getName().toLowerCase()).build());
|
||||
}
|
||||
|
||||
// server --> prefix afaik
|
||||
for (Map.Entry<String, String> prefix : g.getPrefixes().entrySet()) {
|
||||
if (prefix.getValue().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (prefix.getValue().isEmpty()) continue;
|
||||
|
||||
String server = prefix.getKey().toLowerCase();
|
||||
if (prefix.getKey().equals("*") || prefix.getKey().equals("all")) {
|
||||
@@ -192,16 +185,14 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
}
|
||||
|
||||
if (server != null) {
|
||||
group.setPermission(NodeFactory.makePrefixNode(g.getRank(), prefix.getValue()).setServer(server).build());
|
||||
group.setPermission(NodeFactory.buildPrefixNode(g.getRank(), prefix.getValue()).setServer(server).build());
|
||||
} else {
|
||||
group.setPermission(NodeFactory.makePrefixNode(g.getRank(), prefix.getValue()).build());
|
||||
group.setPermission(NodeFactory.buildPrefixNode(g.getRank(), prefix.getValue()).build());
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> suffix : g.getSuffixes().entrySet()) {
|
||||
if (suffix.getValue().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (suffix.getValue().isEmpty()) continue;
|
||||
|
||||
String server = suffix.getKey().toLowerCase();
|
||||
if (suffix.getKey().equals("*") || suffix.getKey().equals("all")) {
|
||||
@@ -209,15 +200,15 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
}
|
||||
|
||||
if (server != null) {
|
||||
group.setPermission(NodeFactory.makeSuffixNode(g.getRank(), suffix.getValue()).setServer(server).build());
|
||||
group.setPermission(NodeFactory.buildSuffixNode(g.getRank(), suffix.getValue()).setServer(server).build());
|
||||
} else {
|
||||
group.setPermission(NodeFactory.makeSuffixNode(g.getRank(), suffix.getValue()).build());
|
||||
group.setPermission(NodeFactory.buildSuffixNode(g.getRank(), suffix.getValue()).build());
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getStorage().saveGroup(group);
|
||||
log.logAllProgress("Migrated {} groups so far.", groupCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + groupCount.get() + " groups");
|
||||
|
||||
// Migrate all users
|
||||
@@ -225,14 +216,13 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
|
||||
// Increment the max weight from the group migrations. All user meta should override.
|
||||
maxWeight += 5;
|
||||
maxWeight.addAndGet(5);
|
||||
|
||||
// Migrate all users and their groups
|
||||
for (UUID uuid : uuids) {
|
||||
SafeIteration.iterate(uuids, uuid -> {
|
||||
|
||||
// Create a LuckPerms user for the UUID
|
||||
plugin.getStorage().loadUser(uuid, null).join();
|
||||
User user = plugin.getUserManager().getIfLoaded(uuid);
|
||||
User user = plugin.getStorage().loadUser(uuid, null).join();
|
||||
|
||||
List<Permission> permissions = joinFuture(pm.getPlayerOwnPermissions(uuid));
|
||||
|
||||
@@ -257,18 +247,18 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
String suffix = joinFuture(pm.getPlayerOwnSuffix(uuid));
|
||||
|
||||
if (prefix != null && !prefix.isEmpty()) {
|
||||
user.setPermission(NodeFactory.makePrefixNode(maxWeight, prefix).build());
|
||||
user.setPermission(NodeFactory.buildPrefixNode(maxWeight.get(), prefix).build());
|
||||
}
|
||||
|
||||
if (suffix != null && !suffix.isEmpty()) {
|
||||
user.setPermission(NodeFactory.makeSuffixNode(maxWeight, suffix).build());
|
||||
user.setPermission(NodeFactory.buildSuffixNode(maxWeight.get(), suffix).build());
|
||||
}
|
||||
|
||||
Group primaryGroup = joinFuture(pm.getPlayerPrimaryGroup(uuid));
|
||||
if (primaryGroup != null && primaryGroup.getName() != null) {
|
||||
String primary = primaryGroup.getName().toLowerCase();
|
||||
if (!primary.equals("default")) {
|
||||
user.setPermission(NodeFactory.make("group." + primary));
|
||||
if (!primary.equals(NodeFactory.DEFAULT_GROUP_NAME)) {
|
||||
user.setPermission(NodeFactory.buildGroupNode(primary).build());
|
||||
user.getPrimaryGroup().setStoredValue(primary);
|
||||
}
|
||||
}
|
||||
@@ -276,7 +266,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
plugin.getUserManager().cleanup(user);
|
||||
plugin.getStorage().saveUser(user);
|
||||
log.logProgress("Migrated {} users so far.", userCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
|
||||
log.log("Migrated " + userCount.get() + " users.");
|
||||
log.log("Success! Migration complete.");
|
||||
@@ -314,7 +304,7 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
server = "global";
|
||||
}
|
||||
|
||||
Node.Builder nb = NodeFactory.newBuilder(node).setValue(value);
|
||||
Node.Builder nb = NodeFactory.builder(node).setValue(value);
|
||||
if (expireAt != 0) nb.setExpiry(expireAt);
|
||||
if (server != null) nb.setServer(server);
|
||||
if (world != null) nb.setWorld(world);
|
||||
@@ -324,14 +314,14 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
|
||||
private void applyGroup(PermissionManager pm, PermissionHolder holder, CachedGroup g, String server) {
|
||||
Group group = pm.getGroup(g.getGroupId());
|
||||
String node = "group." + MigrationUtils.standardizeName(group.getName());
|
||||
String node = NodeFactory.groupNode(MigrationUtils.standardizeName(group.getName()));
|
||||
|
||||
long expireAt = 0L;
|
||||
if (g.willExpire()) {
|
||||
expireAt = g.getExpirationDate().getTime() / 1000L;
|
||||
}
|
||||
|
||||
Node.Builder nb = NodeFactory.newBuilder(node);
|
||||
Node.Builder nb = NodeFactory.builder(node);
|
||||
|
||||
if (expireAt != 0) {
|
||||
nb.setExpiry(expireAt);
|
||||
@@ -344,8 +334,54 @@ public class MigrationPowerfulPerms extends SubCommand<Object> {
|
||||
holder.setPermission(nb.build());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static <T> T joinFuture(Future<T> future) {
|
||||
return future.get();
|
||||
try {
|
||||
return future.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple hikari wrapper
|
||||
*/
|
||||
public static final class HikariSupplier implements AutoCloseable {
|
||||
|
||||
private final String address;
|
||||
private final String database;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
private HikariDataSource hikari;
|
||||
|
||||
public HikariSupplier(String address, String database, String username, String password) {
|
||||
this.address = address;
|
||||
this.database = database;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setup(String poolName) {
|
||||
this.hikari = new HikariDataSource();
|
||||
this.hikari.setPoolName(poolName);
|
||||
this.hikari.setMaximumPoolSize(2);
|
||||
this.hikari.setDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
|
||||
this.hikari.addDataSourceProperty("serverName", this.address.split(":")[0]);
|
||||
this.hikari.addDataSourceProperty("port", this.address.split(":")[1]);
|
||||
this.hikari.addDataSourceProperty("databaseName", this.database);
|
||||
this.hikari.addDataSourceProperty("user", this.username);
|
||||
this.hikari.addDataSourceProperty("password", this.password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.hikari.close();
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException {
|
||||
return this.hikari.getConnection();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+27
-37
@@ -28,13 +28,11 @@ package me.lucko.luckperms.bukkit.migration;
|
||||
import me.lucko.luckperms.api.ChatMetaType;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.api.event.cause.CreationCause;
|
||||
import me.lucko.luckperms.common.commands.CommandException;
|
||||
import me.lucko.luckperms.common.commands.CommandPermission;
|
||||
import me.lucko.luckperms.common.commands.CommandResult;
|
||||
import me.lucko.luckperms.common.commands.abstraction.SubCommand;
|
||||
import me.lucko.luckperms.common.commands.impl.migration.MigrationUtils;
|
||||
import me.lucko.luckperms.common.commands.sender.Sender;
|
||||
import me.lucko.luckperms.common.commands.utils.Util;
|
||||
import me.lucko.luckperms.common.constants.CommandPermission;
|
||||
import me.lucko.luckperms.common.locale.CommandSpec;
|
||||
import me.lucko.luckperms.common.locale.LocaleManager;
|
||||
import me.lucko.luckperms.common.logging.ProgressLogger;
|
||||
@@ -45,6 +43,7 @@ import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.NodeFactory;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
import me.lucko.luckperms.common.utils.SafeIteration;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.tyrannyofheaven.bukkit.zPermissions.ZPermissionsService;
|
||||
@@ -69,7 +68,7 @@ public class MigrationZPermissions extends SubCommand<Object> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) throws CommandException {
|
||||
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Object o, List<String> args, String label) {
|
||||
ProgressLogger log = new ProgressLogger("zPermissions");
|
||||
log.addListener(plugin.getConsoleSender());
|
||||
log.addListener(sender);
|
||||
@@ -104,63 +103,60 @@ public class MigrationZPermissions extends SubCommand<Object> {
|
||||
Map<UUID, Set<Node>> userParents = new HashMap<>();
|
||||
|
||||
AtomicInteger groupCount = new AtomicInteger(0);
|
||||
int maxWeight = 0;
|
||||
for (PermissionEntity entity : internalService.getEntities(true)) {
|
||||
AtomicInteger maxWeight = new AtomicInteger(0);
|
||||
SafeIteration.iterate(internalService.getEntities(true), entity -> {
|
||||
String groupName = MigrationUtils.standardizeName(entity.getDisplayName());
|
||||
plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
Group group = plugin.getGroupManager().getIfLoaded(groupName);
|
||||
Group group = plugin.getStorage().createAndLoadGroup(groupName, CreationCause.INTERNAL).join();
|
||||
|
||||
int weight = entity.getPriority();
|
||||
maxWeight = Math.max(maxWeight, weight);
|
||||
maxWeight.set(Math.max(maxWeight.get(), weight));
|
||||
migrateEntity(group, entity, weight);
|
||||
MigrationUtils.setGroupWeight(group, weight);
|
||||
|
||||
// store user data for later
|
||||
Set<Membership> members = entity.getMemberships();
|
||||
for (Membership membership : members) {
|
||||
UUID uuid = Util.parseUuid(membership.getMember());
|
||||
UUID uuid = BukkitMigrationUtils.lookupUuid(log, membership.getMember());
|
||||
if (uuid == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<Node> nodes = userParents.computeIfAbsent(uuid, u -> new HashSet<>());
|
||||
if (membership.getExpiration() == null) {
|
||||
nodes.add(NodeFactory.make("group." + groupName));
|
||||
nodes.add(NodeFactory.buildGroupNode(groupName).build());
|
||||
} else {
|
||||
long expiry = membership.getExpiration().toInstant().getEpochSecond();
|
||||
nodes.add(NodeFactory.newBuilder("group." + groupName).setExpiry(expiry).build());
|
||||
nodes.add(NodeFactory.buildGroupNode(groupName).setExpiry(expiry).build());
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getStorage().saveGroup(group);
|
||||
log.logAllProgress("Migrated {} groups so far.", groupCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + groupCount.get() + " groups");
|
||||
|
||||
// Migrate all tracks
|
||||
log.log("Starting track migration.");
|
||||
AtomicInteger trackCount = new AtomicInteger(0);
|
||||
for (String t : service.getAllTracks()) {
|
||||
SafeIteration.iterate(service.getAllTracks(), t -> {
|
||||
String trackName = MigrationUtils.standardizeName(t);
|
||||
|
||||
plugin.getStorage().createAndLoadTrack(trackName, CreationCause.INTERNAL).join();
|
||||
Track track = plugin.getTrackManager().getIfLoaded(trackName);
|
||||
Track track = plugin.getStorage().createAndLoadTrack(trackName, CreationCause.INTERNAL).join();
|
||||
track.setGroups(service.getTrackGroups(t));
|
||||
plugin.getStorage().saveTrack(track);
|
||||
|
||||
log.logAllProgress("Migrated {} tracks so far.", trackCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
log.log("Migrated " + trackCount.get() + " tracks");
|
||||
|
||||
// Migrate all users.
|
||||
log.log("Starting user migration.");
|
||||
maxWeight += 10;
|
||||
maxWeight.addAndGet(10);
|
||||
AtomicInteger userCount = new AtomicInteger(0);
|
||||
|
||||
Set<UUID> usersToMigrate = new HashSet<>(userParents.keySet());
|
||||
usersToMigrate.addAll(service.getAllPlayersUUID());
|
||||
|
||||
for (UUID u : usersToMigrate) {
|
||||
SafeIteration.iterate(usersToMigrate, u -> {
|
||||
PermissionEntity entity = internalService.getEntity(null, u, false);
|
||||
|
||||
String username = null;
|
||||
@@ -168,12 +164,11 @@ public class MigrationZPermissions extends SubCommand<Object> {
|
||||
username = entity.getDisplayName();
|
||||
}
|
||||
|
||||
plugin.getStorage().loadUser(u, username).join();
|
||||
User user = plugin.getUserManager().getIfLoaded(u);
|
||||
User user = plugin.getStorage().loadUser(u, username).join();
|
||||
|
||||
// migrate permissions & meta
|
||||
if (entity != null) {
|
||||
migrateEntity(user, entity, maxWeight);
|
||||
migrateEntity(user, entity, maxWeight.get());
|
||||
}
|
||||
|
||||
// migrate groups
|
||||
@@ -187,7 +182,7 @@ public class MigrationZPermissions extends SubCommand<Object> {
|
||||
plugin.getUserManager().cleanup(user);
|
||||
plugin.getStorage().saveUser(user);
|
||||
log.logProgress("Migrated {} users so far.", userCount.incrementAndGet());
|
||||
}
|
||||
});
|
||||
|
||||
log.log("Migrated " + userCount.get() + " users.");
|
||||
log.log("Success! Migration complete.");
|
||||
@@ -196,14 +191,12 @@ public class MigrationZPermissions extends SubCommand<Object> {
|
||||
|
||||
private void migrateEntity(PermissionHolder holder, PermissionEntity entity, int weight) {
|
||||
for (Entry e : entity.getPermissions()) {
|
||||
if (e.getPermission().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (e.getPermission().isEmpty()) continue;
|
||||
|
||||
if (e.getWorld() != null && !e.getWorld().getName().equals("")) {
|
||||
holder.setPermission(NodeFactory.newBuilder(e.getPermission()).setValue(e.isValue()).setWorld(e.getWorld().getName()).build());
|
||||
holder.setPermission(NodeFactory.builder(e.getPermission()).setValue(e.isValue()).setWorld(e.getWorld().getName()).build());
|
||||
} else {
|
||||
holder.setPermission(NodeFactory.newBuilder(e.getPermission()).setValue(e.isValue()).build());
|
||||
holder.setPermission(NodeFactory.builder(e.getPermission()).setValue(e.isValue()).build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,23 +204,20 @@ public class MigrationZPermissions extends SubCommand<Object> {
|
||||
if (entity.isGroup()) {
|
||||
for (PermissionEntity inheritance : entity.getParents()) {
|
||||
if (!inheritance.getDisplayName().equals(holder.getObjectName())) {
|
||||
holder.setPermission(NodeFactory.make("group." + MigrationUtils.standardizeName(inheritance.getDisplayName())));
|
||||
holder.setPermission(NodeFactory.buildGroupNode(MigrationUtils.standardizeName(inheritance.getDisplayName())).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (EntityMetadata metadata : entity.getMetadata()) {
|
||||
String key = metadata.getName().toLowerCase();
|
||||
if (key.isEmpty() || metadata.getStringValue().isEmpty()) continue;
|
||||
|
||||
if (key.isEmpty() || metadata.getStringValue().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.equals("prefix") || key.equals("suffix")) {
|
||||
if (key.equals(NodeFactory.PREFIX_KEY) || key.equals(NodeFactory.SUFFIX_KEY)) {
|
||||
ChatMetaType type = ChatMetaType.valueOf(key.toUpperCase());
|
||||
holder.setPermission(NodeFactory.makeChatMetaNode(type, weight, metadata.getStringValue()).build());
|
||||
holder.setPermission(NodeFactory.buildChatMetaNode(type, weight, metadata.getStringValue()).build());
|
||||
} else {
|
||||
holder.setPermission(NodeFactory.makeMetaNode(key, metadata.getStringValue()).build());
|
||||
holder.setPermission(NodeFactory.buildMetaNode(key, metadata.getStringValue()).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-1
@@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.bukkit.model;
|
||||
package me.lucko.luckperms.bukkit.model.dummy;
|
||||
|
||||
import org.bukkit.permissions.PermissibleBase;
|
||||
import org.bukkit.permissions.Permission;
|
||||
@@ -31,14 +31,44 @@ import org.bukkit.permissions.PermissionAttachment;
|
||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class DummyPermissibleBase extends PermissibleBase {
|
||||
private static final Field ATTACHMENTS_FIELD;
|
||||
private static final Field PERMISSIONS_FIELD;
|
||||
|
||||
static {
|
||||
try {
|
||||
ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
|
||||
ATTACHMENTS_FIELD.setAccessible(true);
|
||||
|
||||
PERMISSIONS_FIELD = PermissibleBase.class.getDeclaredField("permissions");
|
||||
PERMISSIONS_FIELD.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void nullFields(PermissibleBase permissibleBase) {
|
||||
try {
|
||||
ATTACHMENTS_FIELD.set(permissibleBase, null);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
PERMISSIONS_FIELD.set(permissibleBase, null);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public static final DummyPermissibleBase INSTANCE = new DummyPermissibleBase();
|
||||
|
||||
private DummyPermissibleBase() {
|
||||
super(null);
|
||||
nullFields(this);
|
||||
}
|
||||
|
||||
@Override public boolean isOp() { return false; }
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.bukkit.model;
|
||||
package me.lucko.luckperms.bukkit.model.dummy;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.Command;
|
||||
+101
-39
@@ -23,11 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.bukkit.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
package me.lucko.luckperms.bukkit.model.permissible;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
@@ -45,10 +41,11 @@ import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* PermissibleBase for LuckPerms.
|
||||
@@ -64,7 +61,6 @@ import java.util.stream.Collectors;
|
||||
* This class is **thread safe**. This means that when LuckPerms is installed on the server,
|
||||
* is is safe to call Player#hasPermission asynchronously.
|
||||
*/
|
||||
@Getter
|
||||
public class LPPermissible extends PermissibleBase {
|
||||
|
||||
// the LuckPerms user this permissible references.
|
||||
@@ -77,7 +73,6 @@ public class LPPermissible extends PermissibleBase {
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
// the players previous permissible. (the one they had before this one was injected)
|
||||
@Setter
|
||||
private PermissibleBase oldPermissible = null;
|
||||
|
||||
// if the permissible is currently active.
|
||||
@@ -87,27 +82,35 @@ public class LPPermissible extends PermissibleBase {
|
||||
// this collection is only modified by the attachments themselves
|
||||
final Set<LPPermissionAttachment> attachments = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public LPPermissible(@NonNull Player player, @NonNull User user, @NonNull LPBukkitPlugin plugin) {
|
||||
public LPPermissible(Player player, User user, LPBukkitPlugin plugin) {
|
||||
super(player);
|
||||
this.user = user;
|
||||
this.player = player;
|
||||
this.plugin = plugin;
|
||||
this.user = Objects.requireNonNull(user, "user");
|
||||
this.player = Objects.requireNonNull(player, "player");
|
||||
this.plugin = Objects.requireNonNull(plugin, "plugin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(@NonNull String permission) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
|
||||
public boolean isPermissionSet(String permission) {
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
|
||||
return ts != Tristate.UNDEFINED || Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(@NonNull Permission permission) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK);
|
||||
public boolean isPermissionSet(Permission permission) {
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK);
|
||||
if (ts != Tristate.UNDEFINED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
if (!this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
return Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
} else {
|
||||
return permission.getDefault().getValue(isOp());
|
||||
@@ -115,19 +118,27 @@ public class LPPermissible extends PermissibleBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull String permission) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK);
|
||||
public boolean hasPermission(String permission) {
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK);
|
||||
return ts != Tristate.UNDEFINED ? ts.asBoolean() : Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@NonNull Permission permission) {
|
||||
Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK);
|
||||
public boolean hasPermission(Permission permission) {
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
Tristate ts = this.user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK);
|
||||
if (ts != Tristate.UNDEFINED) {
|
||||
return ts.asBoolean();
|
||||
}
|
||||
|
||||
if (!plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
if (!this.plugin.getConfiguration().get(ConfigKeys.APPLY_BUKKIT_DEFAULT_PERMISSIONS)) {
|
||||
return Permission.DEFAULT_PERMISSION.getValue(isOp());
|
||||
} else {
|
||||
return permission.getDefault().getValue(isOp());
|
||||
@@ -152,41 +163,57 @@ public class LPPermissible extends PermissibleBase {
|
||||
* @return the calculated contexts for the player.
|
||||
*/
|
||||
private Contexts calculateContexts() {
|
||||
return plugin.getContextManager().getApplicableContexts(player);
|
||||
return this.plugin.getContextManager().getApplicableContexts(this.player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOp(boolean value) {
|
||||
player.setOp(value);
|
||||
this.player.setOp(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
|
||||
Set<PermissionAttachmentInfo> perms = new HashSet<>();
|
||||
perms.addAll(
|
||||
user.getUserData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet().stream()
|
||||
.map(e -> new PermissionAttachmentInfo(player, e.getKey(), null, e.getValue()))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
return perms;
|
||||
Set<Map.Entry<String, Boolean>> permissions = this.user.getCachedData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet();
|
||||
Set<PermissionAttachmentInfo> ret = new HashSet<>(permissions.size());
|
||||
|
||||
for (Map.Entry<String, Boolean> entry : permissions) {
|
||||
ret.add(new PermissionAttachmentInfo(this.player, entry.getKey(), null, entry.getValue()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LPPermissionAttachment addAttachment(Plugin plugin) {
|
||||
if (plugin == null) {
|
||||
throw new NullPointerException("plugin");
|
||||
}
|
||||
|
||||
LPPermissionAttachment ret = new LPPermissionAttachment(this, plugin);
|
||||
ret.hook();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionAttachment addAttachment(Plugin plugin, @NonNull String name, boolean value) {
|
||||
public PermissionAttachment addAttachment(Plugin plugin, String permission, boolean value) {
|
||||
if (plugin == null) {
|
||||
throw new NullPointerException("plugin");
|
||||
}
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
PermissionAttachment ret = addAttachment(plugin);
|
||||
ret.setPermission(name, value);
|
||||
ret.setPermission(permission, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LPPermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) {
|
||||
public LPPermissionAttachment addAttachment(Plugin plugin, int ticks) {
|
||||
if (plugin == null) {
|
||||
throw new NullPointerException("plugin");
|
||||
}
|
||||
|
||||
if (!plugin.isEnabled()) {
|
||||
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
|
||||
}
|
||||
@@ -194,20 +221,31 @@ public class LPPermissible extends PermissibleBase {
|
||||
LPPermissionAttachment ret = addAttachment(plugin);
|
||||
if (getPlugin().getServer().getScheduler().scheduleSyncDelayedTask(plugin, ret::remove, ticks) == -1) {
|
||||
ret.remove();
|
||||
throw new RuntimeException("Could not add PermissionAttachment to " + player + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1");
|
||||
throw new RuntimeException("Could not add PermissionAttachment to " + this.player + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LPPermissionAttachment addAttachment(Plugin plugin, @NonNull String name, boolean value, int ticks) {
|
||||
public LPPermissionAttachment addAttachment(Plugin plugin, String permission, boolean value, int ticks) {
|
||||
if (plugin == null) {
|
||||
throw new NullPointerException("plugin");
|
||||
}
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
LPPermissionAttachment ret = addAttachment(plugin, ticks);
|
||||
ret.setPermission(name, value);
|
||||
ret.setPermission(permission, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttachment(@NonNull PermissionAttachment attachment) {
|
||||
public void removeAttachment(PermissionAttachment attachment) {
|
||||
if (attachment == null) {
|
||||
throw new NullPointerException("attachment");
|
||||
}
|
||||
|
||||
if (!(attachment instanceof LPPermissionAttachment)) {
|
||||
throw new IllegalArgumentException("Given attachment is not a LPPermissionAttachment.");
|
||||
}
|
||||
@@ -227,6 +265,30 @@ public class LPPermissible extends PermissibleBase {
|
||||
|
||||
@Override
|
||||
public void clearPermissions() {
|
||||
attachments.forEach(LPPermissionAttachment::remove);
|
||||
this.attachments.forEach(LPPermissionAttachment::remove);
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public LPBukkitPlugin getPlugin() {
|
||||
return this.plugin;
|
||||
}
|
||||
|
||||
public PermissibleBase getOldPermissible() {
|
||||
return this.oldPermissible;
|
||||
}
|
||||
|
||||
public AtomicBoolean getActive() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
public void setOldPermissible(PermissibleBase oldPermissible) {
|
||||
this.oldPermissible = oldPermissible;
|
||||
}
|
||||
}
|
||||
+65
-59
@@ -23,20 +23,17 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.bukkit.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
package me.lucko.luckperms.bukkit.model.permissible;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.bukkit.model.dummy.DummyPlugin;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.ImmutableTransientNode;
|
||||
import me.lucko.luckperms.common.node.NodeFactory;
|
||||
|
||||
import org.bukkit.permissions.PermissibleBase;
|
||||
import org.bukkit.permissions.PermissionAttachment;
|
||||
import org.bukkit.permissions.PermissionRemovedExecutor;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
@@ -46,6 +43,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -62,20 +60,17 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
private static final Field PERMISSION_ATTACHMENT_PERMISSIONS_FIELD;
|
||||
|
||||
static {
|
||||
Field permissionAttachmentPermissionsField;
|
||||
try {
|
||||
permissionAttachmentPermissionsField = PermissibleBase.class.getDeclaredField("permissions");
|
||||
permissionAttachmentPermissionsField.setAccessible(true);
|
||||
PERMISSION_ATTACHMENT_PERMISSIONS_FIELD = PermissionAttachment.class.getDeclaredField("permissions");
|
||||
PERMISSION_ATTACHMENT_PERMISSIONS_FIELD.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
PERMISSION_ATTACHMENT_PERMISSIONS_FIELD = permissionAttachmentPermissionsField;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parent LPPermissible
|
||||
*/
|
||||
@Getter
|
||||
private final LPPermissible permissible;
|
||||
|
||||
/**
|
||||
@@ -96,8 +91,6 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
/**
|
||||
* Callback to run when the attachment is removed
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private PermissionRemovedExecutor removalCallback = null;
|
||||
|
||||
public LPPermissionAttachment(LPPermissible permissible, Plugin owner) {
|
||||
@@ -114,7 +107,7 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
this.owner = null;
|
||||
|
||||
// copy
|
||||
perms.putAll(bukkit.getPermissions());
|
||||
this.perms.putAll(bukkit.getPermissions());
|
||||
|
||||
injectFakeMap();
|
||||
}
|
||||
@@ -131,22 +124,36 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
FakeBackingMap fakeMap = new FakeBackingMap();
|
||||
|
||||
try {
|
||||
// what's this doing, ay?
|
||||
// the field we need to modify is in the superclass - it's set to private
|
||||
// so we have to use reflection to modify it.
|
||||
// the field we need to modify is in the superclass - it has private
|
||||
// and final modifiers so we have to use reflection to modify it.
|
||||
PERMISSION_ATTACHMENT_PERMISSIONS_FIELD.set(this, fakeMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LPPermissible getPermissible() {
|
||||
return this.permissible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionRemovedExecutor getRemovalCallback() {
|
||||
return this.removalCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemovalCallback(PermissionRemovedExecutor removalCallback) {
|
||||
this.removalCallback = removalCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks this attachment with the parent {@link User} instance.
|
||||
*/
|
||||
public void hook() {
|
||||
hooked = true;
|
||||
permissible.attachments.add(this);
|
||||
for (Map.Entry<String, Boolean> entry : perms.entrySet()) {
|
||||
this.hooked = true;
|
||||
this.permissible.attachments.add(this);
|
||||
for (Map.Entry<String, Boolean> entry : this.perms.entrySet()) {
|
||||
if (entry.getKey() == null || entry.getKey().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -155,50 +162,50 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
}
|
||||
|
||||
private void setPermissionInternal(String name, boolean value) {
|
||||
if (!permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) {
|
||||
if (!this.permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// construct a node for the permission being set
|
||||
// we use the servers static context to *try* to ensure that the node will apply
|
||||
Node node = NodeFactory.newBuilder(name)
|
||||
Node node = NodeFactory.builder(name)
|
||||
.setValue(value)
|
||||
.withExtraContext(permissible.getPlugin().getContextManager().getStaticContext())
|
||||
.withExtraContext(this.permissible.getPlugin().getContextManager().getStaticContext())
|
||||
.build();
|
||||
|
||||
// convert the constructed node to a transient node instance to refer back to this attachment
|
||||
ImmutableTransientNode transientNode = ImmutableTransientNode.of(node, this);
|
||||
ImmutableTransientNode<LPPermissionAttachment> transientNode = ImmutableTransientNode.of(node, this);
|
||||
|
||||
// set the transient node
|
||||
User user = permissible.getUser();
|
||||
User user = this.permissible.getUser();
|
||||
if (user.setTransientPermission(transientNode).asBoolean()) {
|
||||
user.getRefreshBuffer().request();
|
||||
user.reloadCachedData();
|
||||
}
|
||||
}
|
||||
|
||||
private void unsetPermissionInternal(String name) {
|
||||
if (!permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) {
|
||||
if (!this.permissible.getPlugin().getConfiguration().get(ConfigKeys.APPLY_BUKKIT_ATTACHMENT_PERMISSIONS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove transient permissions from the holder which were added by this attachment & equal the permission
|
||||
User user = permissible.getUser();
|
||||
User user = this.permissible.getUser();
|
||||
if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name))) {
|
||||
user.getRefreshBuffer().request();
|
||||
user.reloadCachedData();
|
||||
}
|
||||
}
|
||||
|
||||
private void clearInternal() {
|
||||
// remove all transient permissions added by this attachment
|
||||
User user = permissible.getUser();
|
||||
User user = this.permissible.getUser();
|
||||
if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this)) {
|
||||
user.getRefreshBuffer().request();
|
||||
user.reloadCachedData();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove() {
|
||||
if (!hooked) {
|
||||
if (!this.hooked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -206,31 +213,31 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
clearInternal();
|
||||
|
||||
// run the callback
|
||||
if (removalCallback != null) {
|
||||
removalCallback.attachmentRemoved(this);
|
||||
if (this.removalCallback != null) {
|
||||
this.removalCallback.attachmentRemoved(this);
|
||||
}
|
||||
|
||||
// unhook from the permissible
|
||||
hooked = false;
|
||||
permissible.attachments.remove(this);
|
||||
this.hooked = false;
|
||||
this.permissible.attachments.remove(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermission(String name, boolean value) {
|
||||
Preconditions.checkNotNull(name, "name is null");
|
||||
Objects.requireNonNull(name, "name is null");
|
||||
Preconditions.checkArgument(!name.isEmpty(), "name is empty");
|
||||
|
||||
String permission = name.toLowerCase();
|
||||
|
||||
Boolean previous = perms.put(permission, value);
|
||||
Boolean previous = this.perms.put(permission, value);
|
||||
if (previous != null && previous == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're not hooked, thn don't actually apply the change
|
||||
// if we're not hooked, then don't actually apply the change
|
||||
// it will get applied on hook - if that ever happens
|
||||
if (!hooked) {
|
||||
if (!this.hooked) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -243,19 +250,19 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
|
||||
@Override
|
||||
public void unsetPermission(String name) {
|
||||
Preconditions.checkNotNull(name, "name is null");
|
||||
Objects.requireNonNull(name, "name is null");
|
||||
Preconditions.checkArgument(!name.isEmpty(), "name is empty");
|
||||
|
||||
String permission = name.toLowerCase();
|
||||
|
||||
Boolean previous = perms.remove(permission);
|
||||
Boolean previous = this.perms.remove(permission);
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're not hooked, thn don't actually apply the change
|
||||
// if we're not hooked, then don't actually apply the change
|
||||
// it will get applied on hook - if that ever happens
|
||||
if (!hooked) {
|
||||
if (!this.hooked) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -264,12 +271,12 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
|
||||
@Override
|
||||
public Map<String, Boolean> getPermissions() {
|
||||
return perms;
|
||||
return this.perms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return owner != null ? owner : permissible.getPlugin();
|
||||
return this.owner != null ? this.owner : this.permissible.getPlugin();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -296,9 +303,8 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
|
||||
@Override
|
||||
public Boolean put(String key, Boolean value) {
|
||||
|
||||
// grab the previous result, so we can still satisfy the method signature of Map
|
||||
Boolean previous = perms.get(key);
|
||||
Boolean previous = LPPermissionAttachment.this.perms.get(key);
|
||||
|
||||
// proxy the call back through the PermissionAttachment instance
|
||||
setPermission(key, value);
|
||||
@@ -317,7 +323,7 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
String permission = ((String) key);
|
||||
|
||||
// grab the previous result, so we can still satisfy the method signature of Map
|
||||
Boolean previous = perms.get(permission);
|
||||
Boolean previous = LPPermissionAttachment.this.perms.get(permission);
|
||||
|
||||
// proxy the call back through the PermissionAttachment instance
|
||||
unsetPermission(permission);
|
||||
@@ -336,61 +342,61 @@ public class LPPermissionAttachment extends PermissionAttachment {
|
||||
@Override
|
||||
public void clear() {
|
||||
// remove the permissions which have already been applied
|
||||
if (hooked) {
|
||||
if (LPPermissionAttachment.this.hooked) {
|
||||
clearInternal();
|
||||
}
|
||||
|
||||
// clear the backing map
|
||||
perms.clear();
|
||||
LPPermissionAttachment.this.perms.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
// return the size of the permissions map - probably the most accurate value we have
|
||||
return perms.size();
|
||||
return LPPermissionAttachment.this.perms.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
// return if the permissions map is empty - again probably the most accurate thing
|
||||
// we can return
|
||||
return perms.isEmpty();
|
||||
return LPPermissionAttachment.this.perms.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
// just proxy
|
||||
return perms.containsKey(key);
|
||||
return LPPermissionAttachment.this.perms.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
// just proxy
|
||||
return perms.containsValue(value);
|
||||
return LPPermissionAttachment.this.perms.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean get(Object key) {
|
||||
// just proxy
|
||||
return perms.get(key);
|
||||
return LPPermissionAttachment.this.perms.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
// just proxy
|
||||
return perms.keySet();
|
||||
return LPPermissionAttachment.this.perms.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Boolean> values() {
|
||||
// just proxy
|
||||
return perms.values();
|
||||
return LPPermissionAttachment.this.perms.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, Boolean>> entrySet() {
|
||||
// just proxy
|
||||
return perms.entrySet();
|
||||
return LPPermissionAttachment.this.perms.entrySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.bukkit.model.permissible;
|
||||
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.bukkit.model.dummy.DummyPermissibleBase;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.verbose.CheckOrigin;
|
||||
import me.lucko.luckperms.common.verbose.VerboseHandler;
|
||||
|
||||
import org.bukkit.permissions.PermissibleBase;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionAttachment;
|
||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A PermissibleBase extension which logs permission checks to the
|
||||
* plugin's {@link VerboseHandler} facility.
|
||||
*
|
||||
* Method calls are forwarded to the delegate permissible.
|
||||
*/
|
||||
public class MonitoredPermissibleBase extends PermissibleBase {
|
||||
private final LuckPermsPlugin plugin;
|
||||
private final PermissibleBase delegate;
|
||||
private final String name;
|
||||
|
||||
// remains false until the object has been constructed
|
||||
// necessary to catch the superclass call to #recalculatePermissions on init
|
||||
@SuppressWarnings("UnusedAssignment")
|
||||
private boolean initialised = false;
|
||||
|
||||
public MonitoredPermissibleBase(LuckPermsPlugin plugin, PermissibleBase delegate, String name) {
|
||||
super(null);
|
||||
DummyPermissibleBase.nullFields(this);
|
||||
|
||||
this.plugin = plugin;
|
||||
this.delegate = delegate;
|
||||
this.name = name;
|
||||
this.initialised = true;
|
||||
|
||||
// since we effectively cancel the execution of this call in the super
|
||||
// constructor we need to call it again.
|
||||
recalculatePermissions();
|
||||
}
|
||||
|
||||
private void logCheck(CheckOrigin origin, String permission, boolean result) {
|
||||
this.plugin.getVerboseHandler().offerCheckData(origin, this.name, ContextSet.empty(), permission, Tristate.fromBoolean(result));
|
||||
this.plugin.getPermissionVault().offer(permission);
|
||||
}
|
||||
|
||||
PermissibleBase getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(String permission) {
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
final boolean result = this.delegate.isPermissionSet(permission);
|
||||
logCheck(CheckOrigin.PLATFORM_LOOKUP_CHECK, permission, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPermissionSet(Permission permission) {
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
final boolean result = this.delegate.isPermissionSet(permission);
|
||||
logCheck(CheckOrigin.PLATFORM_LOOKUP_CHECK, permission.getName(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
final boolean result = this.delegate.hasPermission(permission);
|
||||
logCheck(CheckOrigin.PLATFORM_PERMISSION_CHECK, permission, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Permission permission) {
|
||||
if (permission == null) {
|
||||
throw new NullPointerException("permission");
|
||||
}
|
||||
|
||||
final boolean result = this.delegate.hasPermission(permission);
|
||||
logCheck(CheckOrigin.PLATFORM_PERMISSION_CHECK, permission.getName(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculatePermissions() {
|
||||
if (!this.initialised) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.delegate.recalculatePermissions();
|
||||
}
|
||||
|
||||
// just forward calls to the delegate permissible
|
||||
@Override public boolean isOp() { return this.delegate.isOp(); }
|
||||
@Override public void setOp(boolean value) { this.delegate.setOp(value); }
|
||||
@Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { return this.delegate.addAttachment(plugin, name, value); }
|
||||
@Override public PermissionAttachment addAttachment(Plugin plugin) { return this.delegate.addAttachment(plugin); }
|
||||
@Override public void removeAttachment(PermissionAttachment attachment) { this.delegate.removeAttachment(attachment); }
|
||||
@Override public void clearPermissions() { this.delegate.clearPermissions(); }
|
||||
@Override public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { return this.delegate.addAttachment(plugin, name, value, ticks); }
|
||||
@Override public PermissionAttachment addAttachment(Plugin plugin, int ticks) { return this.delegate.addAttachment(plugin, ticks); }
|
||||
@Override public Set<PermissionAttachmentInfo> getEffectivePermissions() { return this.delegate.getEffectivePermissions(); }
|
||||
|
||||
}
|
||||
+12
-21
@@ -23,11 +23,10 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.bukkit.model;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
package me.lucko.luckperms.bukkit.model.permissible;
|
||||
|
||||
import me.lucko.luckperms.bukkit.compat.ReflectionUtil;
|
||||
import me.lucko.luckperms.bukkit.model.dummy.DummyPermissibleBase;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.PermissibleBase;
|
||||
@@ -42,12 +41,11 @@ import java.util.List;
|
||||
* This allows LuckPerms to directly intercept permission checks and take over all handling of
|
||||
* checks made by plugins.
|
||||
*/
|
||||
@UtilityClass
|
||||
public class Injector {
|
||||
public final class PermissibleInjector {
|
||||
|
||||
/**
|
||||
* All permission checks made on standard Bukkit objects are effectively proxied to a
|
||||
* {@link PermissibleBase} object, held as a parameter on the object.
|
||||
* {@link PermissibleBase} object, held as a variable on the object.
|
||||
*
|
||||
* This field is where the permissible is stored on a HumanEntity.
|
||||
*/
|
||||
@@ -59,35 +57,26 @@ public class Injector {
|
||||
private static final Field PERMISSIBLE_BASE_ATTACHMENTS_FIELD;
|
||||
|
||||
static {
|
||||
Field humanEntityPermissibleField;
|
||||
Field permissibleBaseAttachmentsField;
|
||||
|
||||
try {
|
||||
// Catch all. If this setup doesn't fully complete without
|
||||
// exceptions, then the Injector will not work.
|
||||
|
||||
// Try to load the permissible field.
|
||||
Field humanEntityPermissibleField;
|
||||
try {
|
||||
// craftbukkit
|
||||
humanEntityPermissibleField = ReflectionUtil.obcClass("entity.CraftHumanEntity").getDeclaredField("perm");
|
||||
humanEntityPermissibleField.setAccessible(true);
|
||||
|
||||
} catch (Exception e) {
|
||||
// glowstone
|
||||
humanEntityPermissibleField = Class.forName("net.glowstone.entity.GlowHumanEntity").getDeclaredField("permissions");
|
||||
humanEntityPermissibleField.setAccessible(true);
|
||||
}
|
||||
HUMAN_ENTITY_PERMISSIBLE_FIELD = humanEntityPermissibleField;
|
||||
|
||||
// Try to load the attachments field.
|
||||
permissibleBaseAttachmentsField = PermissibleBase.class.getDeclaredField("attachments");
|
||||
permissibleBaseAttachmentsField.setAccessible(true);
|
||||
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Injector did not init successfully.", t);
|
||||
PERMISSIBLE_BASE_ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
|
||||
PERMISSIBLE_BASE_ATTACHMENTS_FIELD.setAccessible(true);
|
||||
} catch (ClassNotFoundException | NoSuchFieldException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
|
||||
HUMAN_ENTITY_PERMISSIBLE_FIELD = humanEntityPermissibleField;
|
||||
PERMISSIBLE_BASE_ATTACHMENTS_FIELD = permissibleBaseAttachmentsField;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,4 +151,6 @@ public class Injector {
|
||||
}
|
||||
}
|
||||
|
||||
private PermissibleInjector() {}
|
||||
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.bukkit.model.permissible;
|
||||
|
||||
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
|
||||
import me.lucko.luckperms.bukkit.compat.ReflectionUtil;
|
||||
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.permissions.PermissibleBase;
|
||||
import org.bukkit.permissions.ServerOperator;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Injects {@link MonitoredPermissibleBase}s into non-player permissibles on
|
||||
* the server so their checks can be monitored by the verbose facility.
|
||||
*/
|
||||
public class PermissibleMonitoringInjector implements Runnable {
|
||||
private final LPBukkitPlugin plugin;
|
||||
|
||||
public PermissibleMonitoringInjector(LPBukkitPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
injectConsole();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
injectCommandBlock();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
injectEntity();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private MonitoredPermissibleBase wrap(PermissibleBase permBase, String name) {
|
||||
Objects.requireNonNull(permBase, "permBase");
|
||||
|
||||
// unwrap any previous injection
|
||||
if (permBase instanceof MonitoredPermissibleBase) {
|
||||
permBase = ((MonitoredPermissibleBase) permBase).getDelegate();
|
||||
}
|
||||
|
||||
// create a monitored instance which delegates to the previous PermissibleBase
|
||||
return new MonitoredPermissibleBase(this.plugin, permBase, name);
|
||||
}
|
||||
|
||||
private void injectConsole() throws Exception {
|
||||
ConsoleCommandSender consoleSender = this.plugin.getServer().getConsoleSender();
|
||||
|
||||
// get the ServerCommandSender class
|
||||
Class<?> serverCommandSenderClass = ReflectionUtil.obcClass("command.ServerCommandSender");
|
||||
|
||||
// get the perm field
|
||||
Field permField = serverCommandSenderClass.getDeclaredField("perm");
|
||||
permField.setAccessible(true);
|
||||
|
||||
// get the PermissibleBase instance
|
||||
PermissibleBase permBase = (PermissibleBase) permField.get(consoleSender);
|
||||
|
||||
// create a monitored instance which delegates to the previous PermissibleBase
|
||||
MonitoredPermissibleBase newPermBase = wrap(permBase, "internal/console");
|
||||
|
||||
// inject the monitored instance
|
||||
permField.set(consoleSender, newPermBase);
|
||||
}
|
||||
|
||||
private void injectCommandBlock() throws Exception {
|
||||
// get the ServerCommandSender class
|
||||
Class<?> serverCommandSenderClass = ReflectionUtil.obcClass("command.ServerCommandSender");
|
||||
|
||||
// get the blockPermInst field
|
||||
Field permField = serverCommandSenderClass.getDeclaredField("blockPermInst");
|
||||
permField.setAccessible(true);
|
||||
|
||||
// get the PermissibleBase instance
|
||||
PermissibleBase permBase = (PermissibleBase) permField.get(null);
|
||||
|
||||
// if no commandblock senders have been made yet, this field will be null
|
||||
// we can just initialise one anyway
|
||||
if (permBase == null) {
|
||||
permBase = new PermissibleBase(new CommandBlockServerOperator());
|
||||
}
|
||||
|
||||
// create a monitored instance which delegates to the previous PermissibleBase
|
||||
MonitoredPermissibleBase newPermBase = wrap(permBase, "internal/commandblock");
|
||||
|
||||
// inject the monitored instance
|
||||
permField.set(null, newPermBase);
|
||||
}
|
||||
|
||||
private void injectEntity() throws Exception {
|
||||
// get the CraftEntity class
|
||||
Class<?> entityClass = ReflectionUtil.obcClass("entity.CraftEntity");
|
||||
|
||||
// get the method used to obtain a PermissibleBase
|
||||
// this method will initialise a new PB instance if one doesn't yet exist
|
||||
Method getPermissibleBaseMethod = entityClass.getDeclaredMethod("getPermissibleBase");
|
||||
getPermissibleBaseMethod.setAccessible(true);
|
||||
|
||||
// get the PermissibleBase instance
|
||||
PermissibleBase permBase = (PermissibleBase) getPermissibleBaseMethod.invoke(null);
|
||||
|
||||
// get the perm field on CraftEntity
|
||||
Field permField = entityClass.getDeclaredField("perm");
|
||||
permField.setAccessible(true);
|
||||
|
||||
// create a monitored instance which delegates to the previous PermissibleBase
|
||||
MonitoredPermissibleBase newPermBase = wrap(permBase, "internal/entity");
|
||||
|
||||
// inject the monitored instance
|
||||
permField.set(null, newPermBase);
|
||||
}
|
||||
|
||||
// behaviour copied from the implementation of obc.command.CraftBlockCommandSender
|
||||
private static final class CommandBlockServerOperator implements ServerOperator {
|
||||
@Override
|
||||
public boolean isOp() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOp(boolean value) {
|
||||
throw new UnsupportedOperationException("Cannot change operator status of a block");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 630 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user