Compare commits

..
146 Commits
Author SHA1 Message Date
Luck 2429831153 API changes for 2.11 2016-10-05 21:40:54 +01:00
Luck 80d2246634 Fix exporting users not in the default group 2016-10-04 20:48:58 +01:00
Luck 56df2477ca Fix UnsupportedOperationExceptions with #getPermissions 2016-10-04 17:30:27 +01:00
Luck 9b4384dc33 Revert: Merge dummy implementations 2016-10-04 17:15:10 +01:00
Luck bc65857831 Fix CME 2016-10-04 17:13:32 +01:00
Luck de462d2385 Merge dummy implementations 2016-10-04 16:58:54 +01:00
Luck 90150428be Inject a dummy on quit instead of a real PermBase instance 2016-10-04 16:19:41 +01:00
Luck 2a7f951f4f Fix NPE / crash on player quit 2016-10-04 16:10:06 +01:00
Luck 82789328c1 Pre-process permissions and apply immediately on login events 2016-10-04 14:57:00 +01:00
Luck 13bccd1d3f Fix users not receiving permission updates 2016-10-03 21:41:47 +01:00
Luck 43c514132d Cache node deserialization 2016-10-03 21:18:59 +01:00
Luck 9e0fdcbfee Fix memory leak, woops 2016-10-03 20:43:36 +01:00
Luck 008dc3fc52 Move some classes around 2016-10-03 20:42:11 +01:00
Luck 9fde237d9b Swap slow get method 2016-10-03 19:53:43 +01:00
Luck d626c08b4c Small fix for #getAllWithPermission 2016-10-03 16:45:52 +01:00
Luck 58e223e74b Implement bulk prefix/suffix removal 2016-10-02 21:17:59 +01:00
Luck 1da1df156e Fix issues related to Node comparisons 2016-10-02 15:44:46 +01:00
Luck 3442372281 Cleanup random debug messages 2016-10-02 15:43:44 +01:00
Luck 1b3b50c113 Refactor PermissionHolder & Sponge subjects 2016-10-02 15:05:53 +01:00
Luck 18e126b781 Fix issue with permissions sometimes not being removed 2016-10-02 10:33:58 +01:00
Luck c88600adda Make User constructor protected. Closes #18. 2016-10-01 22:56:57 +01:00
Luck 4787361e66 Implement locks to hopefully resolve race conditions with I/O - experimental 2016-10-01 19:03:05 +01:00
Luck b5ece8b5bd Fix exception 2016-09-30 22:26:39 +01:00
Luck 7ee3116f29 Maybe fix broken inheritance 2016-09-30 20:43:08 +01:00
Luck 1fc0d77971 Cache & fix (and maybe break) Vault Chat implementation 2016-09-30 20:08:59 +01:00
Luck a32ab4bf7f Show where a permission was inherited from in the inheritsperm command 2016-09-30 19:21:19 +01:00
Luck 2d8083bf18 Another Vault fix & cleanup User / Group classes 2016-09-30 17:45:29 +01:00
Luck 63b9be9845 Change Vault behavior & cleanup Node class 2016-09-30 17:09:19 +01:00
Luck 85a83d8c71 Add logging output to Vault 2016-09-29 22:04:46 +01:00
Luck 71b33a296c Calculate Sponge defaults using context 2016-09-29 20:38:03 +01:00
Luck becdfd6f03 Silence subject load errors unless debug-permission-checks is true 2016-09-29 20:02:38 +01:00
Luck 41744f9416 Add clone commands 2016-09-29 18:07:41 +01:00
Luck cb89a92ba9 Message colour changes and standardization 2016-09-28 21:16:07 +01:00
Luck 93d54373c6 Add detailed command info system 2016-09-28 20:59:08 +01:00
Luck 385addfe08 cleanup 2016-09-27 20:21:13 +01:00
Luck 4c21957b04 Cache Vault lookups & properly sort listnodes output 2016-09-27 20:18:06 +01:00
Luck ab6b011225 Fix permission lookups being case sensitive 2016-09-26 18:52:01 +01:00
Luck e72d91503c Maybe nicer looking messages 2016-09-26 18:42:46 +01:00
Luck b9ee206908 Add some more bulk edit commands 2016-09-25 21:23:48 +01:00
Luck 4c9abba5fa Maybe fix vault (again) 2016-09-25 19:26:36 +01:00
Luck 3424b0542a Release 2.10 2016-09-25 17:32:58 +01:00
Luck fec90b8958 Add bulkchange commands 2016-09-25 17:29:11 +01:00
Luck 4da7fe4ff9 Small fixes to context system 2016-09-25 16:55:14 +01:00
Luck c53623dd67 Make command usage output easier to read 2016-09-25 14:34:27 +01:00
Luck 6540ffa4dd Refactor storage 2016-09-25 12:47:15 +01:00
Luck 5ee6db02ff Proper context implementation - WIP 2016-09-25 10:19:36 +01:00
Luck d8221f466a Fix messages not being formatted correctly 2016-09-24 17:22:33 +01:00
Luck 462bea2a09 Remove broken repo, maybe fix build 2016-09-24 16:58:45 +01:00
Luck f452a97278 fix typo 2016-09-23 22:19:17 +01:00
Luck eb9d2be587 Add locale file support 2016-09-23 22:14:40 +01:00
Luck df1e747861 Add commands to view inherited groups 2016-09-23 19:23:31 +01:00
Luck f48595b1ff Add world and group name rewriting, refactor configs 2016-09-23 18:58:19 +01:00
Luck 12cd4e19cb Update config comments 2016-09-22 18:25:49 +01:00
Luck a195e94522 Calculate defaults after all plugins have loaded 2016-09-22 17:34:51 +01:00
Luck 02abb39dc2 Change #isPermissionSet to be more like the Bukkit impl 2016-09-21 22:04:18 +01:00
Luck f7a0b1ef78 Refactor default permissions to be less bad 2016-09-21 21:30:42 +01:00
Luck 7678f25879 Fix default permissions not properly applying on Bukkit 2016-09-21 17:40:05 +01:00
Luck fa9324bda8 Implement basic standalone base, working towards #9 2016-09-20 21:50:28 +01:00
Luck 1e72f9d8fe Listen on lowest priority, may fix issues with plugins checking for permissions on login events 2016-09-20 21:04:53 +01:00
Luck 50fd954599 Use a BiMap in UuidCache 2016-09-19 21:13:45 +01:00
Luck b1e34b55f8 Re-add regex permissions & actually take note of the config options 2016-09-19 21:00:23 +01:00
Luck cff1b8a411 Separate PermissionProcessor into own class 2016-09-19 20:45:37 +01:00
Luck 6ffa6720be Make more specific wildcards take priority over less specific ones 2016-09-19 20:36:52 +01:00
Luck f52a106165 Order nodes alphabetically after other priority rules have been applied 2016-09-19 17:31:00 +01:00
Luck bf247b71e1 Fix possible race condition & maybe use better Hikari flags 2016-09-18 23:13:20 +01:00
Luck 66818e4c82 Fix build 2016-09-17 23:46:11 +01:00
Luck 23d53d39b4 Refactor permission calculation 2016-09-17 23:15:07 +01:00
Luck 30ea5a5b69 Fix no permissions applying when include-global is false, and better support per-server/per-world groups 2016-09-17 19:05:56 +01:00
Luck e7a210dc06 Update default configs 2016-09-16 22:09:55 +01:00
Luck 497de9824a Actually pass on a single wildcard node so it gets applied 2016-09-16 21:46:55 +01:00
Luck be74b50762 Fixes for offline mode servers 2016-09-16 21:24:25 +01:00
Luck f2e06b56e7 Cache permission lookups 2016-09-16 21:11:12 +01:00
Luck f6aa20c300 Fix compile 2016-09-15 17:12:19 +01:00
Luck 5e16134f6f Provide prefixes and suffixes through the PS options 2016-09-15 17:06:27 +01:00
Luck 6165fcbb21 Fix #getDefaults for users 2016-09-15 16:54:12 +01:00
Luck 831e882db0 Actually apply default permissions on Sponge 2016-09-15 16:53:03 +01:00
Luck feb8bf4bd3 Update readme 2016-09-14 20:16:36 +01:00
Luck ce3ee866ca Sponge fixes 2016-09-14 19:37:20 +01:00
Luck d29dd35689 Support the latest powerfulperms so-called "api" 2016-09-13 20:14:16 +01:00
Luck 3a0a89853f Apply negated wildcards 2016-09-13 17:23:16 +01:00
Luck e7ca79d251 Use Splitter instead of Patterns, don't split arguments in quotes 2016-09-12 19:00:37 +01:00
Luck 22426bab33 Update placeholders to use the latest API version 2016-09-11 15:36:19 +01:00
Luck 116094ea96 Maybe fix an issue with per-server prefixes/suffixes 2016-09-11 14:34:38 +01:00
Luck 9df39a9545 Inject PermissibleBase instead of using attachments. Resolves #14 2016-09-11 11:50:59 +01:00
Luck b8f44df723 Add missing headers 2016-09-10 22:40:36 +01:00
Luck 74f275eb69 Add more info the importer error output 2016-09-10 22:08:07 +01:00
Luck 1142cbcb2c Add platform type to the info command 2016-09-10 21:54:31 +01:00
Luck 37749e5829 Don't apply the default group when exporting users 2016-09-10 16:53:04 +01:00
Luck f9025c7a51 fix #getUniqueUsers for SQL storage types 2016-09-10 16:38:37 +01:00
Luck c7bfc38477 Build an extra jar with guava and gson shaded to support 1.7 2016-09-10 16:32:37 +01:00
Luck 7ca394d7b9 Add some extra console output to the debug command 2016-09-10 14:35:12 +01:00
Luck 31884ea59d Maybe fix race condition with messages not sending 2016-09-10 14:31:42 +01:00
Luck e79b7c7023 Update readme 2016-09-10 09:04:59 +01:00
Luck 1fdb4e8ade Merge pull request #11 from HeroCC/patch-1
Fix PermissionsEx false node migration
2016-09-09 21:49:43 +01:00
HeroCC 53db8c5928 Fix PermissionsEx false node migration
PermissionsEx false nodes begin with a '-' not a '!'
2016-09-09 16:39:13 -04:00
Luck 07514000a4 Fix global groups not being migrated from GroupManager 2016-09-09 18:41:15 +01:00
Luck f28721e55a Possible fix for permissions not applying on BungeeCord 2016-09-09 18:32:50 +01:00
Luck fc230291d3 Fix ops not receiving all permission nodes 2016-09-09 18:00:38 +01:00
Luck 89ff4cfa40 Add a much better system for exporting data 2016-09-08 21:01:37 +01:00
Luck d786b6abb2 Fix removing negative nodes 2016-09-08 17:28:31 +01:00
Luck fb0e694155 Add auto-op. Closes #8 2016-09-07 23:21:24 +01:00
Luck cd6cac1fb6 Fix bad grammar in config option comment 2016-09-07 21:01:03 +01:00
Luck ea07f05097 Release 2.8 2016-09-07 20:53:33 +01:00
Luck d84767af68 Fix blank users not receiving group permission updates 2016-09-06 22:28:37 +01:00
Luck aab73a8d81 Add more shorthand nodes 2016-09-06 22:27:46 +01:00
Luck 995fc2ccd8 Bump internal release version and add missing javadoc 2016-09-05 20:06:41 +01:00
Luck 60bcd5f643 Release 2.7 2016-09-05 20:00:01 +01:00
Luck ba04fb320b Remove unnecessary reflection 2016-09-05 16:44:59 +01:00
Luck 770b7dc282 Fix permissions not being removed on Sponge 2016-09-04 17:59:01 +01:00
Luck ed85eee36c Fix inherited permissions not applying on Sponge 2016-09-04 16:25:10 +01:00
Luck 23df8a2df8 Some small fixes 2016-09-04 16:24:36 +01:00
Luck 36cd388607 Slight re-wording 2016-09-03 21:34:35 +01:00
Luck 4d48ef1939 Add comment about Paper's "bungee-online-mode" option. Resolves #6 2016-09-03 21:30:37 +01:00
Luck 7970e2a1c6 Add bPermissions migration 2016-09-03 18:15:52 +01:00
Luck cee96d7daa make SenderFactory nicer & maybe faster 2016-09-03 00:14:40 +01:00
Luck 861c1ddd69 remove #childInit 2016-09-02 19:19:22 +01:00
Luck 5dec5a54cb Add YAML storage support 2016-09-02 19:15:31 +01:00
Luck 5c760aabd4 Fire PermissionRefreshEvent after the nodes have actually updated 2016-09-01 13:00:22 +01:00
Luck 1a2fa5d8c3 Fix log notify 2016-08-31 21:02:06 +01:00
Luck ef0c53da82 fix formatting 2016-08-31 09:38:47 +01:00
Luck 3fd62a80e2 Add support for split storage 2016-08-31 09:30:19 +01:00
Luck 5c19dd8b15 .-. 2016-08-30 22:38:08 +01:00
Luck 1fc88e147f Make output of listnodes nicer 2016-08-30 18:04:30 +01:00
Luck 9b4bbe0500 Make log notifications toggleable 2016-08-30 16:02:01 +01:00
Luck b37fdd3619 Add user to group instead of failing in setprimarygroup command 2016-08-30 13:23:48 +01:00
Luck ab00c41452 Fix NPE, and properly send possible permissions in BukkitUser 2016-08-30 13:06:36 +01:00
Luck c15727377b Apply single '*' wildcard 2016-08-30 12:49:46 +01:00
Luck 1e6f32bdc4 Add extra disallowed character 2016-08-30 11:54:18 +01:00
Luck 42e69c98a7 Change name limitation to be more flexible 2016-08-30 11:52:32 +01:00
Luck a217727bb4 Match negative priorities and fix PEX weight migration 2016-08-30 11:04:34 +01:00
Luck ba10b1c06e Fix removal of temporary nodes 2016-08-30 10:22:55 +01:00
Luck 075ba33e8b Add prefix/suffix/meta commands 2016-08-30 10:22:25 +01:00
Luck 19dbc7d90a formatting pls 2016-08-30 07:47:52 +01:00
Boomclaw ade70bb7ba PEX migration carries weights of prefixes/suffixes of groups and individuals (#5)
* PEX migration carries weights of prefixes/suffixes of groups and individuals.

* Forgot to increase maxGroupWeight.
2016-08-29 22:45:19 +01:00
Luck 4b67913eee getGroup methods should not include inherited groups 2016-08-29 22:03:10 +01:00
Luck 75858c1555 Use new Node system in #getGroups 2016-08-29 21:38:47 +01:00
Luck 659baaf5fb Make #getPlayerGroups return global too 2016-08-29 21:35:11 +01:00
Luck 3c83c578eb Fix typo 2016-08-29 18:40:37 +01:00
Luck 3ddbf1da87 Migrate PEX primary groups 2016-08-29 17:57:47 +01:00
Luck 35cb5b81dc hopefully fix server stopped responding error with SkipListSet 2016-08-29 16:28:36 +01:00
Luck 02d9b996a7 woops 2016-08-29 15:51:19 +01:00
Luck d4cf6a5ad7 Remove subjects from cache when they leave the server 2016-08-29 13:32:21 +01:00
Luck 9fdb4fa25f Maybe fix sponge permissions not expiring 2016-08-29 13:02:56 +01:00
Luck d5bc1cdf5d Fix not clearing user's permissions, and issue with unsetting permissions 2016-08-29 12:53:05 +01:00
Luck f5995c0afa Fix usernames showing as "null" and NPE related to primaryGroup not copying correctly 2016-08-29 12:11:03 +01:00
Luck 2cc65160b4 Bump release version 2016-08-28 22:12:34 +01:00
242 changed files with 14502 additions and 4277 deletions

No files matched your search

+10 -1
View File
@@ -1,5 +1,6 @@
![alt text](https://i.imgur.com/7TwJZ5e.png "Banner")
# LuckPerms [![Build Status](https://ci.lucko.me/job/LuckPerms/badge/icon)](https://ci.lucko.me/job/LuckPerms/)
A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
An advanced permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
## Links
* **Development Builds** - <https://ci.lucko.me/job/LuckPerms>
@@ -9,9 +10,17 @@ A permissions implementation for Bukkit/Spigot, BungeeCord and Sponge.
## Description
LuckPerms is an advanced permissions implementation aiming to be a flexible, performant and reliable alternative to other permission systems, providing all the features you could ever possibly need in a permissions plugin, along with an extensive and well documented API for developers.
* [Spigot Plugin Page](https://www.spigotmc.org/resources/luckperms-an-advanced-permissions-system.28140/ "Spigot Plugin Page")
* [Sponge Plugin Page](https://forums.spongepowered.org/t/luckperms-an-advanced-permissions-system/14274 "Sponge Plugin Page")
## LuckPerms vs Other Plugins
![alt text](https://static.lucko.me/luckperms-compare-copy.png "Feature comparison")
_Features checked on 23rd Aug 2016. If you find any inaccuracies, please do let me know. I tried to be as fair as possible, and copy all major features from the respective plugin pages._
## License
See LICENSE.md.
## Thanks to:
[![JProfiler](https://www.ej-technologies.com/images/product_banners/jprofiler_large.png)](http://www.ej-technologies.com/products/jprofiler/overview.html)
Thanks to ej-technologies for granting LuckPerms an open source licence to their [java profiling software](http://www.ej-technologies.com/products/jprofiler/overview.html "Java Profiler").
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.11-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import com.google.common.collect.ImmutableMap;
import java.util.Collections;
import java.util.Map;
/**
* Represents the context and options for a permission lookup.
* All values are immutable.
* @since 2.11
*/
public class Contexts {
public static final String SERVER_KEY = "server";
public static final String WORLD_KEY = "world";
/**
* Gets a context that will allow all nodes
* @return a context that will not apply any filters
*/
public static Contexts allowAll() {
return new Contexts(Collections.emptyMap(), true, true, true, true, true);
}
public static Contexts of(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
return new Contexts(context, includeGlobal, includeGlobalWorld, applyGroups, applyGlobalGroups, applyGlobalWorldGroups);
}
public Contexts(Map<String, String> context, boolean includeGlobal, boolean includeGlobalWorld, boolean applyGroups, boolean applyGlobalGroups, boolean applyGlobalWorldGroups) {
if (context == null) {
throw new NullPointerException("context");
}
this.context = ImmutableMap.copyOf(context);
this.includeGlobal = includeGlobal;
this.includeGlobalWorld = includeGlobalWorld;
this.applyGroups = applyGroups;
this.applyGlobalGroups = applyGlobalGroups;
this.applyGlobalWorldGroups = applyGlobalWorldGroups;
}
/**
* The contexts that apply for this lookup
*
* The keys for servers and worlds are defined as static values.
*/
private final Map<String, String> context;
/**
* If global or non server specific nodes should be applied
*/
private final boolean includeGlobal;
/**
* If global or non world specific nodes should be applied
*/
private final boolean includeGlobalWorld;
/**
* If parent groups should be applied
*/
private final boolean applyGroups;
/**
* If global or non server specific group memberships should be applied
*/
private final boolean applyGlobalGroups;
/**
* If global or non world specific group memberships should be applied
*/
private final boolean applyGlobalWorldGroups;
/**
* Gets the contexts that apply for this lookup
* @return an immutable map of context key value pairs
*/
public Map<String, String> getContext() {
return this.context;
}
/**
* Gets if global or non server specific nodes should be applied
* @return true if global or non server specific nodes should be applied
*/
public boolean isIncludeGlobal() {
return this.includeGlobal;
}
/**
* Gets if global or non world specific nodes should be applied
* @return true if global or non world specific nodes should be applied
*/
public boolean isIncludeGlobalWorld() {
return this.includeGlobalWorld;
}
/**
* Gets if parent groups should be applied
* @return true if parent groups should be applied
*/
public boolean isApplyGroups() {
return this.applyGroups;
}
/**
* Gets if global or non server specific group memberships should be applied
* @return true if global or non server specific group memberships should be applied
*/
public boolean isApplyGlobalGroups() {
return this.applyGlobalGroups;
}
/**
* Gets if global or non world specific group memberships should be applied
* @return true if global or non world specific group memberships should be applied
*/
public boolean isApplyGlobalWorldGroups() {
return this.applyGlobalWorldGroups;
}
public String toString() {
return "Contexts(" +
"context=" + this.getContext() + ", " +
"includeGlobal=" + this.isIncludeGlobal() + ", " +
"includeGlobalWorld=" + this.isIncludeGlobalWorld() + ", " +
"applyGroups=" + this.isApplyGroups() + ", " +
"applyGlobalGroups=" + this.isApplyGlobalGroups() + ", " +
"applyGlobalWorldGroups=" + this.isApplyGlobalWorldGroups() +
")";
}
}
@@ -28,7 +28,7 @@ import java.util.Set;
import java.util.UUID;
/**
* Wrapper interface for the internal Datastore instance
* Interface for the internal Datastore instance
*/
@SuppressWarnings("unused")
public interface Datastore {
@@ -55,7 +55,7 @@ public interface Datastore {
Async async();
/**
* Gets the {@link Future} interface
* Gets the {@link Future} interface.
*
* All operations through this interface are called in a new, separate asynchronous thread, similar to {@link Async}.
* The only difference is that instead of providing a callback, a {@link java.util.concurrent.Future} is returned.
@@ -112,7 +112,7 @@ public interface Datastore {
* @param username the users username. (if you want to specify <code>null</code> here, just input "null" as a string.)
* @return if the operation was performed successfully
* @throws NullPointerException if uuid or username is null
* @since 1.6
* @since 2.6
*/
boolean loadUser(UUID uuid, String username);
@@ -128,14 +128,14 @@ public interface Datastore {
/**
* Removes users from the datastore who are "default". This is called every time the plugin loads.
* @return true if the operation completed successfully
* @since 1.6
* @since 2.6
*/
boolean cleanupUsers();
/**
* Gets a set all user's UUIDs who are "unique", and aren't just a member of the "default" group.
* @return a set of uuids, or null if the operation failed.
* @since 1.6
* @since 2.6
*/
Set<UUID> getUniqueUsers();
@@ -28,7 +28,7 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
/**
* Wrapper interface for internal Group instances
* Interface for internal Group instances
*/
@SuppressWarnings("unused")
public interface Group extends PermissionHolder {
@@ -25,6 +25,8 @@ package me.lucko.luckperms.api;
import me.lucko.luckperms.api.data.DatastoreConfiguration;
import me.lucko.luckperms.api.data.MySQLConfiguration;
import java.util.Map;
/**
* A wrapper interface for the internal LuckPerms configuration, providing read only access.
*/
@@ -43,14 +45,14 @@ public interface LPConfiguration {
/**
* @return the default group, in a node representation
* @deprecated as of 1.6, the default group is always "default"
* @deprecated as of 2.6, the default group is always "default"
*/
@Deprecated
String getDefaultGroupNode();
/**
* @return the name of the default group
* @deprecated as of 1.6, the default group is always "default"
* @deprecated as of 2.6, the default group is always "default"
*/
@Deprecated
String getDefaultGroupName();
@@ -60,6 +62,24 @@ public interface LPConfiguration {
*/
boolean getIncludeGlobalPerms();
/**
* @return if the users on this server will have their global world permissions applied
* @since 2.9
*/
boolean getIncludeGlobalWorldPerms();
/**
* @return true if the platform is applying global groups
* @since 2.9
*/
boolean getApplyGlobalGroups();
/**
* @return true if the platform is applying global world groups
* @since 2.9
*/
boolean getApplyGlobalWorldGroups();
/**
* @return the online mode setting in the config
*/
@@ -80,6 +100,48 @@ public interface LPConfiguration {
*/
boolean getApplyShorthand();
/**
* @return if LuckPerms will send notifications to users when permissions are modified
* @since 2.7
*/
boolean getLogNotify();
/**
* @return true if permission checks are being recorded / debugged
* @since 2.9
*/
boolean getDebugPermissionChecks();
/**
* @return true if the vanilla op system is enabled
* @since 2.8
*/
boolean getEnableOps();
/**
* @return true if opped players are allowed to use LuckPerms commands
* @since 2.8
*/
boolean getCommandsAllowOp();
/**
* @return true if auto op is enabled
* @since 2.9
*/
boolean getAutoOp();
/**
* @return the name of the server used within Vault operations
* @since 2.7
*/
String getVaultServer();
/**
* @return true if global permissions should be considered when retrieving meta or player groups
* @since 2.7
*/
boolean getVaultIncludeGlobal();
/**
* @return the database values set in the configuration
* @deprecated use {@link #getDatastoreConfig()}
@@ -98,4 +160,17 @@ public interface LPConfiguration {
*/
String getStorageMethod();
/**
* @return true if split storage is enabled
* @since 2.7
*/
boolean getSplitStorage();
/**
* @return a map of split storage options, where the key is the storage section, and the value is the storage method.
* For example: key = user, value = json
* @since 2.7
*/
Map<String, String> getSplitStorageOptions();
}
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
/**
* Represents a Node and where it was inherited from.
* @since 2.11
*/
public interface LocalizedNode extends Node {
/**
* Gets the node
* @return the node this instance is representing
*/
Node getNode();
/**
* Gets the location where the {@link Node} is inherited from
* @return where the node was inherited from. Will not return null.
* @see PermissionHolder#getObjectName()
*/
String getLocation();
}
@@ -27,6 +27,7 @@ import java.util.UUID;
/**
* Represents a single entry in a log
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class LogEntry implements Comparable<LogEntry> {
public static LogEntryBuilder builder() {
return new LogEntryBuilder();
@@ -23,7 +23,7 @@
package me.lucko.luckperms.api;
/**
* A wrapper class for platform logger instances.
* A wrapper interface for platform logger instances.
*
* <p> Bukkit/Bungee both use java.util.logging, and Sponge uses org.slf4j. This class wraps those classes so the commons
* module can access a logger.
@@ -22,6 +22,8 @@
package me.lucko.luckperms.api;
import me.lucko.luckperms.api.context.ContextListener;
import me.lucko.luckperms.api.context.IContextCalculator;
import me.lucko.luckperms.api.event.LPListener;
import java.util.Optional;
@@ -41,7 +43,7 @@ public interface LuckPermsApi {
/**
* @return the version of the API running on the platform
* @since 1.6
* @since 2.6
*/
double getApiVersion();
@@ -50,6 +52,12 @@ public interface LuckPermsApi {
*/
String getVersion();
/**
* @return the platform LuckPerms is running on
* @since 2.7
*/
PlatformType getPlatformType();
/**
* Registers a listener to be sent LuckPerms events
* @param listener the listener instance
@@ -138,7 +146,7 @@ public interface LuckPermsApi {
* Unload a user from the internal storage, if they're not currently online.
* @param user the user to unload
* @throws NullPointerException if the user is null
* @since 1.6
* @since 2.6
*/
void cleanupUser(User user);
@@ -181,7 +189,7 @@ public interface LuckPermsApi {
Track getTrack(String name);
/**
* Gets a wrapped tracj object from the track storage. This method does not return null, unlike {@link #getTrack}
* Gets a wrapped track object from the track storage. This method does not return null, unlike {@link #getTrack}
* @param name the name of the track to get
* @return an optional {@link Track} object
* @throws NullPointerException if the name is null
@@ -208,8 +216,22 @@ public interface LuckPermsApi {
* @return a {@link Node.Builder} instance
* @throws IllegalArgumentException if the permission is invalid
* @throws NullPointerException if the permission is null
* @since 1.6
* @since 2.6
*/
Node.Builder buildNode(String permission) throws IllegalArgumentException;
/**
* Register a custom context calculator to the server
* @param contextCalculator the context calculator to register. The type MUST be the player class of the platform.
* @throws ClassCastException if the type is not the player class of the platform.
*/
void registerContextCalculator(IContextCalculator<?> contextCalculator);
/**
* Registers a custom context listener to the server,
* @param contextListener the context listener to register. The type MUST be the player class of the platform.
* @throws ClassCastException if the type is not the player class of the platform.
*/
void registerContextListener(ContextListener<?> contextListener);
}
@@ -0,0 +1,316 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
import me.lucko.luckperms.LuckPerms;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* A collection of utilities to help retrieve meta values for {@link PermissionHolder}s
* @since 2.7
*/
public class MetaUtils {
private MetaUtils(){}
/**
* Escapes special characters used within LuckPerms, so the string can be saved without issues
* @param s the string to escape
* @return an escaped string
* @throws NullPointerException if the string is null
*/
public static String escapeCharacters(String s) {
if (s == null) {
throw new NullPointerException();
}
s = s.replace(".", "{SEP}");
s = s.replace("/", "{FSEP}");
s = s.replace("$", "{DSEP}");
return s;
}
/**
* Unescapes special characters used within LuckPerms, the inverse of {@link #escapeCharacters(String)}
* @param s the string to unescape
* @return an unescaped string
* @throws NullPointerException if the string is null
*/
public static String unescapeCharacters(String s) {
if (s == null) {
throw new NullPointerException();
}
s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$");
return s;
}
/**
* Sets a meta value on a holder
* @param holder the holder to apply the meta node to
* @param server the server to apply the meta on, can be null
* @param world the world to apply the meta on, can be null
* @param node the meta node
* @param value the meta value
* @throws NullPointerException if the holder, node or value is null
* @throws IllegalArgumentException if the node or value is empty
*/
public static void setMeta(PermissionHolder holder, String server, String world, String node, String value) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (node == null) {
throw new NullPointerException("node");
}
if (value == null) {
throw new NullPointerException("value");
}
if (node.equals("")) {
throw new IllegalArgumentException("node is empty");
}
if (value.equals("")) {
throw new IllegalArgumentException("value is empty");
}
if (server == null || server.equals("")) {
server = "global";
}
node = escapeCharacters(node);
value = escapeCharacters(value);
Set<Node> toRemove = new HashSet<>();
for (Node n : holder.getEnduringPermissions()) {
if (n.isMeta() && n.getMeta().getKey().equals(node)) {
toRemove.add(n);
}
}
for (Node n : toRemove) {
try {
holder.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
}
Node.Builder metaNode = LuckPerms.getApi().buildNode("meta." + node + "." + value).setValue(true);
if (!server.equalsIgnoreCase("global")) {
metaNode.setServer(server);
}
if (world != null && !world.equals("")) {
metaNode.setServer(server).setWorld(world);
}
try {
holder.setPermission(metaNode.build());
} catch (ObjectAlreadyHasException ignored) {}
}
/**
* Gets a meta value for the holder
* @param holder the holder to get the meta from
* @param server the server to retrieve the meta on, can be null
* @param world the world to retrieve the meta on, can be null
* @param node the node to get
* @param defaultValue the default value to return if the node is not set
* @param includeGlobal if global nodes should be considered when retrieving the meta
* @return a meta string, or the default value if the user does not have the meta node
* @throws NullPointerException if the holder or node is null
* @throws IllegalArgumentException if the node is empty
*/
public static String getMeta(PermissionHolder holder, String server, String world, String node, String defaultValue, boolean includeGlobal) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (server == null || server.equals("")) {
server = "global";
}
if (node == null) {
throw new NullPointerException("node");
}
if (node.equals("")) {
throw new IllegalArgumentException("node is empty");
}
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
if (!n.getValue()) {
continue;
}
if (!n.isMeta()) {
continue;
}
if (!server.equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(server, includeGlobal, false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
return unescapeCharacters(meta.getValue());
}
}
return defaultValue;
}
private static void setChatMeta(boolean prefix, PermissionHolder holder, String value, int priority, String server, String world) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (value == null || value.equals("")) {
throw new IllegalArgumentException("value is null/empty");
}
Node.Builder node = LuckPerms.getApi().buildNode(prefix ? "prefix" : "suffix" + "." + priority + "." + escapeCharacters(value));
node.setValue(true);
if (!server.equalsIgnoreCase("global")) {
node.setServer(server);
}
if (world != null && !world.equals("")) {
node.setServer(server).setWorld(world);
}
try {
holder.setPermission(node.build());
} catch (ObjectAlreadyHasException ignored) {}
}
/**
* Adds a prefix to a holder on a specific server and world
* @param holder the holder to set the prefix for
* @param prefix the prefix value
* @param priority the priority to set the prefix at
* @param server the server to set the prefix on, can be null
* @param world the world to set the prefix on, can be null
* @throws NullPointerException if the holder is null
* @throws IllegalArgumentException if the prefix is null or empty
*/
public static void setPrefix(PermissionHolder holder, String prefix, int priority, String server, String world) {
setChatMeta(true, holder, prefix, priority, server, world);
}
/**
* Adds a suffix to a holder on a specific server and world
* @param holder the holder to set the suffix for
* @param suffix the suffix value
* @param priority the priority to set the suffix at
* @param server the server to set the suffix on, can be null
* @param world the world to set the suffix on, can be null
* @throws NullPointerException if the holder is null
* @throws IllegalArgumentException if the suffix is null or empty
*/
public static void setSuffix(PermissionHolder holder, String suffix, int priority, String server, String world) {
setChatMeta(false, holder, suffix, priority, server, world);
}
private static String getChatMeta(boolean prefix, PermissionHolder holder, String server, String world, boolean includeGlobal) {
if (holder == null) {
throw new NullPointerException("holder");
}
if (server == null) {
server = "global";
}
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes()) {
if (!n.getValue()) {
continue;
}
if (!server.equalsIgnoreCase("global")) {
if (!n.shouldApplyOnServer(server, includeGlobal, false)) {
continue;
}
}
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) {
continue;
}
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
}
}
return meta == null ? "" : unescapeCharacters(meta);
}
/**
* Returns a holders highest priority prefix, if they have one
* @param holder the holder
* @param server the server to retrieve the prefix on
* @param world the world to retrieve the prefix on
* @param includeGlobal if global nodes should be considered when retrieving the prefix
* @return a prefix string, if the holder has one, or an empty string if not.
* @throws NullPointerException if the holder is null
*/
public static String getPrefix(PermissionHolder holder, String server, String world, boolean includeGlobal) {
return getChatMeta(true, holder, server, world, includeGlobal);
}
/**
* Returns a holders highest priority suffix, if they have one
* @param holder the holder
* @param server the server to retrieve the suffix on
* @param world the world to retrieve the suffix on
* @param includeGlobal if global nodes should be considered when retrieving the suffix
* @return a suffix string, if the holder has one, or an empty string if not.
* @throws NullPointerException if the holder is null
*/
public static String getSuffix(PermissionHolder holder, String server, String world, boolean includeGlobal) {
return getChatMeta(false, holder, server, world, includeGlobal);
}
}
@@ -30,8 +30,9 @@ import java.util.Optional;
/**
* Represents an immutable node object
* <p> Use {@link LuckPermsApi#buildNode(String)} to get an instance.
* @since 1.6
* @since 2.6
*/
@SuppressWarnings("unused")
public interface Node extends Map.Entry<String, Boolean> {
/**
@@ -104,7 +105,15 @@ public interface Node extends Map.Entry<String, Boolean> {
boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex);
/**
* If this node should apply given the specific context
* If this node should apply in the given context
* @param context the context key value pairs
* @param worldAndServer if world and server contexts should be checked
* @return true if the node should apply
*/
boolean shouldApplyWithContext(Map<String, String> context, boolean worldAndServer);
/**
* If this node should apply in the given context
* @param context the context key value pairs
* @return true if the node should apply
*/
@@ -246,19 +255,38 @@ public interface Node extends Map.Entry<String, Boolean> {
Map.Entry<Integer, String> getSuffix();
/**
* Similar to {@link #equals(Object)}, except doesn't take note of the value
* @param node the other node
* @return true if the two nodes are almost equal
* Checks if this Node is equal to another node
* @param obj the other node
* @return true if this node is equal to the other provided
* @see #equalsIgnoringValue(Node) for a less strict implementation of this method
*/
boolean equalsIgnoringValue(Node node);
boolean equals(Object obj);
/**
* Similar to {@link #equals(Object)}, except doesn't take note of the expiry time or value
* @param node the other node
* 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
*/
boolean almostEquals(Node node);
boolean equalsIgnoringValue(Node other);
/**
* 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
*/
boolean almostEquals(Node other);
/**
* 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
*/
boolean equalsIgnoringValueOrTemp(Node other);
/**
* Builds a Node instance
*/
interface Builder {
Builder setNegated(boolean negated);
Builder setValue(boolean value);
@@ -31,7 +31,7 @@ import java.util.Set;
import java.util.SortedSet;
/**
* Wrapper interface for internal PermissionHolder (user/group) instances
* Interface for internal PermissionHolder (user/group) instances
*/
@SuppressWarnings("unused")
public interface PermissionHolder {
@@ -45,21 +45,21 @@ public interface PermissionHolder {
/**
* Gets an immutable Set of the objects permission nodes
* @return an immutable set of permissions in priority order
* @since 1.6
* @since 2.6
*/
SortedSet<Node> getPermissions();
SortedSet<? extends Node> getPermissions();
/**
* Similar to {@link #getPermissions()}, except excluding transient permissions
* @return a set of nodes
* @since 1.6
* @since 2.6
*/
Set<Node> getEnduringPermissions();
/**
* Similar to {@link #getPermissions()}, except excluding non-transient permissions
* @return a set of nodes
* @since 1.6
* @since 2.6
*/
Set<Node> getTransientPermissions();
@@ -67,10 +67,35 @@ public interface PermissionHolder {
/**
* Gets an immutable set of the nodes that this object has and inherits
* @return an immutable set of permissions
* @since 1.6
* @since 2.6
* @deprecated in favour of {@link #getAllNodes(Contexts)}
*/
@Deprecated
Set<Node> getAllNodes();
/**
* Gets a mutable sorted set of the nodes that this object has and inherits, filtered by context
* Unlike {@link #getAllNodesFiltered(Contexts)}, this method will not filter individual nodes. The context is only
* used to determine which groups should apply.
* Nodes are sorted into priority order.
* @param contexts the context for the lookup,
* @return a mutable sorted set of permissions
* @throws NullPointerException if the context is null
* @since 2.11
*/
SortedSet<LocalizedNode> getAllNodes(Contexts contexts);
/**
* Gets a mutable set of the nodes that is objects has and inherits, filtered by context.
* Unlike {@link #getAllNodes(Contexts)}, this method WILL filter individual nodes, and only return ones that fully
* meet the context provided.
* @param contexts the context for the lookup
* @return a mutable set of permissions
* @throws NullPointerException if the context is null
* @since 2.11
*/
Set<LocalizedNode> getAllNodesFiltered(Contexts contexts);
/**
* Gets an immutable Map of the objects permission nodes
* @return an immutable map of permissions
@@ -84,7 +109,7 @@ public interface PermissionHolder {
* @param node the node to check for
* @return a Tristate for the holders permission status for the node
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
Tristate hasPermission(Node node);
@@ -93,7 +118,7 @@ public interface PermissionHolder {
* @param node the node to check for
* @return a Tristate for the holders permission status for the node
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
Tristate hasTransientPermission(Node node);
@@ -171,7 +196,7 @@ public interface PermissionHolder {
* @param node the node to check for
* @return a Tristate for the holders inheritance status for the node
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
Tristate inheritsPermission(Node node);
@@ -249,7 +274,7 @@ public interface PermissionHolder {
* @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
void setPermission(Node node) throws ObjectAlreadyHasException;
@@ -267,7 +292,7 @@ public interface PermissionHolder {
* @param node The node to be set
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
void setTransientPermission(Node node) throws ObjectAlreadyHasException;
@@ -357,7 +382,7 @@ public interface PermissionHolder {
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
void unsetPermission(Node node) throws ObjectLacksException;
@@ -366,7 +391,7 @@ public interface PermissionHolder {
* @param node The node to be unset
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @since 1.6
* @since 2.6
*/
void unsetTransientPermission(Node node) throws ObjectLacksException;
@@ -498,8 +523,10 @@ public interface PermissionHolder {
* @param possibleNodes a list of possible permissions for resolving wildcards
* @param applyGroups if inherited group permissions should be included
* @return a map of permissions
* @since 1.6
* @since 2.6
* @deprecated in favour of {@link #getAllNodesFiltered(Contexts)}
*/
@Deprecated
Map<String, Boolean> getPermissions(String server, String world, Map<String, String> extraContext, boolean includeGlobal, List<String> possibleNodes, boolean applyGroups);
/**
@@ -513,7 +540,7 @@ public interface PermissionHolder {
/**
* Processes the nodes and returns the temporary ones.
* @return a set of temporary nodes
* @since 1.6
* @since 2.6
*/
Set<Node> getTemporaryPermissionNodes();
@@ -528,7 +555,7 @@ public interface PermissionHolder {
/**
* Processes the nodes and returns the non-temporary ones.
* @return a set of permanent nodes
* @since 1.6
* @since 2.6
*/
Set<Node> getPermanentPermissionNodes();
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api;
/**
* Represents the platform type that LuckPerms is running on
* @since 2.7
*/
public enum PlatformType {
BUKKIT("Bukkit"),
BUNGEE("Bungee"),
SPONGE("Sponge"),
/**
* @since 2.9
*/
STANDALONE("Standalone");
private final String friendlyName;
PlatformType(String friendlyName) {
this.friendlyName = friendlyName;
}
public String getFriendlyName() {
return friendlyName;
}
}
@@ -28,7 +28,7 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
/**
* Wrapper interface for internal Track instances
* Interface for internal Track instances
*/
@SuppressWarnings("unused")
public interface Track {
@@ -29,7 +29,7 @@ import java.util.List;
import java.util.UUID;
/**
* Wrapper interface for internal User instances
* Interface for internal User instances
*/
@SuppressWarnings("unused")
public interface User extends PermissionHolder {
@@ -25,7 +25,9 @@ package me.lucko.luckperms.api;
import java.util.UUID;
/**
* This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode servers
* A UUID cache for online users, between external Mojang UUIDs, and internal LuckPerms UUIDs.
*
* <p> This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode servers
* or mixed offline mode and online mode servers. Platforms running in offline mode generate a random UUID for a user when
* they first join the server, but this UUID will then not be consistent across the network. LuckPerms will instead check
* the datastore cache, to get a UUID for a user that is consistent across an entire network.
@@ -35,6 +37,7 @@ import java.util.UUID;
*
* <p> WARNING: THIS IS ONLY EFFECTIVE FOR ONLINE PLAYERS. USE THE DATASTORE METHODS FOR OFFLINE PLAYERS.
*/
@SuppressWarnings("unused")
public interface UuidCache {
/**
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A simple implementation of the listener aspects of {@link IContextCalculator<T>}
* @param <T> the subject type
*/
public abstract class ContextCalculator<T> implements IContextCalculator<T> {
private final List<ContextListener<T>> listeners = new CopyOnWriteArrayList<>();
/**
* Pushes an update to all registered {@link ContextListener}s.
* Make sure any changes are applied internally before this method is called.
* @param subject the subject that changed
* @param before the context state before the change
* @param current the context state after the change (now)
* @throws NullPointerException if any parameters are null
*/
protected void pushUpdate(T subject, Map.Entry<String, String> before, Map.Entry<String, String> current) {
if (subject == null) {
throw new NullPointerException("subject");
}
if (before == null) {
throw new NullPointerException("before");
}
if (current == null) {
throw new NullPointerException("current");
}
for (ContextListener<T> listener : listeners) {
try {
listener.onContextChange(subject, before, current);
} catch (Exception e) {
System.out.println("Exception whilst passing context change to listener: " + listener);
e.printStackTrace();
}
}
}
@Override
public void addListener(ContextListener<T> listener) {
if (listener == null) {
throw new NullPointerException("listener");
}
listeners.add(listener);
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import java.util.Map;
/**
* Listens to context changes
* @param <T> the subject type, Is ALWAYS the player class of the platform.
*/
public interface ContextListener<T> {
/**
* Called whenever a context changes on the
* @param subject the subject that had context changed
* @param before the context state before the change
* @param current the context state after the change (now)
*/
void onContextChange(T subject, Map.Entry<String, String> before, Map.Entry<String, String> current) throws Exception;
}
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.context;
import java.util.Map;
/**
* Calculates whether contexts are applicable to {@link T}
*
* <p>Somewhat inspired by the system used on Sponge.
* @param <T> the subject type. Is ALWAYS the player class of the platform.
*/
public interface IContextCalculator<T> {
/**
* Gives the subject all of the applicable contexts they meet
* @param subject the subject to add contexts tp
* @param accumulator a map of contexts to add to
* @return the map
*/
Map<String, String> giveApplicableContext(T subject, Map<String, String> accumulator);
/**
* Checks to see if a context is applicable to a subject
* @param subject the subject to check against
* @param context the context to check for
* @return true if met, or false if not. If this calculator does not calculate the given context, return false.
*/
boolean isContextApplicable(T subject, Map.Entry<String, String> context);
/**
* Adds a listener to be called whenever a context handled by this calculator changes
* @param listener the listener instance
* @throws NullPointerException if listener is null
*/
void addListener(ContextListener<T> listener);
}
@@ -26,7 +26,7 @@ import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.PermissionHolder;
/**
* @since 1.6
* @since 2.6
*/
public class AbstractPermissionEvent extends TargetedEvent<PermissionHolder> {
@@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a temporary permission node expires
* @since 1.6
* @since 2.6
*/
public class PermissionNodeExpireEvent extends AbstractPermissionEvent {
public PermissionNodeExpireEvent(PermissionHolder target, Node node) {
@@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a permission node is set on a holder
* @since 1.6
* @since 2.6
*/
public class PermissionNodeSetEvent extends AbstractPermissionEvent {
public PermissionNodeSetEvent(PermissionHolder target, Node node) {
@@ -28,7 +28,7 @@ import me.lucko.luckperms.api.event.AbstractPermissionEvent;
/**
* Called when a permission node is unset from a holder
* @since 1.6
* @since 2.6
*/
public class PermissionNodeUnsetEvent extends AbstractPermissionEvent {
public PermissionNodeUnsetEvent(PermissionHolder target, Node node) {
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.exceptions;
/**
* Thrown when a permission holding object doesn't / already has a permission or isn't / is already is a member of a group
* @since 2.7
*/
public abstract class MembershipException extends Exception {
}
@@ -26,5 +26,5 @@ package me.lucko.luckperms.exceptions;
* Thrown when a permission holding object already has a permission, is already a member of a group, or when a track
* already contains a group.
*/
public class ObjectAlreadyHasException extends Exception {
public class ObjectAlreadyHasException extends MembershipException {
}
@@ -26,5 +26,5 @@ package me.lucko.luckperms.exceptions;
* Thrown when a permission holding object does not already have a permission, is not already a member of a group,
* or when a track doesn't contain a group.
*/
public class ObjectLacksException extends Exception {
public class ObjectLacksException extends MembershipException {
}
+119
View File
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.11-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms-bukkit-legacy</artifactId>
<build>
<defaultGoal>clean package</defaultGoal>
<finalName>LuckPerms-bukkit-legacy-${release.version}.${git.closest.tag.commit.count}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>false</minimizeJar>
<artifactSet>
<excludes>
<exclude>org.xerial:*</exclude>
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>me.lucko.luckperms.lib.slf4j</shadedPattern>
</relocation>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>me.lucko.luckperms.lib.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>com.mongodb</pattern>
<shadedPattern>me.lucko.luckperms.lib.mongodb</shadedPattern>
</relocation>
<relocation>
<pattern>org.bson</pattern>
<shadedPattern>me.lucko.luckperms.lib.bson</shadedPattern>
</relocation>
<relocation>
<pattern>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<injectAllReactorProjects>true</injectAllReactorProjects>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- LuckPerms Bukkit -->
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-bukkit</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</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>
+3 -3
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.11-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -30,8 +30,8 @@
<dependency>
<groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<version>2.8</version>
<scope>provided</scope>
</dependency>
<!-- BukkitAPI -->
<dependency>
@@ -25,15 +25,11 @@ package me.lucko.luckperms.api.placeholders;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.util.TimeUtil;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.Track;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
/*
* PlaceholderAPI Expansion for LuckPerms, implemented using the LuckPerms API.
@@ -51,7 +47,6 @@ import java.util.regex.Pattern;
* - suffix
* - meta_<node>
*/
@SuppressWarnings("deprecation")
public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
private static final String IDENTIFIER = "luckperms";
private static final String PLUGIN_NAME = "LuckPerms";
@@ -76,7 +71,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
@Override
public String getVersion() {
return api == null ? "null" : api.getVersion();
return "2.8";
}
@Override
@@ -85,7 +80,7 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
return "";
}
Optional<User> u = api.getUserSafe(player.getUniqueId());
Optional<User> u = api.getUserSafe(api.getUuidCache().getUUID(player.getUniqueId()));
if (!u.isPresent()) {
return "";
}
@@ -128,17 +123,11 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (identifier.startsWith("expiry_time_") && identifier.length() > "expiry_time_".length()) {
String node = identifier.substring("expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
Map<Map.Entry<String, Boolean>, Long> temps = user.getTemporaryNodes();
for (Map.Entry<Map.Entry<String, Boolean>, Long> e : temps.entrySet()) {
if (!e.getKey().getKey().equalsIgnoreCase(node)) {
continue;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.getPermission().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
return TimeUtil.getTime((int) (e.getValue() - currentTime));
}
return "";
@@ -146,42 +135,27 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
if (identifier.startsWith("group_expiry_time_") && identifier.length() > "group_expiry_time_".length()) {
String node = "group." + identifier.substring("group_expiry_time_".length());
long currentTime = System.currentTimeMillis() / 1000L;
Map<Map.Entry<String, Boolean>, Long> temps = user.getTemporaryNodes();
for (Map.Entry<Map.Entry<String, Boolean>, Long> e : temps.entrySet()) {
if (!e.getKey().getKey().equalsIgnoreCase(node)) {
continue;
for (Node n : user.getTemporaryPermissionNodes()) {
if (n.getPermission().equalsIgnoreCase(node)) {
return TimeUtil.getTime((int) (n.getExpiryUnixTime() - currentTime));
}
return TimeUtil.getTime((int) (e.getValue() - currentTime));
}
return "";
}
if (identifier.equalsIgnoreCase("prefix")) {
return getChatMeta(PREFIX_PATTERN, user);
return MetaUtils.getPrefix(user, null, null, true);
}
if (identifier.equalsIgnoreCase("suffix")) {
return getChatMeta(SUFFIX_PATTERN, user);
return MetaUtils.getSuffix(user, null, null, true);
}
if (identifier.startsWith("meta_") && identifier.length() > "meta_".length()) {
String node = "meta." + escapeCharacters(identifier.substring("meta_".length())) + ".";
Map<String, Boolean> nodes = user.getNodes();
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
if (!e.getValue()) continue;
if (!e.getKey().toLowerCase().startsWith(node)) continue;
String meta = e.getKey().substring(node.length());
return unescapeCharacters(meta);
}
return "";
String node = identifier.substring("meta_".length());
return MetaUtils.getMeta(user, null, null, node, "", true);
}
return null;
@@ -205,44 +179,4 @@ public class LuckPermsPlaceholderExpansion extends PlaceholderExpansion {
private static String formatBoolean(boolean b) {
return b ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse();
}
private static String escapeCharacters(String s) {
s = s.replace(".", "{SEP}");
s = s.replace("/", "{FSEP}");
s = s.replace("$", "{DSEP}");
return s;
}
private static String unescapeCharacters(String s) {
s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$");
return s;
}
private static final Pattern PREFIX_PATTERN = Pattern.compile("(?i)prefix\\.\\d+\\..*");
private static final Pattern SUFFIX_PATTERN = Pattern.compile("(?i)suffix\\.\\d+\\..*");
private static final Pattern DOT_PATTERN = Pattern.compile("\\.");
private static String getChatMeta(Pattern pattern, User user) {
int priority = 0;
String meta = null;
for (Map.Entry<String, Boolean> e : user.getLocalPermissions(null, null).entrySet()) {
if (!e.getValue()) continue;
if (pattern.matcher(e.getKey()).matches()) {
String[] parts = DOT_PATTERN.split(e.getKey(), 3);
int p = Integer.parseInt(parts[1]);
if (meta == null || p > priority) {
meta = parts[2];
priority = p;
}
}
}
return meta == null ? "" : unescapeCharacters(meta);
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.11-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -22,8 +22,12 @@
package me.lucko.luckperms;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -39,13 +43,18 @@ class BukkitCommand extends CommandManager implements CommandExecutor, TabExecut
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
onCommand(BukkitSenderFactory.get().wrap(sender), label, Arrays.asList(args), Callback.empty());
onCommand(
BukkitSenderFactory.get(getPlugin()).wrap(sender),
label,
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args))),
Callback.empty()
);
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String label, String[] args) {
return onTabComplete(BukkitSenderFactory.get().wrap(sender), Arrays.asList(args));
return onTabComplete(BukkitSenderFactory.get(getPlugin()).wrap(sender), Arrays.asList(args));
}
}
@@ -22,14 +22,17 @@
package me.lucko.luckperms;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.config.AbstractConfiguration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
class BukkitConfig extends LPConfiguration<LPBukkitPlugin> {
class BukkitConfig extends AbstractConfiguration<LPBukkitPlugin> {
private YamlConfiguration configuration;
BukkitConfig(LPBukkitPlugin plugin) {
@@ -55,11 +58,6 @@ class BukkitConfig extends LPConfiguration<LPBukkitPlugin> {
}
}
@Override
protected void set(String path, Object value) {
configuration.set(path, value);
}
@Override
protected String getString(String path, String def) {
return configuration.getString(path, def);
@@ -74,4 +72,20 @@ class BukkitConfig extends LPConfiguration<LPBukkitPlugin> {
protected boolean getBoolean(String path, boolean def) {
return configuration.getBoolean(path, def);
}
@SuppressWarnings("unchecked")
@Override
protected Map<String, String> getMap(String path, Map<String, String> def) {
Map<String, String> map = new HashMap<>();
ConfigurationSection section = configuration.getConfigurationSection(path);
if (section == null) {
return def;
}
for (String key : section.getKeys(false)) {
map.put(key, section.getString(key));
}
return map;
}
}
@@ -22,17 +22,22 @@
package me.lucko.luckperms;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.inject.Injector;
import me.lucko.luckperms.inject.LPPermissible;
import me.lucko.luckperms.users.BukkitUser;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.AbstractListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.*;
import org.bukkit.permissions.PermissionAttachment;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
class BukkitListener extends AbstractListener implements Listener {
private final LPBukkitPlugin plugin;
@@ -42,18 +47,52 @@ class BukkitListener extends AbstractListener implements Listener {
this.plugin = plugin;
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerPreLogin(AsyncPlayerPreLoginEvent e) {
if (!plugin.getDatastore().isAcceptingLogins()) {
// Datastore is disabled, prevent players from joining the server
// The datastore is disabled, prevent players from joining the server
e.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Message.LOADING_ERROR.toString());
return;
}
// Process login
onAsyncLogin(e.getUniqueId(), e.getName());
// Pre-process the user's permissions, so they're ready for PLE.
BukkitUser user = (BukkitUser) plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getUniqueId()));
Map<String, Boolean> toApply = user.exportNodes(
new Contexts(
Collections.singletonMap("server", plugin.getConfiguration().getServer()),
plugin.getConfiguration().isIncludingGlobalPerms(),
plugin.getConfiguration().isIncludingGlobalWorldPerms(),
true,
plugin.getConfiguration().isApplyingGlobalGroups(),
plugin.getConfiguration().isApplyingGlobalWorldGroups()
),
Collections.emptyList(),
true
);
user.setLoginPreProcess(toApply);
// Hook with Vault early
if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) {
plugin.getVaultHook().getPermissionHook().getVaultUserManager().setupUser(user);
}
}
@EventHandler
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerPreLoginMonitor(AsyncPlayerPreLoginEvent e) {
if (plugin.getDatastore().isAcceptingLogins() && e.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
// Login event was cancelled by another plugin
final UUID internal = plugin.getUuidCache().getUUID(e.getUniqueId());
onLeave(e.getUniqueId());
if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) {
plugin.getVaultHook().getPermissionHook().getVaultUserManager().clearUser(internal);
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent e) {
final Player player = e.getPlayer();
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
@@ -63,39 +102,74 @@ class BukkitListener extends AbstractListener implements Listener {
return;
}
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
BukkitUser u = (BukkitUser) user;
try {
// Make a new permissible for the user
LPPermissible lpPermissible = new LPPermissible(player, plugin, plugin.getDefaultsProvider());
PermissionAttachment attachment = player.addAttachment(plugin);
try {
BukkitUser.getPermissionsField().set(attachment, new ConcurrentHashMap<>());
} catch (Throwable t) {
t.printStackTrace();
}
// Insert the pre-processed permissions into the permissible
lpPermissible.getLuckPermsPermissions().putAll(u.getLoginPreProcess());
u.setLoginPreProcess(null);
u.setAttachment(attachment);
// Inject into the player
Injector.inject(player, lpPermissible);
u.setLpPermissible(lpPermissible);
} catch (Throwable t) {
t.printStackTrace();
}
plugin.doAsync(user::refreshPermissions);
}
@EventHandler
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerLoginMonitor(PlayerLoginEvent e) {
if (e.getResult() != PlayerLoginEvent.Result.ALLOWED) {
// The player got denied on sync login.
final UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
onLeave(e.getPlayer().getUniqueId());
if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) {
plugin.getVaultHook().getPermissionHook().getVaultUserManager().clearUser(internal);
}
} else {
User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
// Call another update to calculate full context. (incl. per world permissions)
plugin.doAsync(user::refreshPermissions);
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent e) {
// Refresh permissions again
plugin.getUserManager().getWorldCache().put(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(e.getPlayer().getUniqueId()));
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
plugin.getWorldCalculator().getWorldCache().put(internal, e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(internal));
}
@EventHandler
public void onPlayerChangedWorld(PlayerChangedWorldEvent e) {
plugin.getUserManager().getWorldCache().put(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName());
plugin.doAsync(() -> refreshPlayer(e.getPlayer().getUniqueId()));
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerQuit(PlayerQuitEvent e) {
plugin.getUserManager().getWorldCache().remove(e.getPlayer().getUniqueId());
final UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
plugin.getWorldCalculator().getWorldCache().remove(internal);
onLeave(e.getPlayer().getUniqueId());
if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) {
plugin.getVaultHook().getPermissionHook().getVaultUserManager().clearUser(internal);
}
}
@EventHandler
public void onPlayerCommand(PlayerCommandPreprocessEvent e) {
if (plugin.getConfiguration().isOpsEnabled()) {
return;
}
String s = e.getMessage()
.replace("/", "")
.replace("bukkit:", "")
.replace("spigot:", "")
.replace("minecraft:", "");
if (s.startsWith("op") || s.startsWith("deop")) {
e.setCancelled(true);
e.getPlayer().sendMessage(Message.OP_DISABLED.toString());
}
}
}
@@ -22,8 +22,6 @@
package me.lucko.luckperms;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import me.lucko.luckperms.commands.SenderFactory;
import me.lucko.luckperms.constants.Constants;
import org.bukkit.command.CommandSender;
@@ -31,12 +29,16 @@ import org.bukkit.entity.Player;
import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BukkitSenderFactory extends SenderFactory<CommandSender> {
private static BukkitSenderFactory instance = null;
public static BukkitSenderFactory get() {
if (instance == null){
instance = new BukkitSenderFactory();
private BukkitSenderFactory(LuckPermsPlugin plugin) {
super(plugin);
}
public static synchronized BukkitSenderFactory get(LuckPermsPlugin plugin) {
if (instance == null) {
instance = new BukkitSenderFactory(plugin);
}
return instance;
}
@@ -25,12 +25,17 @@ package me.lucko.luckperms;
import lombok.Getter;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.api.vault.VaultHook;
import me.lucko.luckperms.calculators.DefaultsProvider;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.config.LPConfiguration;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.contexts.ContextManager;
import me.lucko.luckperms.contexts.ServerCalculator;
import me.lucko.luckperms.contexts.WorldCalculator;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.data.Importer;
import me.lucko.luckperms.groups.GroupManager;
@@ -40,9 +45,12 @@ import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.storage.StorageFactory;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.BukkitUserManager;
import me.lucko.luckperms.utils.LocaleManager;
import me.lucko.luckperms.utils.LogFactory;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin;
@@ -54,6 +62,8 @@ import java.util.stream.Collectors;
@Getter
public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private VaultHook vaultHook = null;
private final Set<UUID> ignoringLogs = ConcurrentHashMap.newKeySet();
private LPConfiguration configuration;
private BukkitUserManager userManager;
@@ -65,6 +75,10 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private Logger log;
private Importer importer;
private ConsecutiveExecutor consecutiveExecutor;
private DefaultsProvider defaultsProvider;
private LocaleManager localeManager;
private ContextManager<Player> contextManager;
private WorldCalculator worldCalculator;
@Override
public void onEnable() {
@@ -73,6 +87,20 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
getLog().info("Loading configuration...");
configuration = new BukkitConfig(this);
localeManager = new LocaleManager();
File locale = new File(getDataFolder(), "lang.yml");
if (locale.exists()) {
getLog().info("Found locale file. Attempting to load from it.");
try {
localeManager.loadFromFile(locale);
} catch (Exception e) {
e.printStackTrace();
}
}
defaultsProvider = new DefaultsProvider();
getServer().getScheduler().runTaskLater(this, () -> defaultsProvider.refresh(), 1L);
// register events
PluginManager pm = getServer().getPluginManager();
pm.registerEvents(new BukkitListener(this), this);
@@ -88,20 +116,27 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
datastore = StorageFactory.getDatastore(this, "h2");
getLog().info("Loading internal permission managers...");
uuidCache = new UuidCache(getConfiguration().getOnlineMode());
uuidCache = new UuidCache(getConfiguration().isOnlineMode());
userManager = new BukkitUserManager(this);
groupManager = new GroupManager(this);
trackManager = new TrackManager();
importer = new Importer(commandManager);
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
contextManager = new ContextManager<>();
worldCalculator = new WorldCalculator(this);
pm.registerEvents(worldCalculator, this);
contextManager.registerCalculator(worldCalculator);
contextManager.registerCalculator(new ServerCalculator<>(getConfiguration().getServer()));
contextManager.registerListener(userManager);
int mins = getConfiguration().getSyncTime();
if (mins > 0) {
long ticks = mins * 60 * 20;
getServer().getScheduler().runTaskTimerAsynchronously(this, new UpdateTask(this), ticks, ticks);
}
getServer().getScheduler().runTaskTimer(this, BukkitSenderFactory.get(), 1L, 1L);
getServer().getScheduler().runTaskTimer(this, BukkitSenderFactory.get(this), 1L, 1L);
getServer().getScheduler().runTaskTimerAsynchronously(this, new ExpireTemporaryTask(this), 60L, 60L);
getServer().getScheduler().runTaskTimerAsynchronously(this, consecutiveExecutor, 20L, 20L);
@@ -109,7 +144,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
getLog().info("Attempting to hook into Vault...");
try {
if (getServer().getPluginManager().isPluginEnabled("Vault")) {
VaultHook.hook(this);
vaultHook = new VaultHook();
vaultHook.hook(this);
getLog().info("Registered Vault permission & chat hook.");
} else {
getLog().info("Vault not found.");
@@ -132,6 +168,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
e.printStackTrace();
}
registerPermissions(getConfiguration().isCommandsAllowOp() ? PermissionDefault.OP : PermissionDefault.FALSE);
if (!getConfiguration().isOpsEnabled()) {
getServer().getOperators().forEach(o -> o.setOp(false));
}
getLog().info("Successfully loaded.");
}
@@ -143,6 +184,10 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
getLog().info("Unregistering API...");
LuckPerms.unregisterProvider();
getServer().getServicesManager().unregisterAll(this);
if (vaultHook != null) {
vaultHook.unhook(this);
}
}
@Override
@@ -161,8 +206,8 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public Type getType() {
return Type.BUKKIT;
public PlatformType getType() {
return PlatformType.BUKKIT;
}
@Override
@@ -186,13 +231,16 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
}
@Override
public List<Sender> getSenders() {
return getServer().getOnlinePlayers().stream().map(p -> BukkitSenderFactory.get().wrap(p)).collect(Collectors.toList());
public List<Sender> getNotifyListeners() {
return getServer().getOnlinePlayers().stream()
.map(p -> BukkitSenderFactory.get(this).wrap(p, Collections.singleton(me.lucko.luckperms.constants.Permission.LOG_NOTIFY)))
.filter(me.lucko.luckperms.constants.Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
public Sender getConsoleSender() {
return BukkitSenderFactory.get().wrap(getServer().getConsoleSender());
return BukkitSenderFactory.get(this).wrap(getServer().getConsoleSender());
}
@Override
@@ -217,6 +265,16 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
return getServer().getServicesManager().load(clazz);
}
@SuppressWarnings("deprecation")
@Override
public UUID getUUID(String playerName) {
try {
return getServer().getOfflinePlayer(playerName).getUniqueId();
} catch (Exception e) {
return null;
}
}
@Override
public boolean isPluginLoaded(String name) {
return getServer().getPluginManager().isPluginEnabled(name);
@@ -226,4 +284,34 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
public void runUpdateTask() {
getServer().getScheduler().runTaskAsynchronously(this, new UpdateTask(this));
}
private void registerPermissions(PermissionDefault def) {
PluginManager pm = getServer().getPluginManager();
Map<String, List<String>> wildcards = new HashMap<>();
List<String> all = new ArrayList<>();
for (me.lucko.luckperms.constants.Permission p : me.lucko.luckperms.constants.Permission.values()) {
pm.addPermission(new Permission(p.getNode(), def));
if (p.getGroup() != null) {
if (!wildcards.containsKey(p.getGroup())) {
wildcards.put(p.getGroup(), new ArrayList<>());
}
wildcards.get(p.getGroup()).add(p.getTag());
}
all.add(p.getNode());
}
for (Map.Entry<String, List<String>> e : wildcards.entrySet()) {
pm.addPermission(new Permission(
"luckperms." + e.getKey() + ".*", def,
e.getValue().stream()
.map(tag -> "luckperms." + e.getKey() + "." + tag)
.collect(Collectors.toMap(s -> s, s -> true))
)
);
}
pm.addPermission(new Permission("luckperms.*", def, all.stream().collect(Collectors.toMap(s -> s, s -> true))));
}
}
@@ -23,16 +23,21 @@
package me.lucko.luckperms.api.vault;
import lombok.NonNull;
import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.vault.cache.ChatCache;
import me.lucko.luckperms.api.vault.cache.VaultUser;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
import net.milkbowl.vault.chat.Chat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static me.lucko.luckperms.utils.ArgumentChecker.escapeCharacters;
import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters;
@@ -52,13 +57,8 @@ import static me.lucko.luckperms.utils.ArgumentChecker.unescapeCharacters;
*
* Node that special characters used within LuckPerms are escaped:
* See {@link me.lucko.luckperms.utils.ArgumentChecker#escapeCharacters(String)}
*
* Registered on normal priority so other plugins can override.
*/
public class VaultChatHook extends Chat {
@Setter
private LPBukkitPlugin plugin;
private final VaultPermissionHook perms;
VaultChatHook(VaultPermissionHook perms) {
@@ -77,116 +77,103 @@ public class VaultChatHook extends Chat {
private void saveMeta(PermissionHolder holder, String world, String node, String value) {
if (holder == null) return;
if (node.equals("")) return;
node = escapeCharacters(node);
value = escapeCharacters(value);
if (world == null || world.equals("")) {
perms.scheduleTask(() -> {
String k = escapeCharacters(node);
String v = escapeCharacters(value);
List<Node> toRemove = holder.getNodes().stream()
.filter(n -> n.isMeta() && n.getMeta().getKey().equals(k))
.collect(Collectors.toList());
toRemove.forEach(n -> {
try {
holder.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
Node.Builder metaNode = new me.lucko.luckperms.core.Node.Builder("meta." + k + "." + v).setValue(true);
if (!perms.getServer().equalsIgnoreCase("global")) {
metaNode.setServer(perms.getServer());
}
if (world != null && !world.equals("")) {
metaNode.setServer(perms.getServer()).setWorld(world);
}
try {
holder.setPermission("meta." + node + "." + value, true);
holder.setPermission(metaNode.build());
} catch (ObjectAlreadyHasException ignored) {}
} else {
perms.save(holder);
});
}
private void setChatMeta(boolean prefix, PermissionHolder holder, String value, String world) {
if (holder == null) return;
if (value.equals("")) return;
perms.scheduleTask(() -> {
Node.Builder node = new me.lucko.luckperms.core.Node.Builder(prefix ? "prefix" : "suffix" + ".1000." + escapeCharacters(value));
node.setValue(true);
if (!perms.getServer().equalsIgnoreCase("global")) {
node.setServer(perms.getServer());
}
if (world != null && !world.equals("")) {
node.setServer(perms.getServer()).setWorld(world);
}
try {
holder.setPermission("meta." + node + "." + value, true, "global", world);
holder.setPermission(node.build());
} catch (ObjectAlreadyHasException ignored) {}
}
perms.objectSave(holder);
perms.save(holder);
});
}
private static int getMeta(PermissionHolder holder, String world, String node, int defaultValue) {
if (holder == null) return defaultValue;
private String getUserMeta(User user, String world, String node, String defaultValue) {
if (user == null) return defaultValue;
node = escapeCharacters(node);
if (!perms.getVaultUserManager().containsUser(user.getUuid())) {
return defaultValue;
}
VaultUser vaultUser = perms.getVaultUserManager().getUser(user.getUuid());
Map<String, String> context = new HashMap<>();
context.put("server", perms.getServer());
if (world != null) {
context.put("world", world);
}
ChatCache cd = vaultUser.processChatData(context);
return unescapeCharacters(cd.getMeta().getOrDefault(node, defaultValue));
}
private String getUserChatMeta(boolean prefix, User user, String world) {
if (user == null) return "";
if (!perms.getVaultUserManager().containsUser(user.getUuid())) {
return "";
}
VaultUser vaultUser = perms.getVaultUserManager().getUser(user.getUuid());
Map<String, String> context = new HashMap<>();
context.put("server", perms.getServer());
if (world != null) {
context.put("world", world);
}
ChatCache cd = vaultUser.processChatData(context);
return unescapeCharacters(prefix ? (cd.getPrefix() == null ? "" : cd.getPrefix()) : (cd.getSuffix() == null ? "" : cd.getSuffix()));
}
private String getGroupMeta(Group group, String world, String node, String defaultValue) {
if (group == null) return defaultValue;
if (node.equals("")) return defaultValue;
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
if (!n.isMeta()) {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
try {
return Integer.parseInt(unescapeCharacters(meta.getValue()));
} catch (Throwable t) {
return defaultValue;
}
}
}
return defaultValue;
}
private static double getMeta(PermissionHolder holder, String world, String node, double defaultValue) {
if (holder == null) return defaultValue;
if (node.equals("")) return defaultValue;
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
if (!n.isMeta()) {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
try {
return Double.parseDouble(unescapeCharacters(meta.getValue()));
} catch (Throwable t) {
return defaultValue;
}
}
}
return defaultValue;
}
private static boolean getMeta(PermissionHolder holder, String world, String node, boolean defaultValue) {
if (holder == null) return defaultValue;
if (node.equals("")) return defaultValue;
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
if (!n.isMeta()) {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
try {
return Boolean.parseBoolean(unescapeCharacters(meta.getValue()));
} catch (Throwable t) {
return defaultValue;
}
}
}
return defaultValue;
}
private static String getMeta(PermissionHolder holder, String world, String node, String defaultValue) {
if (holder == null) return defaultValue;
if (node.equals("")) return defaultValue;
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
for (Node n : group.getPermissions(true)) {
if (!n.getValue()) {
continue;
}
@@ -195,60 +182,56 @@ public class VaultChatHook extends Chat {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
if (meta.getKey().equalsIgnoreCase(node)) {
try {
return unescapeCharacters(meta.getValue());
} catch (Throwable t) {
return defaultValue;
}
return unescapeCharacters(meta.getValue());
}
}
return defaultValue;
}
private static String getChatMeta(boolean prefix, PermissionHolder holder, String world) {
if (holder == null) return "";
private String getGroupChatMeta(boolean prefix, Group group, String world) {
if (group == null) return "";
int priority = -1000;
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes(null)) {
Map<String, String> context = new HashMap<>();
context.put("server", perms.getServer());
if (world != null) {
context.put("world", world);
}
for (Node n : group.getAllNodes(null, new Contexts(context, perms.isIncludeGlobal(), true, true, true, true))) {
if (!n.getValue()) {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
if (prefix ? !n.isPrefix() : !n.isSuffix()) {
continue;
}
if (prefix) {
if (!n.isPrefix()) {
continue;
}
if (!n.shouldApplyOnServer(perms.getServer(), perms.isIncludeGlobal(), false)) {
continue;
}
Map.Entry<Integer, String> prefixValue = n.getPrefix();
if (prefixValue.getKey() > priority) {
meta = prefixValue.getValue();
priority = prefixValue.getKey();
}
} else {
if (!n.isSuffix()) {
continue;
}
if (!n.shouldApplyOnWorld(world, perms.isIncludeGlobal(), false)) {
continue;
}
Map.Entry<Integer, String> suffixValue = n.getSuffix();
if (suffixValue.getKey() > priority) {
meta = suffixValue.getValue();
priority = suffixValue.getKey();
}
Map.Entry<Integer, String> value = prefix ? n.getPrefix() : n.getSuffix();
if (value.getKey() > priority) {
meta = value.getValue();
priority = value.getKey();
}
}
@@ -256,154 +239,146 @@ public class VaultChatHook extends Chat {
}
public String getPlayerPrefix(String world, @NonNull String player) {
final User user = plugin.getUserManager().get(player);
return getChatMeta(true, user, world);
final User user = perms.getPlugin().getUserManager().get(player);
return getUserChatMeta(true, user, world);
}
public void setPlayerPrefix(String world, @NonNull String player, @NonNull String prefix) {
final User user = plugin.getUserManager().get(player);
if (user == null) return;
if (prefix.equals("")) return;
try {
user.setPermission("prefix.1000." + escapeCharacters(prefix), true);
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(user);
final User user = perms.getPlugin().getUserManager().get(player);
setChatMeta(true, user, prefix, world);
}
public String getPlayerSuffix(String world, @NonNull String player) {
final User user = plugin.getUserManager().get(player);
return getChatMeta(false, user, world);
final User user = perms.getPlugin().getUserManager().get(player);
return getUserChatMeta(false, user, world);
}
public void setPlayerSuffix(String world, @NonNull String player, @NonNull String suffix) {
final User user = plugin.getUserManager().get(player);
if (user == null) return;
if (suffix.equals("")) return;
try {
user.setPermission("suffix.1000." + escapeCharacters(suffix), true);
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(user);
final User user = perms.getPlugin().getUserManager().get(player);
setChatMeta(false, user, suffix, world);
}
public String getGroupPrefix(String world, @NonNull String group) {
final Group g = plugin.getGroupManager().get(group);
return getChatMeta(false, g, world);
final Group g = perms.getPlugin().getGroupManager().get(group);
return getGroupChatMeta(false, g, world);
}
public void setGroupPrefix(String world, @NonNull String group, @NonNull String prefix) {
final Group g = plugin.getGroupManager().get(group);
if (g == null) return;
if (prefix.equals("")) return;
try {
g.setPermission("prefix.1000." + escapeCharacters(prefix), true);
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(g);
final Group g = perms.getPlugin().getGroupManager().get(group);
setChatMeta(true, g, prefix, world);
}
public String getGroupSuffix(String world, @NonNull String group) {
final Group g = plugin.getGroupManager().get(group);
return getChatMeta(false, g, world);
final Group g = perms.getPlugin().getGroupManager().get(group);
return getGroupChatMeta(false, g, world);
}
public void setGroupSuffix(String world, @NonNull String group, @NonNull String suffix) {
final Group g = plugin.getGroupManager().get(group);
if (g == null) return;
if (suffix.equals("")) return;
try {
g.setPermission("suffix.1000." + escapeCharacters(suffix), true);
} catch (ObjectAlreadyHasException ignored) {}
perms.objectSave(g);
final Group g = perms.getPlugin().getGroupManager().get(group);
setChatMeta(false, g, suffix, world);
}
public int getPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int defaultValue) {
final User user = plugin.getUserManager().get(player);
return getMeta(user, world, node, defaultValue);
final User user = perms.getPlugin().getUserManager().get(player);
try {
return Integer.parseInt(getUserMeta(user, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setPlayerInfoInteger(String world, @NonNull String player, @NonNull String node, int value) {
final User user = plugin.getUserManager().get(player);
final User user = perms.getPlugin().getUserManager().get(player);
saveMeta(user, world, node, String.valueOf(value));
}
public int getGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int defaultValue) {
final Group g = plugin.getGroupManager().get(group);
return getMeta(g, world, node, defaultValue);
final Group g = perms.getPlugin().getGroupManager().get(group);
try {
return Integer.parseInt(getGroupMeta(g, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setGroupInfoInteger(String world, @NonNull String group, @NonNull String node, int value) {
final Group g = plugin.getGroupManager().get(group);
final Group g = perms.getPlugin().getGroupManager().get(group);
saveMeta(g, world, node, String.valueOf(value));
}
public double getPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double defaultValue) {
final User user = plugin.getUserManager().get(player);
return getMeta(user, world, node, defaultValue);
final User user = perms.getPlugin().getUserManager().get(player);
try {
return Double.parseDouble(getUserMeta(user, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setPlayerInfoDouble(String world, @NonNull String player, @NonNull String node, double value) {
final User user = plugin.getUserManager().get(player);
final User user = perms.getPlugin().getUserManager().get(player);
saveMeta(user, world, node, String.valueOf(value));
}
public double getGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double defaultValue) {
final Group g = plugin.getGroupManager().get(group);
return getMeta(g, world, node, defaultValue);
final Group g = perms.getPlugin().getGroupManager().get(group);
try {
return Double.parseDouble(getGroupMeta(g, world, node, String.valueOf(defaultValue)));
} catch (NumberFormatException e) {
return defaultValue;
}
}
public void setGroupInfoDouble(String world, @NonNull String group, @NonNull String node, double value) {
final Group g = plugin.getGroupManager().get(group);
final Group g = perms.getPlugin().getGroupManager().get(group);
saveMeta(g, world, node, String.valueOf(value));
}
public boolean getPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean defaultValue) {
final User user = plugin.getUserManager().get(player);
return getMeta(user, world, node, defaultValue);
final User user = perms.getPlugin().getUserManager().get(player);
String s = getUserMeta(user, world, node, String.valueOf(defaultValue));
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
return defaultValue;
}
return Boolean.parseBoolean(s);
}
public void setPlayerInfoBoolean(String world, @NonNull String player, @NonNull String node, boolean value) {
final User user = plugin.getUserManager().get(player);
final User user = perms.getPlugin().getUserManager().get(player);
saveMeta(user, world, node, String.valueOf(value));
}
public boolean getGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean defaultValue) {
final Group g = plugin.getGroupManager().get(group);
return getMeta(g, world, node, defaultValue);
final Group g = perms.getPlugin().getGroupManager().get(group);
String s = getGroupMeta(g, world, node, String.valueOf(defaultValue));
if (!s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) {
return defaultValue;
}
return Boolean.parseBoolean(s);
}
public void setGroupInfoBoolean(String world, @NonNull String group, @NonNull String node, boolean value) {
final Group g = plugin.getGroupManager().get(group);
final Group g = perms.getPlugin().getGroupManager().get(group);
saveMeta(g, world, node, String.valueOf(value));
}
public String getPlayerInfoString(String world, @NonNull String player, @NonNull String node, String defaultValue) {
final User user = plugin.getUserManager().get(player);
return getMeta(user, world, node, defaultValue);
final User user = perms.getPlugin().getUserManager().get(player);
return getUserMeta(user, world, node, defaultValue);
}
public void setPlayerInfoString(String world, @NonNull String player, @NonNull String node, String value) {
final User user = plugin.getUserManager().get(player);
final User user = perms.getPlugin().getUserManager().get(player);
saveMeta(user, world, node, value);
}
public String getGroupInfoString(String world, @NonNull String group, @NonNull String node, String defaultValue) {
final Group g = plugin.getGroupManager().get(group);
return getMeta(g, world, node, defaultValue);
final Group g = perms.getPlugin().getGroupManager().get(group);
return getGroupMeta(g, world, node, defaultValue);
}
public void setGroupInfoString(String world, @NonNull String group, @NonNull String node, String value) {
final Group g = plugin.getGroupManager().get(group);
final Group g = perms.getPlugin().getGroupManager().get(group);
saveMeta(g, world, node, value);
}
@@ -22,38 +22,53 @@
package me.lucko.luckperms.api.vault;
import lombok.Getter;
import me.lucko.luckperms.LPBukkitPlugin;
import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.ServicesManager;
@Getter
public class VaultHook {
private VaultChatHook chatHook = null;
private VaultPermissionHook permissionHook = null;
private static VaultChatHook chatHook = null;
private static VaultPermissionHook permissionHook = null;
public static void hook(LPBukkitPlugin plugin) {
public void hook(LPBukkitPlugin plugin) {
try {
if (permissionHook == null) {
permissionHook = new VaultPermissionHook();
}
permissionHook.setPlugin(plugin);
permissionHook.setServer(plugin.getConfiguration().getVaultServer());
permissionHook.setIncludeGlobal(plugin.getConfiguration().isVaultIncludingGlobal());
permissionHook.setup();
if (chatHook == null) {
chatHook = new VaultChatHook(permissionHook);
}
chatHook.setPlugin(plugin);
final ServicesManager sm = plugin.getServer().getServicesManager();
sm.unregisterAll(plugin);
sm.register(Permission.class, permissionHook, plugin, ServicePriority.High);
sm.register(Chat.class, chatHook, plugin, ServicePriority.Low);
sm.register(Chat.class, chatHook, plugin, ServicePriority.High);
} catch (Exception e) {
e.printStackTrace();
}
}
public void unhook(LPBukkitPlugin plugin) {
final ServicesManager sm = plugin.getServer().getServicesManager();
if (permissionHook != null) {
sm.unregister(Permission.class, permissionHook);
}
if (chatHook != null) {
sm.unregister(Chat.class, chatHook);
}
}
public boolean isHooked() {
return permissionHook != null && chatHook != null;
}
}
@@ -22,10 +22,14 @@
package me.lucko.luckperms.api.vault;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.vault.cache.VaultUser;
import me.lucko.luckperms.api.vault.cache.VaultUserManager;
import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -33,11 +37,58 @@ import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.users.User;
import net.milkbowl.vault.permission.Permission;
public class VaultPermissionHook extends Permission {
import java.util.*;
/**
* The LuckPerms Vault Permission implementation
* Most lookups are cached.
*/
public class VaultPermissionHook extends Permission implements Runnable {
private final List<Runnable> tasks = new ArrayList<>();
@Getter
@Setter
private LPBukkitPlugin plugin;
@Getter
private VaultUserManager vaultUserManager;
@Getter
@Setter
private String server = "global";
@Getter
@Setter
private boolean includeGlobal = true;
public void setup() {
vaultUserManager = new VaultUserManager(plugin, this);
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this, 1L, 1L);
}
public void log(String s) {
if (plugin.getConfiguration().isDebugPermissionChecks()) {
plugin.getLog().info("[VAULT] " + s);
}
}
void scheduleTask(Runnable r) {
synchronized (tasks) {
tasks.add(r);
}
}
@Override
public void run() {
List<Runnable> toRun = new ArrayList<>();
synchronized (tasks) {
toRun.addAll(tasks);
tasks.clear();
}
toRun.forEach(Runnable::run);
}
@Override
public String getName() {
return "LuckPerms";
@@ -53,154 +104,199 @@ public class VaultPermissionHook extends Permission {
return true;
}
private boolean objectHas(String world, PermissionHolder object, String permission) {
if (object == null) return false;
private boolean objectHas(String world, Group group, String permission) {
if (group == null) return false;
Map<String, String> context = new HashMap<>();
if (world != null && !world.equals("")) {
return object.hasPermission(permission, true, "global", world);
} else {
return object.hasPermission(permission, true);
context.put("world", world);
}
context.put("server", server);
Map<String, Boolean> toApply = group.exportNodes(
new Contexts(context, includeGlobal, includeGlobal, true, true, true),
Collections.emptyList(), true
);
return toApply.containsKey(permission) && toApply.get(permission);
}
private boolean objectAdd(String world, PermissionHolder object, String permission) {
private boolean add(String world, PermissionHolder object, String permission) {
if (object == null) return false;
try {
if (world != null && !world.equals("")) {
object.setPermission(permission, true, "global", world);
object.setPermission(permission, true, server, world);
} else {
object.setPermission(permission, true);
object.setPermission(permission, true, server);
}
} catch (ObjectAlreadyHasException ignored) {}
objectSave(object);
save(object);
return true;
}
private boolean objectRemove(String world, PermissionHolder object, String permission) {
private boolean remove(String world, PermissionHolder object, String permission) {
if (object == null) return false;
try {
if (world != null && !world.equals("")) {
object.unsetPermission(permission, "global", world);
object.unsetPermission(permission, server, world);
} else {
object.unsetPermission(permission);
object.unsetPermission(permission, server);
}
} catch (ObjectLacksException ignored) {}
objectSave(object);
save(object);
return true;
}
void objectSave(PermissionHolder t) {
void save(PermissionHolder t) {
if (t instanceof User) {
((User) t).refreshPermissions();
plugin.getDatastore().saveUser(((User) t), Callback.empty());
plugin.getDatastore().saveUser(((User) t));
}
if (t instanceof Group) {
plugin.getDatastore().saveGroup(((Group) t), c -> plugin.runUpdateTask());
plugin.getDatastore().saveGroup(((Group) t));
plugin.runUpdateTask();
}
}
@Override
public boolean playerHas(String world, @NonNull String player, @NonNull String permission) {
return objectHas(world, plugin.getUserManager().get(player), permission);
log("Checking if player " + player + " has permission: " + permission + " on world " + world + ", server " + server);
User user = plugin.getUserManager().get(player);
if (user == null) return false;
if (!vaultUserManager.containsUser(user.getUuid())) {
return false;
}
VaultUser vaultUser = vaultUserManager.getUser(user.getUuid());
Map<String, String> context = new HashMap<>();
context.put("server", server);
if (world != null) {
context.put("world", world);
}
return vaultUser.hasPermission(context, permission);
}
@Override
public boolean playerAdd(String world, @NonNull String player, @NonNull String permission) {
log("Adding permission to player " + player + ": '" + permission + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player);
return objectAdd(world, user, permission);
scheduleTask(() -> add(world, user, permission));
return true;
}
@Override
public boolean playerRemove(String world, @NonNull String player, @NonNull String permission) {
log("Removing permission from player " + player + ": '" + permission + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player);
return objectRemove(world, user, permission);
scheduleTask(() -> remove(world, user, permission));
return true;
}
@Override
public boolean groupHas(String world, @NonNull String groupName, @NonNull String permission) {
log("Checking if group " + groupName + " has permission: " + permission + " on world " + world + ", server " + server);
final Group group = plugin.getGroupManager().get(groupName);
return objectHas(world, group, permission);
}
@Override
public boolean groupAdd(String world, @NonNull String groupName, @NonNull String permission) {
log("Adding permission to group " + groupName + ": '" + permission + "' on world " + world + ", server " + server);
final Group group = plugin.getGroupManager().get(groupName);
return objectAdd(world, group, permission);
scheduleTask(() -> add(world, group, permission));
return true;
}
@Override
public boolean groupRemove(String world, @NonNull String groupName, @NonNull String permission) {
log("Removing permission from group " + groupName + ": '" + permission + "' on world " + world + ", server " + server);
final Group group = plugin.getGroupManager().get(groupName);
return objectRemove(world, group, permission);
scheduleTask(() -> remove(world, group, permission));
return true;
}
@Override
public boolean playerInGroup(String world, @NonNull String player, @NonNull String group) {
log("Checking if player " + player + " is in group: " + group + " on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player);
if (user == null) return false;
final Group group1 = plugin.getGroupManager().get(group);
if (group1 == null) return false;
if (world != null && !world.equals("")) {
return user.isInGroup(group1, "global", world);
} else {
return user.isInGroup(group1);
}
return user.getNodes().stream()
.filter(Node::isGroupNode)
.filter(n -> n.shouldApplyOnServer(server, isIncludeGlobal(), false))
.filter(n -> n.shouldApplyOnWorld(world, true, false))
.map(Node::getGroupName)
.filter(s -> s.equalsIgnoreCase(group))
.findAny().isPresent();
}
@Override
public boolean playerAddGroup(String world, @NonNull String player, @NonNull String groupName) {
log("Adding player " + player + " to group: '" + groupName + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player);
if (user == null) return false;
final Group group = plugin.getGroupManager().get(groupName);
if (group == null) return false;
try {
if (world != null && !world.equals("")) {
user.addGroup(group, "global", world);
} else {
user.addGroup(group);
}
} catch (ObjectAlreadyHasException ignored) {}
objectSave(user);
scheduleTask(() -> {
try {
if (world != null && !world.equals("")) {
user.addGroup(group, server, world);
} else {
user.addGroup(group, server);
}
} catch (ObjectAlreadyHasException ignored) {}
save(user);
});
return true;
}
@Override
public boolean playerRemoveGroup(String world, @NonNull String player, @NonNull String groupName) {
log("Removing player " + player + " from group: '" + groupName + "' on world " + world + ", server " + server);
final User user = plugin.getUserManager().get(player);
if (user == null) return false;
final Group group = plugin.getGroupManager().get(groupName);
if (group == null) return false;
try {
if (world != null && !world.equals("")) {
user.removeGroup(group, "global", world);
} else {
user.removeGroup(group);
}
} catch (ObjectLacksException ignored) {}
objectSave(user);
scheduleTask(() -> {
try {
if (world != null && !world.equals("")) {
user.removeGroup(group, server, world);
} else {
user.removeGroup(group, server);
}
} catch (ObjectLacksException ignored) {}
save(user);
});
return true;
}
@Override
public String[] getPlayerGroups(String world, @NonNull String player) {
final User user = plugin.getUserManager().get(player);
return (user == null) ? new String[0] :
world != null && !world.equals("") ? user.getLocalGroups("global", world).toArray(new String[0]) :
user.getGroupNames().toArray(new String[0]);
log("Getting groups of player: " + player + ", on world " + world + ", server " + server);
User user = plugin.getUserManager().get(player);
if (user == null) return new String[0];
return user.getNodes().stream()
.filter(Node::isGroupNode)
.filter(n -> n.shouldApplyOnServer(server, isIncludeGlobal(), false))
.filter(n -> n.shouldApplyOnWorld(world, true, false))
.map(Node::getGroupName)
.toArray(String[]::new);
}
@Override
public String getPrimaryGroup(String world, @NonNull String player) {
log("Getting primary group of player: " + player);
final User user = plugin.getUserManager().get(player);
return (user == null) ? null : user.getPrimaryGroup();
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.vault.cache;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Getter
@RequiredArgsConstructor
public class ChatCache {
private final Map<String, String> context;
@Setter
private String prefix = null;
@Setter
private String suffix = null;
private Map<String, String> meta = new ConcurrentHashMap<>();
public void invalidateCache() {
prefix = null;
suffix = null;
meta.clear();
}
}
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.vault.cache;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.calculators.*;
import me.lucko.luckperms.users.BukkitUser;
import me.lucko.luckperms.users.User;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ContextCache {
@Getter
private final Map<String, String> context;
@Getter
private final Map<String, Boolean> permissionCache = new ConcurrentHashMap<>();
private final PermissionCalculator calculator;
public ContextCache(User user, Map<String, String> context, LuckPermsPlugin plugin, DefaultsProvider defaultsProvider) {
this.context = context;
List<PermissionProcessor> processors = new ArrayList<>(5);
processors.add(new MapProcessor(permissionCache));
if (plugin.getConfiguration().isApplyingWildcards()) {
processors.add(new WildcardProcessor(permissionCache));
}
if (plugin.getConfiguration().isApplyingRegex()) {
processors.add(new RegexProcessor(permissionCache));
}
processors.add(new DefaultsProcessor(((BukkitUser) user)::isOp, defaultsProvider));
calculator = new PermissionCalculator(plugin, user.getName(), plugin.getConfiguration().isDebugPermissionChecks(), processors);
}
public void invalidateCache() {
calculator.invalidateCache();
}
public Tristate getPermissionValue(@NonNull String permission) {
return calculator.getPermissionValue(permission);
}
}
@@ -0,0 +1,181 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.vault.cache;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.vault.VaultPermissionHook;
import me.lucko.luckperms.users.User;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor
public class VaultUser {
private final LPBukkitPlugin plugin;
private final VaultPermissionHook vault;
@Getter
private final User user;
@Getter
private final Map<Map<String, String>, ContextCache> contextData = new ConcurrentHashMap<>();
@Getter
private final Map<Map<String, String>, ChatCache> chatData = new ConcurrentHashMap<>();
public boolean hasPermission(Map<String, String> context, String permission) {
ContextCache cd = contextData.computeIfAbsent(context, map -> calculatePermissions(map, false));
return cd.getPermissionValue(permission).asBoolean();
}
public ChatCache processChatData(Map<String, String> context) {
return chatData.computeIfAbsent(context, map -> calculateChat(map, false));
}
public ContextCache calculatePermissions(Map<String, String> context, boolean apply) {
Map<String, Boolean> toApply = user.exportNodes(
new Contexts(context, vault.isIncludeGlobal(), true, true, true, true),
Collections.emptyList(),
true
);
ContextCache existing = contextData.get(context);
if (existing == null) {
existing = new ContextCache(user, context, plugin, plugin.getDefaultsProvider());
if (apply) {
contextData.put(context, existing);
}
}
boolean different = false;
if (toApply.size() != existing.getPermissionCache().size()) {
different = true;
} else {
for (Map.Entry<String, Boolean> e : existing.getPermissionCache().entrySet()) {
if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) {
continue;
}
different = true;
break;
}
}
if (!different) return existing;
existing.getPermissionCache().clear();
existing.invalidateCache();
existing.getPermissionCache().putAll(toApply);
return existing;
}
public ChatCache calculateChat(Map<String, String> context, boolean apply) {
ChatCache existing = chatData.get(context);
if (existing == null) {
existing = new ChatCache(context);
if (apply) {
chatData.put(context, existing);
}
}
Map<String, String> contexts = new HashMap<>(context);
String server = contexts.get("server");
String world = contexts.get("world");
contexts.remove("server");
contexts.remove("world");
existing.invalidateCache();
// Load meta
for (Node n : user.getPermissions(true)) {
if (!n.getValue()) {
continue;
}
if (!n.isMeta()) {
continue;
}
if (!n.shouldApplyOnServer(server, vault.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
continue;
}
if (!n.shouldApplyWithContext(contexts, false)) {
continue;
}
Map.Entry<String, String> meta = n.getMeta();
existing.getMeta().put(meta.getKey(), meta.getValue());
}
// Load prefixes & suffixes
int prefixPriority = Integer.MIN_VALUE;
int suffixPriority = Integer.MIN_VALUE;
for (Node n : user.getAllNodes(null, new Contexts(context, vault.isIncludeGlobal(), true, true, true, true))) {
if (!n.getValue()) {
continue;
}
if (!n.isPrefix() && !n.isSuffix()) {
continue;
}
if (!n.shouldApplyOnServer(server, vault.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, true, false)) {
continue;
}
if (!n.shouldApplyWithContext(contexts, false)) {
continue;
}
if (n.isPrefix()) {
Map.Entry<Integer, String> value = n.getPrefix();
if (value.getKey() > prefixPriority) {
existing.setPrefix(value.getValue());
}
} else {
Map.Entry<Integer, String> value = n.getSuffix();
if (value.getKey() > suffixPriority) {
existing.setSuffix(value.getValue());
}
}
}
return existing;
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.vault.cache;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.vault.VaultPermissionHook;
import me.lucko.luckperms.users.User;
import org.bukkit.World;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor
public class VaultUserManager {
private final LPBukkitPlugin plugin;
private final VaultPermissionHook vault;
private final Map<UUID, VaultUser> userCache = new ConcurrentHashMap<>();
public void setupUser(User user) {
VaultUser vaultUser = userCache.computeIfAbsent(user.getUuid(), uuid -> new VaultUser(plugin, vault, user));
vaultUser.calculatePermissions(Collections.singletonMap("server", vault.getServer()), true);
vaultUser.calculateChat(Collections.singletonMap("server", vault.getServer()), true);
for (World world : plugin.getServer().getWorlds()) {
Map<String, String> context = new HashMap<>();
context.put("server", vault.getServer());
context.put("world", world.getName());
vaultUser.calculatePermissions(context, true);
vaultUser.calculateChat(context, true);
}
}
public void clearUser(UUID uuid) {
userCache.remove(uuid);
}
public boolean containsUser(UUID uuid) {
return userCache.containsKey(uuid);
}
public VaultUser getUser(UUID uuid) {
return userCache.get(uuid);
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.calculators;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.api.Tristate;
import org.bukkit.permissions.PermissionAttachmentInfo;
import java.util.Map;
@AllArgsConstructor
public class AttachmentProcessor implements PermissionProcessor {
@Getter
private final Map<String, PermissionAttachmentInfo> map;
@Override
public Tristate hasPermission(String permission) {
if (map.containsKey(permission)) {
return Tristate.fromBoolean(map.get(permission).getValue());
}
return Tristate.UNDEFINED;
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.calculators;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import java.util.function.Supplier;
@AllArgsConstructor
public class DefaultsProcessor implements PermissionProcessor {
private final Supplier<Boolean> isOp;
private final DefaultsProvider defaultsProvider;
@Override
public Tristate hasPermission(String permission) {
Tristate t = defaultsProvider.hasDefault(permission, isOp.get());
if (t != Tristate.UNDEFINED) {
return t;
}
Permission defPerm = Bukkit.getServer().getPluginManager().getPermission(permission);
if (defPerm != null) {
return Tristate.fromBoolean(defPerm.getDefault().getValue(isOp.get()));
} else {
return Tristate.UNDEFINED;
}
}
}
@@ -0,0 +1,177 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.calculators;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.api.Tristate;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class DefaultsProvider {
private final DummyPermissible opDummy = new DummyPermissible(this::refreshOp);
private final DummyPermissible nonOpDummy = new DummyPermissible(this::refreshNonOp);
private final Map<String, Boolean> op = new HashMap<>();
private final Map<String, Boolean> nonOp = new HashMap<>();
public void refresh() {
refreshOp();
refreshNonOp();
}
private void refreshOp() {
calculateDefaults(op, opDummy, true);
}
private void refreshNonOp() {
calculateDefaults(nonOp, nonOpDummy, false);
}
private static void calculateDefaults(Map<String, Boolean> map, DummyPermissible p, boolean op) {
Set<String> perms = map.keySet();
for (String name : perms) {
Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, p);
}
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(false, p);
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(true, p);
map.clear();
Set<Permission> defaults = Bukkit.getServer().getPluginManager().getDefaultPermissions(op);
Bukkit.getServer().getPluginManager().subscribeToDefaultPerms(op, p);
for (Permission perm : defaults) {
String name = perm.getName().toLowerCase();
map.put(name, true);
Bukkit.getServer().getPluginManager().subscribeToPermission(name, p);
calculateChildPermissions(map, p, perm.getChildren(), false);
}
}
private static void calculateChildPermissions(Map<String, Boolean> map, DummyPermissible p, Map<String, Boolean> children, boolean invert) {
for (Map.Entry<String, Boolean> e : children.entrySet()) {
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
boolean value = e.getValue() ^ invert;
String lName = e.getKey().toLowerCase();
map.put(lName, value);
Bukkit.getServer().getPluginManager().subscribeToPermission(e.getKey(), p);
if (perm != null) {
calculateChildPermissions(map, p, perm.getChildren(), !value);
}
}
}
public Tristate hasDefault(String permission, boolean isOp) {
Map<String, Boolean> map = isOp ? op : nonOp;
if (!map.containsKey(permission)) {
return Tristate.UNDEFINED;
}
return Tristate.fromBoolean(map.get(permission));
}
@AllArgsConstructor
private static class DummyPermissible implements Permissible {
private final Runnable onRefresh;
@Override
public void recalculatePermissions() {
onRefresh.run();
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return Collections.emptySet();
}
@Override
public boolean isPermissionSet(String name) {
return false;
}
@Override
public boolean isPermissionSet(Permission perm) {
return false;
}
@Override
public boolean hasPermission(String name) {
return false;
}
@Override
public boolean hasPermission(Permission perm) {
return false;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
return null;
}
@Override
public void removeAttachment(PermissionAttachment attachment) {
}
@Override
public boolean isOp() {
return false;
}
@Override
public void setOp(boolean value) {
}
}
}
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.contexts;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.context.ContextCalculator;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@RequiredArgsConstructor
public class WorldCalculator extends ContextCalculator<Player> implements Listener {
private static final String WORLD_KEY = "world";
private final LuckPermsPlugin plugin;
@Getter
private final Map<UUID, String> worldCache = new ConcurrentHashMap<>();
@Override
public Map<String, String> giveApplicableContext(Player subject, Map<String, String> accumulator) {
String world = getWorld(subject);
if (world != null) {
accumulator.put(WORLD_KEY, world);
}
return accumulator;
}
@Override
public boolean isContextApplicable(Player subject, Map.Entry<String, String> context) {
if (!context.getKey().equals(WORLD_KEY)) {
return false;
}
String world = getWorld(subject);
return world != null && world.equals(context.getValue());
}
private String getWorld(Player player) {
UUID internal = plugin.getUuidCache().getUUID(player.getUniqueId());
if (!worldCache.containsKey(internal)) {
return null;
}
String world = worldCache.get(internal);
return plugin.getConfiguration().getWorldRewrites().getOrDefault(world, world);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerChangedWorld(PlayerChangedWorldEvent e) {
UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
worldCache.put(internal, e.getPlayer().getWorld().getName());
pushUpdate(
e.getPlayer(),
Maps.immutableEntry(WORLD_KEY, e.getFrom().getName()),
Maps.immutableEntry(WORLD_KEY, e.getPlayer().getWorld().getName())
);
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.inject;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import java.util.Collections;
import java.util.Set;
public class DummyPermissibleBase extends PermissibleBase {
public DummyPermissibleBase() {
super(null);
}
@Override
public boolean isOp() {
return false;
}
@Override
public void setOp(boolean value) {
}
@Override
public boolean isPermissionSet(String name) {
return false;
}
@Override
public boolean isPermissionSet(Permission perm) {
return false;
}
@Override
public boolean hasPermission(String inName) {
return false;
}
@Override
public boolean hasPermission(Permission perm) {
return false;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin) {
return null;
}
@Override
public void removeAttachment(PermissionAttachment attachment) {
}
@Override
public void recalculatePermissions() {
}
@Override
public synchronized void clearPermissions() {
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
return null;
}
@Override
public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
return null;
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return Collections.emptySet();
}
}
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.inject;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.PermissibleBase;
import java.lang.reflect.Field;
/**
* Injects a {@link LPPermissible} into a {@link Player}
*/
@UtilityClass
public class Injector {
private static Field HUMAN_ENTITY_FIELD;
static {
try {
HUMAN_ENTITY_FIELD = Class.forName(getInternalClassName("entity.CraftHumanEntity")).getDeclaredField("perm");
HUMAN_ENTITY_FIELD.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean inject(CommandSender sender, PermissibleBase permissible) {
try {
Field f = getPermField(sender);
f.set(sender, permissible);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean unInject(CommandSender sender) {
try {
Permissible permissible = getPermissible(sender);
if (permissible instanceof LPPermissible) {
/* The player is most likely leaving. Bukkit will attempt to call #clearPermissions, so we cannot set to null.
However, there's no need to re-inject a real PermissibleBase, so we just inject a dummy instead.
This saves tick time, pointlessly recalculating defaults when the instance will never be used. */
getPermField(sender).set(sender, new DummyPermissibleBase());
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private static Permissible getPermissible(CommandSender sender) {
try {
Field f = getPermField(sender);
return (Permissible) f.get(sender);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Field getPermField(CommandSender sender) {
if (sender instanceof Player) {
return HUMAN_ENTITY_FIELD;
}
throw new RuntimeException("Couldn't get perm field for sender " + sender.getClass().getName());
}
private static String getInternalClassName(String className) {
Class server = Bukkit.getServer().getClass();
if (!server.getSimpleName().equals("CraftServer")) {
throw new RuntimeException("Couldn't inject into server " + server);
}
String version;
if (server.getName().equals("org.bukkit.craftbukkit.CraftServer")) {
// Non versioned class
version = ".";
} else {
version = server.getName().substring("org.bukkit.craftbukkit".length());
version = version.substring(0, version.length() - "CraftServer".length());
}
return "org.bukkit.craftbukkit" + version + className;
}
}
@@ -0,0 +1,260 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.inject;
import lombok.Getter;
import lombok.NonNull;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.calculators.*;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.*;
import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.stream.Collectors;
/**
* Modified PermissibleBase for LuckPerms
*/
public class LPPermissible extends PermissibleBase {
@Getter
private final CommandSender parent;
private final PermissionCalculator calculator;
@Getter
private final Map<String, Boolean> luckPermsPermissions = new ConcurrentHashMap<>();
private final List<PermissionAttachment> attachments = new LinkedList<>();
private final Map<String, PermissionAttachmentInfo> attachmentPermissions = new HashMap<>();
public LPPermissible(@NonNull CommandSender sender, LuckPermsPlugin plugin, DefaultsProvider defaultsProvider) {
super(sender);
this.parent = sender;
List<PermissionProcessor> processors = new ArrayList<>(5);
processors.add(new MapProcessor(luckPermsPermissions));
processors.add(new AttachmentProcessor(attachmentPermissions));
if (plugin.getConfiguration().isApplyingWildcards()) {
processors.add(new WildcardProcessor(luckPermsPermissions));
}
if (plugin.getConfiguration().isApplyingRegex()) {
processors.add(new RegexProcessor(luckPermsPermissions));
}
processors.add(new DefaultsProcessor(parent::isOp, defaultsProvider));
calculator = new PermissionCalculator(plugin, parent.getName(), plugin.getConfiguration().isDebugPermissionChecks(), processors);
recalculatePermissions();
}
public void invalidateCache() {
calculator.invalidateCache();
}
@Override
public boolean isOp() {
return parent.isOp();
}
@Override
public void setOp(boolean value) {
parent.setOp(value);
}
@Override
public boolean isPermissionSet(@NonNull String name) {
return calculator.getPermissionValue(name) != Tristate.UNDEFINED;
}
@Override
public boolean isPermissionSet(@NonNull Permission perm) {
return isPermissionSet(perm.getName());
}
@Override
public boolean hasPermission(@NonNull String name) {
Tristate ts = calculator.getPermissionValue(name);
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
return Permission.DEFAULT_PERMISSION.getValue(isOp());
}
@Override
public boolean hasPermission(@NonNull Permission perm) {
Tristate ts = calculator.getPermissionValue(perm.getName());
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
return perm.getDefault().getValue(isOp());
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin);
result.setPermission(name, value);
recalculatePermissions();
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = new PermissionAttachment(plugin, parent);
attachments.add(result);
recalculatePermissions();
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, @NonNull String name, boolean value, int ticks) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin, ticks);
if (result != null) {
result.setPermission(name, value);
}
return result;
}
@Override
public PermissionAttachment addAttachment(@NonNull Plugin plugin, int ticks) {
if (!plugin.isEnabled()) {
throw new IllegalArgumentException("Plugin " + plugin.getDescription().getFullName() + " is not enabled");
}
PermissionAttachment result = addAttachment(plugin);
if (Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new RemoveAttachmentRunnable(result), ticks) == -1) {
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add PermissionAttachment to " + parent + " for plugin " + plugin.getDescription().getFullName() + ": Scheduler returned -1");
result.remove();
return null;
} else {
return result;
}
}
@Override
public void removeAttachment(@NonNull PermissionAttachment attachment) {
if (attachments.contains(attachment)) {
attachments.remove(attachment);
PermissionRemovedExecutor ex = attachment.getRemovalCallback();
if (ex != null) {
ex.attachmentRemoved(attachment);
}
recalculatePermissions();
} else {
throw new IllegalArgumentException("Given attachment is not part of Permissible object " + parent);
}
}
@Override
public void recalculatePermissions() {
if (attachmentPermissions == null) {
return;
}
attachmentPermissions.clear();
for (PermissionAttachment attachment : attachments) {
calculateChildPermissions(attachment.getPermissions(), false, attachment);
}
invalidateCache();
}
@Override
public synchronized void clearPermissions() {
Set<String> perms = attachmentPermissions.keySet();
for (String name : perms) {
Bukkit.getServer().getPluginManager().unsubscribeFromPermission(name, parent);
}
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(false, parent);
Bukkit.getServer().getPluginManager().unsubscribeFromDefaultPerms(true, parent);
attachmentPermissions.clear();
}
private void calculateChildPermissions(Map<String, Boolean> children, boolean invert, PermissionAttachment attachment) {
for (Map.Entry<String, Boolean> e : children.entrySet()) {
Permission perm = Bukkit.getServer().getPluginManager().getPermission(e.getKey());
boolean value = e.getValue() ^ invert;
String name = e.getKey().toLowerCase();
attachmentPermissions.put(name, new PermissionAttachmentInfo(parent, name, attachment, value));
Bukkit.getServer().getPluginManager().subscribeToPermission(name, parent);
if (perm != null) {
calculateChildPermissions(perm.getChildren(), !value, attachment);
}
}
}
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
Set<PermissionAttachmentInfo> perms = new HashSet<>();
perms.addAll(attachmentPermissions.values());
perms.addAll(luckPermsPermissions.entrySet().stream()
.map(e -> new PermissionAttachmentInfo(parent, e.getKey(), null, e.getValue()))
.collect(Collectors.toList()));
return perms;
}
private class RemoveAttachmentRunnable implements Runnable {
private PermissionAttachment attachment;
private RemoveAttachmentRunnable(PermissionAttachment attachment) {
this.attachment = attachment;
}
public void run() {
attachment.remove();
}
}
}
@@ -25,34 +25,28 @@ package me.lucko.luckperms.users;
import lombok.Getter;
import lombok.Setter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.api.implementation.internal.UserLink;
import org.bukkit.permissions.PermissionAttachment;
import me.lucko.luckperms.inject.LPPermissible;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class BukkitUser extends User {
private static Field permissionsField = null;
public static Field getPermissionsField() {
if (permissionsField == null) {
try {
permissionsField = PermissionAttachment.class.getDeclaredField("permissions");
permissionsField.setAccessible(true);
} catch (SecurityException | NoSuchFieldException e) {
e.printStackTrace();
}
}
return permissionsField;
}
private final LPBukkitPlugin plugin;
@Getter
@Setter
private PermissionAttachment attachment = null;
private LPPermissible lpPermissible = null;
@Getter
@Setter
private Map<String, Boolean> loginPreProcess = null;
BukkitUser(UUID uuid, LPBukkitPlugin plugin) {
super(uuid, plugin);
@@ -64,19 +58,33 @@ public class BukkitUser extends User {
this.plugin = plugin;
}
public boolean isOp() {
return lpPermissible != null && lpPermissible.isOp();
}
@SuppressWarnings("unchecked")
@Override
public void refreshPermissions() {
if (attachment == null) {
public synchronized void refreshPermissions() {
if (lpPermissible == null) {
return;
}
// Calculate the permissions that should be applied. This is done async, who cares about how long it takes or how often it's done.
Map<String, Boolean> toApply = getLocalPermissions(getPlugin().getConfiguration().getServer(), plugin.getUserManager().getWorldCache().get(getUuid()), null);
Map<String, Boolean> toApply = exportNodes(
new Contexts(
plugin.getContextManager().giveApplicableContext((Player) lpPermissible.getParent(), new HashMap<>()),
plugin.getConfiguration().isIncludingGlobalPerms(),
plugin.getConfiguration().isIncludingGlobalWorldPerms(),
true,
plugin.getConfiguration().isApplyingGlobalGroups(),
plugin.getConfiguration().isApplyingGlobalWorldGroups()
),
Collections.emptyList(),
true
);
try {
// The map in the LP PermissionAttachment is a ConcurrentHashMap. We can modify it's contents async.
Map<String, Boolean> existing = (Map<String, Boolean>) getPermissionsField().get(attachment);
Map<String, Boolean> existing = lpPermissible.getLuckPermsPermissions();
boolean different = false;
if (toApply.size() != existing.size()) {
@@ -94,17 +102,34 @@ public class BukkitUser extends User {
if (!different) return;
existing.clear();
lpPermissible.invalidateCache();
existing.putAll(toApply);
/* Must be called sync, as #recalculatePermissions is an unmodified Bukkit API call that is absolutely not thread safe.
Shouldn't be too taxing on the server. This only gets called when permissions have actually changed,
which is like once per user per login, assuming their permissions don't get modified. */
plugin.doSync(() -> attachment.getPermissible().recalculatePermissions());
if (plugin.getConfiguration().isAutoOp()) {
boolean op = false;
for (Map.Entry<String, Boolean> e : toApply.entrySet()) {
if (e.getKey().equalsIgnoreCase("luckperms.autoop") && e.getValue()) {
op = true;
break;
}
}
final boolean finalOp = op;
if (lpPermissible.isOp() != op) {
final Permissible parent = lpPermissible.getParent();
plugin.doSync(() -> parent.setOp(finalOp));
}
}
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
} catch (Exception e) {
e.printStackTrace();
}
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
if (plugin.getVaultHook() != null && plugin.getVaultHook().isHooked()) {
plugin.getVaultHook().getPermissionHook().getVaultUserManager().setupUser(this);
}
}
}
@@ -22,45 +22,39 @@
package me.lucko.luckperms.users;
import lombok.Getter;
import me.lucko.luckperms.LPBukkitPlugin;
import me.lucko.luckperms.api.context.ContextListener;
import me.lucko.luckperms.inject.Injector;
import org.bukkit.entity.Player;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class BukkitUserManager extends UserManager {
public class BukkitUserManager extends UserManager implements ContextListener<Player> {
private final LPBukkitPlugin plugin;
@Getter
private final Map<UUID, String> worldCache = new ConcurrentHashMap<>();
public BukkitUserManager(LPBukkitPlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
public void unload(User user) {
if (user != null) {
public void preUnload(User user) {
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (player != null) {
if (u.getLpPermissible() != null) {
Injector.unInject(player); // TODO is this needed?
u.setLpPermissible(null);
}
if (user instanceof BukkitUser) {
BukkitUser u = (BukkitUser) user;
if (u.getAttachment() != null) {
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (player != null) {
player.removeAttachment(u.getAttachment());
}
u.setAttachment(null);
if (plugin.getConfiguration().isAutoOp()) {
player.setOp(false);
}
}
getAll().remove(user.getUuid());
}
}
@@ -72,18 +66,17 @@ public class BukkitUserManager extends UserManager {
}
@Override
public User make(UUID uuid) {
return new BukkitUser(uuid, plugin);
}
@Override
public User make(UUID uuid, String username) {
return new BukkitUser(uuid, username, plugin);
public User apply(UserIdentifier id) {
BukkitUser user = id.getUsername() == null ?
new BukkitUser(id.getUuid(), plugin) :
new BukkitUser(id.getUuid(), id.getUsername(), plugin);
giveDefaultIfNeeded(user, false);
return user;
}
@Override
public void updateAllUsers() {
// Sometimes called async, so we need to get the players on the Bukkit thread.
// Sometimes called async, as we need to get the players on the Bukkit thread.
plugin.doSync(() -> {
Set<UUID> players = plugin.getServer().getOnlinePlayers().stream()
.map(p -> plugin.getUuidCache().getUUID(p.getUniqueId()))
@@ -91,4 +84,14 @@ public class BukkitUserManager extends UserManager {
plugin.doAsync(() -> players.forEach(u -> plugin.getDatastore().loadUser(u, "null")));
});
}
@Override
public void onContextChange(Player subject, Map.Entry<String, String> before, Map.Entry<String, String> current) throws Exception {
UUID internal = plugin.getUuidCache().getUUID(subject.getUniqueId());
User user = get(internal);
if (user != null) {
plugin.doAsync(user::refreshPermissions);
}
}
}
+67 -6
View File
@@ -8,9 +8,20 @@
# The name of the server, used for server specific permissions. Set to 'global' to disable.
server: global
# If users on this server should have their global permissions/groups applied.
# If users on this server should have their global permissions applied.
# If set to false, only server specific permissions will apply for users on this server
include-global: true
# If users on this server should have their global world permissions applied.
# If set to false, only world specific permissions will apply for users on this server
include-global-world: true
# If users on this server should have global (non-server specific) groups applied
apply-global-groups: true
# If users on this server should have global (non-world specific) groups applied
apply-global-world-groups: true
# If this server is in offline or online mode.
# This setting allows a player to have the same UUID across a network of offline mode/mixed servers.
@@ -22,28 +33,78 @@ include-global: true
# 2. You are only running one server instance using LuckPerms, (not a network)
# In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache)
# 3. If your proxy is running in offline mode, and you are using PaperSpigot (https://ci.destroystokyo.com/job/PaperSpigot/),
# you should set "bungee-online-mode" to false in the paper.yml, and set "online-mode" to true in all LuckPerms configs.
# This approach is thoroughly recommended for offline mode networks.
online-mode: true
# If the plugin should apply wildcard permissions.
# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered permissions matching
# the wildcard. This will only work for plugins that define all of their permissions to the server.
# the wildcard.
apply-wildcards: true
# If the plugin should parse regex permissions.
# If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the node, and resolve &
# apply all registered permissions matching the regex. This will only work for plugins that define all of their
# permissions to the server.
# apply all registered permissions matching the regex.
apply-regex: true
# If the plugin should complete and apply shorthand permissions.
# If set to true, LuckPerms will detect and expand shorthand node patterns.
apply-shorthand: true
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# If LuckPerms should print to console every time a plugin checks if a player has a permission
debug-permission-checks: false
# If the vanilla OP system is enabled. If set to false, all users will be de-opped, and the op/deop commands will be disabled.
enable-ops: true
# If set to true, any user with the permission "luckperms.autoop" will automatically be granted server operator status.
# This permission can be inherited, or set on specific servers/worlds, temporarily, etc.
# Additionally, setting this to true will force the "enable-ops" option above to false. All users will be de-opped unless
# they have the permission node, and the op/deop commands will be disabled.
#
# It is recommended that you use this option instead of assigning a single '*' permission.
auto-op: false
# If opped players should be allowed to use LuckPerms commands. Set to false to only allow users who have the permissions access to the commands
commands-allow-op: true
# The name of the server used within Vault operations. If you don't want Vault operations to be server specific, set this
# to "global".
vault-server: global
# If global permissions should be considered when retrieving meta or player groups
vault-include-global: true
# Mirrors world names. Whenever LuckPerms checks what world a user is in, if the world name is in this list, the value assigned
# will be sent forward for permission calculation instead.
world-rewrite:
#world_nether: world
#world_the_end: world
# Rewrites group names. The underlying name of the group does not change, just the output in commands / placeholders / Vault.
group-name-rewrite:
#default: Member
# Which storage method the plugin should use.
# Currently supported: mysql, sqlite, h2, flatfile, mongodb
# Currently supported: mysql, sqlite, h2, json, yaml, mongodb
# Fill out connection info below if you're using MySQL or MongoDB
storage-method: h2
# This block enables support for split datastores.
split-storage:
enabled: false
methods:
user: h2
group: h2
track: h2
uuid: h2
log: h2
data:
address: localhost:3306
database: minecraft
@@ -52,4 +113,4 @@ data:
# Set to -1 to disable. If this is the only instance accessing the datastore, you can disable syncing.
# e.g. if you're using sqlite or flatfile, this can be set to -1 to save resources.
sync-minutes: 3
sync-minutes: 3
+9 -123
View File
@@ -1,130 +1,16 @@
name: LuckPerms
author: Luck
version: ${release.version}.${git.closest.tag.commit.count}
main: me.lucko.luckperms.LPBukkitPlugin
softdepend: [Vault, PermissionsEx, GroupManager, PowerfulPerms, zPermissions] # For migration
description: A permissions plugin
author: Luck
website: https://github.com/lucko/LuckPerms
main: me.lucko.luckperms.LPBukkitPlugin
softdepend: [Vault, PermissionsEx, GroupManager, PowerfulPerms, zPermissions, bPermissions] # For migration
commands:
luckperms:
description: Manage permissions
aliases: [perms, permissions, lp, p, perm]
permissions:
luckperms.*:
description: Gives access to all LuckPerms commands
children:
luckperms.sync: true
luckperms.info: true
luckperms.debug: true
luckperms.import: true
luckperms.creategroup: true
luckperms.deletegroup: true
luckperms.listgroups: true
luckperms.createtrack: true
luckperms.deletetrack: true
luckperms.listtracks: true
luckperms.user.info: true
luckperms.user.getuuid: true
luckperms.user.listnodes: true
luckperms.user.haspermission: true
luckperms.user.inheritspermission: true
luckperms.user.setpermission: true
luckperms.user.unsetpermission: true
luckperms.user.addgroup: true
luckperms.user.removegroup: true
luckperms.user.settemppermission: true
luckperms.user.unsettemppermission: true
luckperms.user.addtempgroup: true
luckperms.user.removetempgroup: true
luckperms.user.setprimarygroup: true
luckperms.user.showtracks: true
luckperms.user.promote: true
luckperms.user.demote: true
luckperms.user.showpos: true
luckperms.user.clear: true
luckperms.group.info: true
luckperms.group.listnodes: true
luckperms.group.haspermission: true
luckperms.group.inheritspermission: true
luckperms.group.setpermission: true
luckperms.group.unsetpermission: true
luckperms.group.setinherit: true
luckperms.group.unsetinherit: true
luckperms.group.settemppermission: true
luckperms.group.unsettemppermission: true
luckperms.group.settempinherit: true
luckperms.group.unsettempinherit: true
luckperms.group.showtracks: true
luckperms.group.clear: true
luckperms.group.rename: true
luckperms.track.info: true
luckperms.track.append: true
luckperms.track.insert: true
luckperms.track.remove: true
luckperms.track.clear: true
luckperms.track.rename: true
luckperms.log.export: true
luckperms.log.userhistory: true
luckperms.log.grouphistory: true
luckperms.log.trackhistory: true
luckperms.log.notify: true
luckperms.log.recent: true
luckperms.log.search: true
luckperms.user.*:
description: Gives access to all LuckPerms user commands
children:
luckperms.user.info: true
luckperms.user.getuuid: true
luckperms.user.listnodes: true
luckperms.user.haspermission: true
luckperms.user.inheritspermission: true
luckperms.user.setpermission: true
luckperms.user.unsetpermission: true
luckperms.user.addgroup: true
luckperms.user.removegroup: true
luckperms.user.settemppermission: true
luckperms.user.unsettemppermission: true
luckperms.user.addtempgroup: true
luckperms.user.removetempgroup: true
luckperms.user.setprimarygroup: true
luckperms.user.showtracks: true
luckperms.user.promote: true
luckperms.user.demote: true
luckperms.user.showpos: true
luckperms.user.clear: true
luckperms.group.*:
description: Gives access to all LuckPerms group commands
children:
luckperms.group.info: true
luckperms.group.listnodes: true
luckperms.group.haspermission: true
luckperms.group.inheritspermission: true
luckperms.group.setpermission: true
luckperms.group.unsetpermission: true
luckperms.group.setinherit: true
luckperms.group.unsetinherit: true
luckperms.group.settemppermission: true
luckperms.group.unsettemppermission: true
luckperms.group.settempinherit: true
luckperms.group.unsettempinherit: true
luckperms.group.showtracks: true
luckperms.group.clear: true
luckperms.group.rename: true
luckperms.track.*:
description: Gives access to all LuckPerms track commands
children:
luckperms.track.info: true
luckperms.track.append: true
luckperms.track.insert: true
luckperms.track.remove: true
luckperms.track.clear: true
luckperms.track.rename: true
luckperms.log.*:
description: Gives access to all LuckPerms log commands
children:
luckperms.log.export: true
luckperms.log.userhistory: true
luckperms.log.grouphistory: true
luckperms.log.trackhistory: true
luckperms.log.notify: true
luckperms.log.recent: true
luckperms.log.search: true
# Permissions are registered programmatically instead of here.
+5 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.11-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -65,6 +65,10 @@
<pattern>org.h2</pattern>
<shadedPattern>me.lucko.luckperms.lib.h2</shadedPattern>
</relocation>
<relocation>
<pattern>org.sqlite</pattern>
<shadedPattern>me.lucko.luckperms.lib.sqlite</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
@@ -22,8 +22,12 @@
package me.lucko.luckperms;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Patterns;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
@@ -40,11 +44,16 @@ class BungeeCommand extends Command implements TabExecutor {
@Override
public void execute(CommandSender sender, String[] args) {
manager.onCommand(BungeeSenderFactory.get().wrap(sender), "bperms", Arrays.asList(args), Callback.empty());
manager.onCommand(
BungeeSenderFactory.get(manager.getPlugin()).wrap(sender),
"bperms",
Util.stripQuotes(Splitter.on(Patterns.COMMAND_SEPARATOR).omitEmptyStrings().splitToList(Joiner.on(' ').join(args))),
Callback.empty()
);
}
@Override
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
return manager.onTabComplete(BungeeSenderFactory.get().wrap(sender), Arrays.asList(args));
return manager.onTabComplete(BungeeSenderFactory.get(manager.getPlugin()).wrap(sender), Arrays.asList(args));
}
}
@@ -22,7 +22,7 @@
package me.lucko.luckperms;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.config.AbstractConfiguration;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
@@ -31,8 +31,10 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
class BungeeConfig extends LPConfiguration<LPBungeePlugin> {
class BungeeConfig extends AbstractConfiguration<LPBungeePlugin> {
private Configuration configuration;
BungeeConfig(LPBungeePlugin plugin) {
@@ -62,11 +64,6 @@ class BungeeConfig extends LPConfiguration<LPBungeePlugin> {
}
}
@Override
protected void set(String path, Object value) {
configuration.set(path, value);
}
@Override
protected String getString(String path, String def) {
return configuration.getString(path, def);
@@ -81,4 +78,19 @@ class BungeeConfig extends LPConfiguration<LPBungeePlugin> {
protected boolean getBoolean(String path, boolean def) {
return configuration.getBoolean(path, def);
}
@Override
protected Map<String, String> getMap(String path, Map<String, String> def) {
Map<String, String> map = new HashMap<>();
Configuration section = configuration.getSection(path);
if (section == null) {
return def;
}
for (String key : section.getKeys()) {
map.put(key, section.getString(key));
}
return map;
}
}
@@ -29,13 +29,13 @@ import me.lucko.luckperms.utils.AbstractListener;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.*;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PermissionCheckEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -52,22 +52,18 @@ public class BungeeListener extends AbstractListener implements Listener {
@EventHandler
public void onPlayerPermissionCheck(PermissionCheckEvent e) {
if (!(e.getSender() instanceof ProxiedPlayer)) {
e.setHasPermission(true);
return;
}
final ProxiedPlayer player = ((ProxiedPlayer) e.getSender());
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) return;
final String server = player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName());
Map<String, Boolean> local = user.getLocalPermissions(plugin.getConfiguration().getServer(), server, null, Collections.singletonList(e.getPermission()));
for (Map.Entry<String, Boolean> en : local.entrySet()) {
if (en.getKey().equalsIgnoreCase(e.getPermission())) {
e.setHasPermission(en.getValue());
return;
}
BungeePlayerCache playerCache = plugin.getPlayerCache().get(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (playerCache == null) {
return;
}
e.setHasPermission(playerCache.getPermissionValue(e.getPermission()));
}
@EventHandler
@@ -109,28 +105,20 @@ public class BungeeListener extends AbstractListener implements Listener {
@EventHandler
public void onPlayerPostLogin(PostLoginEvent e) {
final ProxiedPlayer player = e.getPlayer();
final WeakReference<ProxiedPlayer> p = new WeakReference<>(player);
final UUID internal = plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId());
final User user = plugin.getUserManager().get(internal);
final User user = plugin.getUserManager().get(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
if (user == null) {
plugin.getProxy().getScheduler().schedule(plugin, () -> {
final ProxiedPlayer pl = p.get();
if (pl != null) {
pl.sendMessage(WARN_MESSAGE);
}
}, 3, TimeUnit.SECONDS);
plugin.getProxy().getScheduler().schedule(plugin, () -> player.sendMessage(WARN_MESSAGE), 3, TimeUnit.SECONDS);
} else {
plugin.getPlayerCache().put(internal, new BungeePlayerCache(plugin, e.getPlayer().getName()));
user.refreshPermissions();
}
}
@EventHandler
public void onPlayerQuit(PlayerDisconnectEvent e) {
plugin.getPlayerCache().remove(plugin.getUuidCache().getUUID(e.getPlayer().getUniqueId()));
onLeave(e.getPlayer().getUniqueId());
}
@EventHandler
public void onPlayerServerSwitch(ServerSwitchEvent e) {
refreshPlayer(e.getPlayer().getUniqueId());
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms;
import lombok.Getter;
import me.lucko.luckperms.calculators.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BungeePlayerCache {
private final PermissionCalculator calculator;
@Getter
private final Map<String, Boolean> permissions = new ConcurrentHashMap<>();
public BungeePlayerCache(LuckPermsPlugin plugin, String name) {
List<PermissionProcessor> processors = new ArrayList<>(3);
processors.add(new MapProcessor(permissions));
if (plugin.getConfiguration().isApplyingWildcards()) {
processors.add(new WildcardProcessor(permissions));
}
if (plugin.getConfiguration().isApplyingRegex()) {
processors.add(new RegexProcessor(permissions));
}
calculator = new PermissionCalculator(plugin, name, plugin.getConfiguration().isDebugPermissionChecks(), processors);
}
public void invalidateCache() {
calculator.invalidateCache();
}
public boolean getPermissionValue(String permission) {
return calculator.getPermissionValue(permission).asBoolean();
}
}
@@ -22,8 +22,6 @@
package me.lucko.luckperms;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import me.lucko.luckperms.commands.SenderFactory;
import me.lucko.luckperms.constants.Constants;
import net.md_5.bungee.api.CommandSender;
@@ -32,12 +30,16 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BungeeSenderFactory extends SenderFactory<CommandSender> {
private static BungeeSenderFactory instance = null;
public static BungeeSenderFactory get() {
if (instance == null){
instance = new BungeeSenderFactory();
private BungeeSenderFactory(LuckPermsPlugin plugin) {
super(plugin);
}
public static synchronized BungeeSenderFactory get(LuckPermsPlugin plugin) {
if (instance == null) {
instance = new BungeeSenderFactory(plugin);
}
return instance;
}
@@ -24,12 +24,17 @@ package me.lucko.luckperms;
import lombok.Getter;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.config.LPConfiguration;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.contexts.BackendServerCalculator;
import me.lucko.luckperms.contexts.ContextManager;
import me.lucko.luckperms.contexts.ServerCalculator;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.data.Importer;
import me.lucko.luckperms.groups.GroupManager;
@@ -39,25 +44,23 @@ import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.storage.StorageFactory;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.BungeeUserManager;
import me.lucko.luckperms.users.UserManager;
import me.lucko.luckperms.utils.LocaleManager;
import me.lucko.luckperms.utils.LogFactory;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Getter
public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
private final Map<UUID, BungeePlayerCache> playerCache = new ConcurrentHashMap<>();
private final Set<UUID> ignoringLogs = ConcurrentHashMap.newKeySet();
private LPConfiguration configuration;
private UserManager userManager;
private BungeeUserManager userManager;
private GroupManager groupManager;
private TrackManager trackManager;
private Datastore datastore;
@@ -66,6 +69,8 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
private Logger log;
private Importer importer;
private ConsecutiveExecutor consecutiveExecutor;
private LocaleManager localeManager;
private ContextManager<ProxiedPlayer> contextManager;
@Override
public void onEnable() {
@@ -74,6 +79,17 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
getLog().info("Loading configuration...");
configuration = new BungeeConfig(this);
localeManager = new LocaleManager();
File locale = new File(getDataFolder(), "lang.yml");
if (locale.exists()) {
getLog().info("Found locale file. Attempting to load from it.");
try {
localeManager.loadFromFile(locale);
} catch (Exception e) {
e.printStackTrace();
}
}
// register events
getProxy().getPluginManager().registerListener(this, new BungeeListener(this));
@@ -88,20 +104,27 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
datastore = StorageFactory.getDatastore(this, "h2");
getLog().info("Loading internal permission managers...");
uuidCache = new UuidCache(getConfiguration().getOnlineMode());
uuidCache = new UuidCache(getConfiguration().isOnlineMode());
userManager = new BungeeUserManager(this);
groupManager = new GroupManager(this);
trackManager = new TrackManager();
importer = new Importer(commandManager);
consecutiveExecutor = new ConsecutiveExecutor(commandManager);
contextManager = new ContextManager<>();
BackendServerCalculator serverCalculator = new BackendServerCalculator();
getProxy().getPluginManager().registerListener(this, serverCalculator);
contextManager.registerCalculator(serverCalculator);
contextManager.registerCalculator(new ServerCalculator<>(getConfiguration().getServer()));
contextManager.registerListener(userManager);
int mins = getConfiguration().getSyncTime();
if (mins > 0) {
getProxy().getScheduler().schedule(this, new UpdateTask(this), mins, mins, TimeUnit.MINUTES);
}
// 20 times per second (once per "tick")
getProxy().getScheduler().schedule(this, BungeeSenderFactory.get(), 50L, 50L, TimeUnit.MILLISECONDS);
getProxy().getScheduler().schedule(this, BungeeSenderFactory.get(this), 50L, 50L, TimeUnit.MILLISECONDS);
getProxy().getScheduler().schedule(this, new ExpireTemporaryTask(this), 3L, 3L, TimeUnit.SECONDS);
getProxy().getScheduler().schedule(this, consecutiveExecutor, 1L, 1L, TimeUnit.SECONDS);
@@ -135,8 +158,8 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
}
@Override
public Type getType() {
return Type.BUNGEE;
public PlatformType getType() {
return PlatformType.BUNGEE;
}
@Override
@@ -160,13 +183,16 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
}
@Override
public List<Sender> getSenders() {
return getProxy().getPlayers().stream().map(p -> BungeeSenderFactory.get().wrap(p)).collect(Collectors.toList());
public List<Sender> getNotifyListeners() {
return getProxy().getPlayers().stream()
.map(p -> BungeeSenderFactory.get(this).wrap(p, Collections.singleton(Permission.LOG_NOTIFY)))
.filter(Permission.LOG_NOTIFY::isAuthorized)
.collect(Collectors.toList());
}
@Override
public Sender getConsoleSender() {
return BungeeSenderFactory.get().wrap(getProxy().getConsole());
return BungeeSenderFactory.get(this).wrap(getProxy().getConsole());
}
@Override
@@ -185,6 +211,11 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
return null;
}
@Override
public UUID getUUID(String playerName) {
return null; // Not needed on Bungee
}
@Override
public boolean isPluginLoaded(String name) {
return getProxy().getPluginManager().getPlugins().stream()
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.contexts;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.context.ContextCalculator;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ServerSwitchEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.util.Map;
public class BackendServerCalculator extends ContextCalculator<ProxiedPlayer> implements Listener {
private static final String WORLD_KEY = "world";
@Override
public Map<String, String> giveApplicableContext(ProxiedPlayer subject, Map<String, String> accumulator) {
String server = getServer(subject);
if (server != null) {
accumulator.put(WORLD_KEY, server);
}
return accumulator;
}
@Override
public boolean isContextApplicable(ProxiedPlayer subject, Map.Entry<String, String> context) {
if (!context.getKey().equals(WORLD_KEY)) {
return false;
}
String server = getServer(subject);
return server != null && server.equals(context.getValue());
}
@EventHandler
public void onPlayerServerSwitch(ServerSwitchEvent e) {
pushUpdate(e.getPlayer(), Maps.immutableEntry("null", "null"), Maps.immutableEntry(WORLD_KEY, getServer(e.getPlayer())));
}
private static String getServer(ProxiedPlayer player) {
return player.getServer() == null ? null : (player.getServer().getInfo() == null ? null : player.getServer().getInfo().getName());
}
}
@@ -22,21 +22,78 @@
package me.lucko.luckperms.users;
import me.lucko.luckperms.BungeePlayerCache;
import me.lucko.luckperms.LPBungeePlugin;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.event.events.UserPermissionRefreshEvent;
import me.lucko.luckperms.api.implementation.internal.UserLink;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class BungeeUser extends User {
private final LPBungeePlugin plugin;
BungeeUser(UUID uuid, LPBungeePlugin plugin) {
super(uuid, plugin);
this.plugin = plugin;
}
BungeeUser(UUID uuid, String username, LPBungeePlugin plugin) {
super(uuid, username, plugin);
this.plugin = plugin;
}
@Override
public void refreshPermissions() {
// Do nothing. Permissions are applied when needed in a listener.
public synchronized void refreshPermissions() {
ProxiedPlayer player = plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid()));
if (player == null) {
return;
}
BungeePlayerCache playerCache = plugin.getPlayerCache().get(getUuid());
if (playerCache == null) {
return;
}
// Calculate the permissions that should be applied. This is done async.
Map<String, Boolean> toApply = exportNodes(
new Contexts(
plugin.getContextManager().giveApplicableContext(player, new HashMap<>()),
plugin.getConfiguration().isIncludingGlobalPerms(),
plugin.getConfiguration().isIncludingGlobalWorldPerms(),
true,
plugin.getConfiguration().isApplyingGlobalGroups(),
plugin.getConfiguration().isApplyingGlobalWorldGroups()
),
Collections.emptyList(),
true
);
Map<String, Boolean> existing = playerCache.getPermissions();
boolean different = false;
if (toApply.size() != existing.size()) {
different = true;
} else {
for (Map.Entry<String, Boolean> e : existing.entrySet()) {
if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) {
continue;
}
different = true;
break;
}
}
if (!different) return;
existing.clear();
playerCache.invalidateCache();
existing.putAll(toApply);
plugin.getApiProvider().fireEventAsync(new UserPermissionRefreshEvent(new UserLink(this)));
}
}
@@ -23,10 +23,13 @@
package me.lucko.luckperms.users;
import me.lucko.luckperms.LPBungeePlugin;
import me.lucko.luckperms.api.context.ContextListener;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.Map;
import java.util.UUID;
public class BungeeUserManager extends UserManager {
public class BungeeUserManager extends UserManager implements ContextListener<ProxiedPlayer> {
private final LPBungeePlugin plugin;
public BungeeUserManager(LPBungeePlugin plugin) {
@@ -34,15 +37,6 @@ public class BungeeUserManager extends UserManager {
this.plugin = plugin;
}
@Override
public void unload(User user) {
if (user != null) {
// Cannot clear the ProxiedPlayer's permission map, they're leaving so that will get GCed anyway
// Calling getPermissions.clear() throws an UnsupportedOperationException
getAll().remove(user.getUuid());
}
}
@Override
public void cleanup(User user) {
if (plugin.getProxy().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())) == null) {
@@ -51,13 +45,12 @@ public class BungeeUserManager extends UserManager {
}
@Override
public User make(UUID uuid) {
return new BungeeUser(uuid, plugin);
}
@Override
public User make(UUID uuid, String username) {
return new BungeeUser(uuid, username, plugin);
public User apply(UserIdentifier id) {
BungeeUser user = id.getUsername() == null ?
new BungeeUser(id.getUuid(), plugin) :
new BungeeUser(id.getUuid(), id.getUsername(), plugin);
giveDefaultIfNeeded(user, false);
return user;
}
@Override
@@ -66,4 +59,14 @@ public class BungeeUserManager extends UserManager {
.map(p -> plugin.getUuidCache().getUUID(p.getUniqueId()))
.forEach(u -> plugin.getDatastore().loadUser(u, "null"));
}
@Override
public void onContextChange(ProxiedPlayer subject, Map.Entry<String, String> before, Map.Entry<String, String> current) throws Exception {
UUID internal = plugin.getUuidCache().getUUID(subject.getUniqueId());
User user = get(internal);
if (user != null) {
plugin.doAsync(user::refreshPermissions);
}
}
}
+47 -5
View File
@@ -8,9 +8,20 @@
# The name of the server, used for server specific permissions. Set to 'global' to disable.
server: bungee
# If users on this server should have their global permissions/groups applied.
# If users on this server should have their global permissions applied.
# If set to false, only server specific permissions will apply for users on this server
include-global: false
# If users on this server should have their global world permissions applied.
# If set to false, only world specific permissions will apply for users on this server
include-global-world: true
# If users on this server should have global (non-server specific) groups applied
apply-global-groups: true
# If users on this server should have global (non-world specific) groups applied
apply-global-world-groups: true
# If this server is in offline or online mode.
# This setting allows a player to have the same UUID across a network of offline mode/mixed servers.
@@ -22,28 +33,59 @@ include-global: false
# 2. You are only running one server instance using LuckPerms, (not a network)
# In this case, set online-mode to true no matter what is set in server.properties. (we can just fallback to the servers uuid cache)
# 3. If your proxy is running in offline mode, and you are using PaperSpigot (https://ci.destroystokyo.com/job/PaperSpigot/),
# you should set "bungee-online-mode" to false in the paper.yml, and set "online-mode" to true in all LuckPerms configs.
# This approach is thoroughly recommended for offline mode networks.
online-mode: true
# If the plugin should apply wildcard permissions.
# If set to true, LuckPerms will detect wildcard permissions, and resolve & apply all registered permissions matching
# the wildcard. This will only work for plugins that define all of their permissions to the server.
# the wildcard.
apply-wildcards: true
# If the plugin should parse regex permissions.
# If set to true, LuckPerms will detect regex permissions, marked with "r=" at the start of the node, and resolve &
# apply all registered permissions matching the regex. This will only work for plugins that define all of their
# permissions to the server.
# apply all registered permissions matching the regex.
apply-regex: true
# If the plugin should complete and apply shorthand permissions.
# If set to true, LuckPerms will detect and expand shorthand node patterns.
apply-shorthand: true
# If the plugin should send log notifications to users whenever permissions are modified.
log-notify: true
# If LuckPerms should print to console every time a plugin checks if a player has a permission
debug-permission-checks: false
# Mirrors world names. Whenever LuckPerms checks what world a user is in, if the world name is in this list, the value assigned
# will be sent forward for permission calculation instead.
# Remember world names on BungeeCord relate to the backend server name a user is connected to.
world-rewrite:
#hub2: hub
#hub3: hub
#opfactions: factions
# Rewrites group names. The underlying name of the group does not change, just the output in commands / placeholders / Vault.
group-name-rewrite:
#default: Member
# Which storage method the plugin should use.
# Currently supported: mysql, sqlite, h2, flatfile, mongodb
# Currently supported: mysql, sqlite, h2, json, yaml, mongodb
# Fill out connection info below if you're using MySQL or MongoDB
storage-method: h2
# This block enables support for split datastores.
split-storage:
enabled: false
methods:
user: h2
group: h2
track: h2
uuid: h2
log: h2
data:
address: localhost:3306
database: minecraft
+3 -3
View File
@@ -1,6 +1,6 @@
name: LuckPerms
author: Luck
version: ${release.version}.${git.closest.tag.commit.count}
main: me.lucko.luckperms.LPBungeePlugin
softdepend: [PowerfulPerms] # For migration
description: A permissions plugin
author: Luck
main: me.lucko.luckperms.LPBungeePlugin
softdepends: [PowerfulPerms] # For migration
+18 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId>
<version>2.6-SNAPSHOT</version>
<version>2.11-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -67,6 +67,13 @@
<version>1.7.9</version>
<scope>compile</scope>
</dependency>
<!-- SnakeYAML -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.14</version>
<scope>provided</scope>
</dependency>
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
@@ -105,6 +112,10 @@
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>net.gravitydevelopment.updater</groupId>
<artifactId>updater</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
@@ -125,6 +136,12 @@
<version>3.0-dev-52</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.bananaco</groupId>
<artifactId>bpermissions-api</artifactId>
<version>2.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -23,17 +23,20 @@
package me.lucko.luckperms;
import me.lucko.luckperms.api.Logger;
import me.lucko.luckperms.api.PlatformType;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.commands.ConsecutiveExecutor;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.config.LPConfiguration;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.core.LPConfiguration;
import me.lucko.luckperms.contexts.ContextManager;
import me.lucko.luckperms.core.UuidCache;
import me.lucko.luckperms.data.Importer;
import me.lucko.luckperms.groups.GroupManager;
import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.UserManager;
import me.lucko.luckperms.utils.LocaleManager;
import java.io.File;
import java.util.List;
@@ -59,17 +62,18 @@ public interface LuckPermsPlugin {
ApiProvider getApiProvider();
Importer getImporter();
ConsecutiveExecutor getConsecutiveExecutor();
LocaleManager getLocaleManager();
ContextManager getContextManager();
/**
* @return the version of the plugin
*/
String getVersion();
/**
* @return the platform type
*/
Type getType();
PlatformType getType();
/**
* @return the main plugin directory
@@ -103,7 +107,7 @@ public interface LuckPermsPlugin {
/**
* @return a {@link List} of senders online on the platform
*/
List<Sender> getSenders();
List<Sender> getNotifyListeners();
/**
* @return the console sender of the instance
@@ -136,6 +140,13 @@ public interface LuckPermsPlugin {
*/
Object getService(Class clazz);
/**
* Used as a backup for migration
* @param playerName the players name
* @return a uuid if found, or null if not
*/
UUID getUUID(String playerName);
/**
* Checks if a plugin is loaded on the platform
* @param name the name of the plugin
@@ -160,8 +171,4 @@ public interface LuckPermsPlugin {
*/
void doSync(Runnable r);
enum Type {
BUKKIT, BUNGEE, SPONGE;
}
}
@@ -27,9 +27,12 @@ import lombok.AllArgsConstructor;
import lombok.NonNull;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.*;
import me.lucko.luckperms.api.context.ContextListener;
import me.lucko.luckperms.api.context.IContextCalculator;
import me.lucko.luckperms.api.event.LPEvent;
import me.lucko.luckperms.api.event.LPListener;
import me.lucko.luckperms.api.implementation.internal.*;
import me.lucko.luckperms.users.UserIdentifier;
import java.util.Optional;
import java.util.Set;
@@ -39,7 +42,7 @@ import java.util.stream.Collectors;
import static me.lucko.luckperms.api.implementation.internal.Utils.checkNode;
/**
* Provides static access to LuckPerms
* Implements the LuckPerms API using the plugin instance
*/
@AllArgsConstructor
public class ApiProvider implements LuckPermsApi {
@@ -67,7 +70,7 @@ public class ApiProvider implements LuckPermsApi {
@Override
public double getApiVersion() {
return 1.6;
return 2.11;
}
@Override
@@ -75,6 +78,11 @@ public class ApiProvider implements LuckPermsApi {
return plugin.getVersion();
}
@Override
public PlatformType getPlatformType() {
return plugin.getType();
}
@Override
public void registerListener(@NonNull LPListener listener) {
eventBus.register(listener);
@@ -142,7 +150,7 @@ public class ApiProvider implements LuckPermsApi {
@Override
public boolean isUserLoaded(@NonNull UUID uuid) {
return plugin.getUserManager().isLoaded(uuid);
return plugin.getUserManager().isLoaded(UserIdentifier.of(uuid, null));
}
@Override
@@ -203,6 +211,18 @@ public class ApiProvider implements LuckPermsApi {
@Override
public Node.Builder buildNode(@NonNull String permission) throws IllegalArgumentException {
return new me.lucko.luckperms.utils.Node.Builder(checkNode(permission));
return new me.lucko.luckperms.core.Node.Builder(checkNode(permission));
}
@SuppressWarnings("unchecked")
@Override
public void registerContextCalculator(IContextCalculator<?> contextCalculator) {
plugin.getContextManager().registerCalculator(contextCalculator);
}
@SuppressWarnings("unchecked")
@Override
public void registerContextListener(ContextListener<?> contextListener) {
plugin.getContextManager().registerListener(contextListener);
}
}
@@ -27,12 +27,14 @@ import me.lucko.luckperms.api.LPConfiguration;
import me.lucko.luckperms.api.data.DatastoreConfiguration;
import me.lucko.luckperms.api.data.MySQLConfiguration;
import java.util.Map;
/**
* Provides a link between {@link LPConfiguration} and {@link me.lucko.luckperms.core.LPConfiguration}
* Provides a link between {@link LPConfiguration} and {@link me.lucko.luckperms.config.LPConfiguration}
*/
@AllArgsConstructor
public class LPConfigurationLink implements LPConfiguration {
private final me.lucko.luckperms.core.LPConfiguration master;
private final me.lucko.luckperms.config.LPConfiguration master;
@Override
public String getServer() {
@@ -56,27 +58,77 @@ public class LPConfigurationLink implements LPConfiguration {
@Override
public boolean getIncludeGlobalPerms() {
return master.getIncludeGlobalPerms();
return master.isIncludingGlobalPerms();
}
@Override
public boolean getIncludeGlobalWorldPerms() {
return master.isIncludingGlobalWorldPerms();
}
@Override
public boolean getApplyGlobalGroups() {
return master.isApplyingGlobalGroups();
}
@Override
public boolean getApplyGlobalWorldGroups() {
return master.isApplyingGlobalWorldGroups();
}
@Override
public boolean getOnlineMode() {
return master.getOnlineMode();
return master.isOnlineMode();
}
@Override
public boolean getApplyWildcards() {
return master.getApplyWildcards();
return master.isApplyingWildcards();
}
@Override
public boolean getApplyRegex() {
return master.getApplyRegex();
return master.isApplyingRegex();
}
@Override
public boolean getApplyShorthand() {
return master.getApplyShorthand();
return master.isApplyingShorthand();
}
@Override
public boolean getLogNotify() {
return master.isLogNotify();
}
@Override
public boolean getDebugPermissionChecks() {
return master.isDebugPermissionChecks();
}
@Override
public boolean getEnableOps() {
return master.isOpsEnabled();
}
@Override
public boolean getCommandsAllowOp() {
return master.isCommandsAllowOp();
}
@Override
public boolean getAutoOp() {
return master.isAutoOp();
}
@Override
public String getVaultServer() {
return master.getVaultServer();
}
@Override
public boolean getVaultIncludeGlobal() {
return master.isVaultIncludingGlobal();
}
@SuppressWarnings("deprecation")
@@ -94,4 +146,14 @@ public class LPConfigurationLink implements LPConfiguration {
public String getStorageMethod() {
return master.getStorageMethod();
}
@Override
public boolean getSplitStorage() {
return master.isSplitStorage();
}
@Override
public Map<String, String> getSplitStorageOptions() {
return master.getSplitStorageOptions();
}
}
@@ -24,9 +24,7 @@ package me.lucko.luckperms.api.implementation.internal;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.PermissionHolder;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.*;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
@@ -38,7 +36,7 @@ import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
/**
* Provides a link between {@link PermissionHolder} and {@link me.lucko.luckperms.core.PermissionHolder}
*/
@SuppressWarnings({"unused", "deprecation"})
@SuppressWarnings("unused")
@AllArgsConstructor
public class PermissionHolderLink implements PermissionHolder {
@@ -51,23 +49,33 @@ public class PermissionHolderLink implements PermissionHolder {
}
@Override
public SortedSet<Node> getPermissions() {
return Collections.unmodifiableSortedSet(master.getPermissions());
public SortedSet<? extends Node> getPermissions() {
return master.getPermissions(false);
}
@Override
public Set<Node> getEnduringPermissions() {
return Collections.unmodifiableSet(master.getNodes());
return master.getNodes();
}
@Override
public Set<Node> getTransientPermissions() {
return Collections.unmodifiableSet(master.getTransientNodes());
return master.getTransientNodes();
}
@Override
public Set<Node> getAllNodes() {
return Collections.unmodifiableSet(master.getAllNodes(null));
return Collections.unmodifiableSet(master.getAllNodes(Collections.emptyList(), Contexts.allowAll()));
}
@Override
public SortedSet<LocalizedNode> getAllNodes(@NonNull Contexts contexts) {
return master.getAllNodes(Collections.emptyList(), contexts);
}
@Override
public Set<LocalizedNode> getAllNodesFiltered(@NonNull Contexts contexts) {
return master.getAllNodesFiltered(contexts);
}
@Override
@@ -232,27 +240,50 @@ public class PermissionHolderLink implements PermissionHolder {
@Override
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups, List<String> possibleNodes) {
return master.getLocalPermissions(server, world, excludedGroups, possibleNodes);
Map<String, String> context = new HashMap<>();
if (server != null && !server.equals("")) {
context.put("server", server);
}
if (world != null && !world.equals("")) {
context.put("world", world);
}
return master.exportNodes(new Contexts(context, true, true, true, true, true), Collections.emptyList(), false);
}
@Override
public Map<String, Boolean> getLocalPermissions(String server, String world, List<String> excludedGroups) {
return master.getLocalPermissions(server, world, excludedGroups);
Map<String, String> context = new HashMap<>();
if (server != null && !server.equals("")) {
context.put("server", server);
}
if (world != null && !world.equals("")) {
context.put("world", world);
}
return master.exportNodes(new Contexts(context, true, true, true, true, true), Collections.emptyList(), false);
}
@Override
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups, List<String> possibleNodes) {
return master.getLocalPermissions(server, excludedGroups, possibleNodes);
return getLocalPermissions(server, null, excludedGroups, possibleNodes);
}
@Override
public Map<String, Boolean> getLocalPermissions(String server, List<String> excludedGroups) {
return master.getLocalPermissions(server, excludedGroups);
return getLocalPermissions(server, null, excludedGroups, null);
}
@Override
public Map<String, Boolean> getPermissions(String server, String world, Map<String, String> extraContext, boolean includeGlobal, List<String> possibleNodes, boolean applyGroups) {
return master.exportNodes(server, world, extraContext, includeGlobal, applyGroups, possibleNodes);
if (extraContext == null) {
extraContext = new HashMap<>();
}
if (server != null && !server.equals("")) {
extraContext.put("server", server);
}
if (world != null && !world.equals("")) {
extraContext.put("world", world);
}
return master.exportNodes(new Contexts(extraContext, includeGlobal, includeGlobal, applyGroups, true, true), possibleNodes, false);
}
@Override
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.calculators;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.api.Tristate;
import java.util.Map;
@AllArgsConstructor
public class MapProcessor implements PermissionProcessor {
@Getter
private final Map<String, Boolean> map;
@Override
public Tristate hasPermission(String permission) {
if (map.containsKey(permission)) {
return Tristate.fromBoolean(map.get(permission));
}
return Tristate.UNDEFINED;
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.calculators;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Tristate;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Calculates and caches permissions
*/
@RequiredArgsConstructor
public class PermissionCalculator {
private final LuckPermsPlugin plugin;
private final String objectName;
private final boolean debug;
private final List<PermissionProcessor> processors;
private final Map<String, Tristate> cache = new ConcurrentHashMap<>();
public void invalidateCache() {
cache.clear();
}
public Tristate getPermissionValue(String permission) {
permission = permission.toLowerCase();
Tristate t = cache.computeIfAbsent(permission, this::lookupPermissionValue);
if (debug) {
plugin.getLog().info("Checking if " + objectName + " has permission: " + permission + " - (" + t.toString() + ")");
}
return t;
}
private Tristate lookupPermissionValue(String permission) {
for (PermissionProcessor processor : processors) {
Tristate v = processor.hasPermission(permission);
if (v == Tristate.UNDEFINED) {
continue;
}
return v;
}
return Tristate.UNDEFINED;
}
}
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.calculators;
import me.lucko.luckperms.api.Tristate;
public interface PermissionProcessor {
Tristate hasPermission(String permission);
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.calculators;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.constants.Patterns;
import java.util.Map;
import java.util.regex.Pattern;
@AllArgsConstructor
public class RegexProcessor implements PermissionProcessor {
@Getter
private final Map<String, Boolean> map;
@Override
public Tristate hasPermission(String permission) {
for (Map.Entry<String, Boolean> e : map.entrySet()) {
if (e.getKey().toLowerCase().startsWith("r=")) {
Pattern p = Patterns.compile(e.getKey().substring(2));
if (p == null) {
continue;
}
if (p.matcher(permission).matches()) {
return Tristate.fromBoolean(e.getValue());
}
}
}
return Tristate.UNDEFINED;
}
}
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.calculators;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.api.Tristate;
import java.util.Map;
@AllArgsConstructor
public class WildcardProcessor implements PermissionProcessor {
@Getter
private final Map<String, Boolean> map;
@Override
public Tristate hasPermission(String permission) {
String node = permission;
while (node.contains(".")) {
int endIndex = node.lastIndexOf('.');
if (endIndex == -1) {
break;
}
node = node.substring(0, endIndex);
if (!isEmpty(node)) {
if (map.containsKey(node + ".*")) {
return Tristate.fromBoolean(map.get(node + ".*"));
}
}
}
if (map.containsKey("'*'")) {
return Tristate.fromBoolean(map.get("'*'"));
}
if (map.containsKey("*")) {
return Tristate.fromBoolean(map.get("*"));
}
return Tristate.UNDEFINED;
}
private static boolean isEmpty(String s) {
if (s.equals("")) {
return true;
}
char[] chars = s.toCharArray();
for (char c : chars) {
if (c != '.') {
return false;
}
}
return true;
}
}
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Arg {
public static Arg create(String name, boolean required, String description) {
return new Arg(name, required, description);
}
public static ImmutableList<Arg> list(Arg... args) {
return ImmutableList.copyOf(args);
}
private final String name;
private final boolean required;
private final String description;
public String asPrettyString() {
if (required) {
return "&8<&7" + name + "&8>";
} else {
return "&8[&7" + name + "&8]";
}
}
}
@@ -39,7 +39,7 @@ import me.lucko.luckperms.commands.track.DeleteTrack;
import me.lucko.luckperms.commands.track.ListTracks;
import me.lucko.luckperms.commands.track.TrackMainCommand;
import me.lucko.luckperms.commands.user.UserMainCommand;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.commands.usersbulkedit.UsersBulkEditMainCommand;
import java.util.ArrayList;
import java.util.Collections;
@@ -49,6 +49,7 @@ import java.util.stream.Collectors;
@AllArgsConstructor
public class CommandManager {
@Getter
private final LuckPermsPlugin plugin;
@Getter
@@ -61,8 +62,10 @@ public class CommandManager {
.add(new InfoCommand())
.add(new DebugCommand())
.add(new ImportCommand())
.add(new ExportCommand())
.add(new QueueCommand())
.add(new MigrationMainCommand())
.add(new UsersBulkEditMainCommand())
.add(new CreateGroup())
.add(new DeleteGroup())
.add(new ListGroups())
@@ -176,10 +179,9 @@ public class CommandManager {
}
private void sendCommandUsage(Sender sender, String label) {
Message.INFO_BRIEF.send(sender, plugin.getVersion());
Util.sendPluginMessage(sender, "&2Running &bLuckPerms v" + plugin.getVersion() + "&2.");
mainCommands.stream()
.filter(c -> c.isAuthorized(sender))
.forEach(c -> Util.sendPluginMessage(sender, "&e-> &d" + String.format(c.getUsage(), label)));
.forEach(c -> Util.sendPluginMessage(sender, "&3> &a" + String.format(c.getUsage(), label)));
}
}
@@ -98,7 +98,7 @@ public abstract class MainCommand<T> {
}
if (sub.getIsArgumentInvalid().test(strippedArgs.size())) {
sub.sendUsage(sender, label);
sub.sendDetailedUsage(sender);
return CommandResult.INVALID_ARGS;
}
@@ -137,7 +137,7 @@ public abstract class MainCommand<T> {
*/
protected void sendUsage(Sender sender, String label) {
if (getSubCommands().isEmpty()) {
Util.sendPluginMessage(sender, "&e-> &d" + String.format(getUsage(), label));
Util.sendPluginMessage(sender, "&3> &a" + String.format(getUsage(), label));
return;
}
@@ -146,10 +146,10 @@ public abstract class MainCommand<T> {
.collect(Collectors.toList());
if (subs.size() > 0) {
Util.sendPluginMessage(sender, "&e" + getName() + " Sub Commands:");
Util.sendPluginMessage(sender, "&b" + getName() + " Sub Commands: &7(" + String.format(getUsage(), label) + " ...)");
for (SubCommand s : subs) {
s.sendUsage(sender, label);
s.sendUsage(sender);
}
} else {
@@ -22,6 +22,7 @@
package me.lucko.luckperms.commands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.constants.Permission;
import java.util.UUID;
@@ -31,6 +32,7 @@ import java.util.UUID;
*/
public interface Sender {
LuckPermsPlugin getPlatform();
String getName();
UUID getUuid();
void sendMessage(String s);
@@ -22,20 +22,27 @@
package me.lucko.luckperms.commands;
import com.google.common.collect.ImmutableMap;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.constants.Constants;
import me.lucko.luckperms.constants.Permission;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
* Factory class to make a thread-safe sender instance
* @param <T> the command sender type
*/
@RequiredArgsConstructor
public abstract class SenderFactory<T> implements Runnable {
private final LuckPermsPlugin plugin;
private final Map<T, List<String>> messages = new HashMap<>();
//private final List<Map.Entry<T, String>> messages = new ArrayList<>();
private final AtomicBoolean shouldSend = new AtomicBoolean(false);
private final SenderFactory<T> factory = this;
protected abstract String getName(T t);
@@ -44,50 +51,82 @@ public abstract class SenderFactory<T> implements Runnable {
protected abstract boolean hasPermission(T t, String node);
public final Sender wrap(T t) {
return new Sender() {
final WeakReference<T> tRef = new WeakReference<>(t);
return new SenderImp(plugin, t, null);
}
// Cache these permissions, so they can be accessed async
final Map<Permission, Boolean> perms = Arrays.stream(Permission.values())
.map(p -> new AbstractMap.SimpleEntry<>(p, factory.hasPermission(t, p.getNode())))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
@Getter
final String name = factory.getName(t);
@Getter
final UUID uuid = factory.getUuid(t);
@Override
public void sendMessage(String s) {
final T t = tRef.get();
if (t != null) {
synchronized (messages) {
if (!messages.containsKey(t)) {
messages.put(t, new ArrayList<>());
}
messages.get(t).add(s);
}
}
}
@Override
public boolean hasPermission(Permission permission) {
synchronized (perms) {
return perms.get(permission);
}
}
};
public final Sender wrap(T t, Set<Permission> toCheck) {
return new SenderImp(plugin, t, toCheck);
}
@Override
public final void run() {
if (!shouldSend.getAndSet(false)) {
return;
}
synchronized (messages) {
if (!messages.isEmpty()) {
messages.entrySet().forEach(e -> e.getValue().forEach(s -> factory.sendMessage(e.getKey(), s)));
messages.clear();
for (Map.Entry<T, List<String>> e : messages.entrySet()) {
for (String s : e.getValue()) {
factory.sendMessage(e.getKey(), s);
}
}
messages.clear();
}
}
private class SenderImp implements Sender {
private final WeakReference<T> tRef;
// Cache these permissions, so they can be accessed async
private Map<Permission, Boolean> perms;
@Getter
private final String name;
@Getter
private final UUID uuid;
private final boolean console;
@Getter
private final LuckPermsPlugin platform;
private SenderImp(LuckPermsPlugin platform, T t, Set<Permission> toCheck) {
this.platform = platform;
this.tRef = new WeakReference<>(t);
this.name = factory.getName(t);
this.uuid = factory.getUuid(t);
this.console = this.uuid.equals(Constants.getConsoleUUID()) || this.uuid.equals(Constants.getImporterUUID());
if (!this.console) {
if (toCheck == null || toCheck.isEmpty()) {
this.perms = ImmutableMap.copyOf(Arrays.stream(Permission.values())
.collect(Collectors.toMap(p -> p, p -> factory.hasPermission(t, p.getNode()))));
} else {
this.perms = ImmutableMap.copyOf(toCheck.stream().collect(Collectors.toMap(p -> p, p -> factory.hasPermission(t, p.getNode()))));
}
}
}
@Override
public void sendMessage(String s) {
final T t = tRef.get();
if (t != null) {
synchronized (messages) {
if (!messages.containsKey(t)) {
messages.put(t, new ArrayList<>());
}
messages.get(t).add(s);
}
shouldSend.set(true);
}
}
@Override
public boolean hasPermission(Permission permission) {
return console || perms.get(permission);
}
}
}
@@ -22,6 +22,7 @@
package me.lucko.luckperms.commands;
import com.google.common.collect.ImmutableList;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.LuckPermsPlugin;
@@ -55,11 +56,6 @@ public abstract class SubCommand<T> {
*/
private final String description;
/**
* The command usage
*/
private final String usage;
/**
* The permission needed to use this command
*/
@@ -70,6 +66,8 @@ public abstract class SubCommand<T> {
*/
private final Predicate<? super Integer> isArgumentInvalid;
private final ImmutableList<Arg> args;
/**
* Called when this sub command is ran
* @param plugin a link to the main plugin instance
@@ -95,10 +93,28 @@ public abstract class SubCommand<T> {
/**
* Send the command usage to a sender
* @param sender the sender to send the usage to
* @param label the command label used
*/
public void sendUsage(Sender sender, String label) {
Util.sendPluginMessage(sender, "&e-> &d" + String.format(getUsage(), label));
public void sendUsage(Sender sender) {
String usage = "";
if (args != null) {
usage += "&3 - &7";
for (Arg arg : args) {
usage += arg.asPrettyString() + " ";
}
}
Util.sendPluginMessage(sender, "&3> &a" + getName() + usage);
}
public void sendDetailedUsage(Sender sender) {
Util.sendPluginMessage(sender, "&3&lCommand Usage &3- &b" + getName());
Util.sendPluginMessage(sender, "&b> &7" + getDescription());
if (args != null) {
Util.sendPluginMessage(sender, "&3Arguments:");
for (Arg arg : args) {
Util.sendPluginMessage(sender, "&b- " + arg.asPrettyString() + "&3 -> &7" + arg.getDescription());
}
}
}
/**
@@ -110,10 +126,11 @@ public abstract class SubCommand<T> {
return permission.isAuthorized(sender);
}
/*
----------------------------------------------------------------------------------
Utility methods used by #onTabComplete and #execute implementations in sub classes
----------------------------------------------------------------------------------
* ----------------------------------------------------------------------------------
* Utility methods used by #onTabComplete and #execute implementations in sub classes
* ----------------------------------------------------------------------------------
*/
protected static List<String> getGroupTabComplete(List<String> args, LuckPermsPlugin plugin) {
@@ -23,13 +23,14 @@
package me.lucko.luckperms.commands;
import lombok.experimental.UtilityClass;
import me.lucko.luckperms.api.LocalizedNode;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.utils.DateUtil;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
@UtilityClass
public class Util {
@@ -38,6 +39,22 @@ public class Util {
sender.sendMessage(color(Message.PREFIX + message));
}
public static List<String> stripQuotes(List<String> input) {
input = new ArrayList<>(input);
ListIterator<String> iterator = input.listIterator();
while (iterator.hasNext()) {
String value = iterator.next();
if (!(value.length() >= 3)) {
continue;
}
if (value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') {
iterator.set(value.substring(1, value.length() - 1));
}
}
return input;
}
public static String color(String s) {
return translateAlternateColorCodes('&', s);
}
@@ -64,27 +81,38 @@ public class Util {
return b ? "&atrue" : "&cfalse";
}
public static void sendBoolean(Sender sender, String node, boolean b) {
sender.sendMessage(Util.color("&b" + node + ": " + formatBoolean(b)));
public static String formatTristate(Tristate t) {
switch (t) {
case TRUE:
return "&atrue";
case FALSE:
return "&cfalse";
default:
return "&cundefined";
}
}
public static void sendTristate(Sender sender, String node, Tristate t) {
sender.sendMessage(Util.color("&b" + node + ": " + formatTristate(t)));
}
public static String listToCommaSep(List<String> strings) {
if (strings.isEmpty()) return "&6None";
if (strings.isEmpty()) return "&bNone";
StringBuilder sb = new StringBuilder();
strings.forEach(s -> sb.append("&6").append(s).append("&7, "));
strings.forEach(s -> sb.append("&3").append(s).append("&7, "));
return sb.delete(sb.length() - 2, sb.length()).toString();
}
public static String listToArrowSep(List<String> strings, String highlight) {
if (strings.isEmpty()) return "&6None";
if (strings.isEmpty()) return "&bNone";
StringBuilder sb = new StringBuilder();
for (String s : strings) {
if (s.equalsIgnoreCase(highlight)) {
sb.append("&e").append(s).append("&7 ---> ");
sb.append("&b").append(s).append("&7 ---> ");
} else {
sb.append("&6").append(s).append("&7 ---> ");
sb.append("&3").append(s).append("&7 ---> ");
}
}
return sb.delete(sb.length() - 6, sb.length()).toString();
@@ -96,11 +124,11 @@ public class Util {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
if (s.equalsIgnoreCase(highlightFirst)) {
sb.append("&e").append(s).append("&4").append(reversed ? " <--- " : " ---> ");
sb.append("&b").append(s).append("&4").append(reversed ? " <--- " : " ---> ");
} else if (s.equalsIgnoreCase(highlightSecond)) {
sb.append("&e").append(s).append("&7").append(reversed ? " <--- " : " ---> ");
sb.append("&b").append(s).append("&7").append(reversed ? " <--- " : " ---> ");
} else {
sb.append("&6").append(s).append("&7").append(reversed ? " <--- " : " ---> ");
sb.append("&3").append(s).append("&7").append(reversed ? " <--- " : " ---> ");
}
}
return sb.delete(sb.length() - 6, sb.length()).toString();
@@ -110,39 +138,115 @@ public class Util {
if (strings.isEmpty()) return "&6None";
StringBuilder sb = new StringBuilder();
strings.forEach(s -> sb.append("&6").append(s).append("&e ---> "));
strings.forEach(s -> sb.append("&3").append(s).append("&b ---> "));
return sb.delete(sb.length() - 6, sb.length()).toString();
}
public static String permNodesToString(Map<String, Boolean> nodes) {
if (nodes.isEmpty()) return "&6None";
public static String permNodesToString(SortedSet<LocalizedNode> nodes) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
if (e.getValue()) {
sb.append("&a").append(e.getKey()).append("&7, ");
} else {
sb.append("&c").append(e.getKey()).append("&7, ");
for (Node node : nodes) {
if (node.isTemporary()) {
continue;
}
sb.append("&3> ").append(node.getValue() ? "&a" : "&c");
sb.append(node.getPermission());
if (node.isServerSpecific()) {
sb.append(" &7(&f").append(node.getServer().get()).append("&7)");
}
if (node.isWorldSpecific()) {
sb.append(" &7(&f").append(node.getWorld().get()).append("&7)");
}
sb.append("\n");
}
return sb.delete(sb.length() - 2, sb.length()).toString();
if (sb.length() == 0) {
return "&3None";
}
return sb.toString();
}
public static String tempNodesToString(Map<Map.Entry<String, Boolean>, Long> nodes) {
if (nodes.isEmpty()) return "&6None";
public static String tempNodesToString(SortedSet<LocalizedNode> nodes) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<Map.Entry<String, Boolean>, Long> e : nodes.entrySet()) {
if (e.getKey().getValue()) {
sb.append("&a").append(e.getKey().getKey()).append("&6 - expires in ")
.append(DateUtil.formatDateDiff(e.getValue())).append("\n");
} else {
sb.append("&c").append(e.getKey().getKey()).append("&6 - expires in ")
.append(DateUtil.formatDateDiff(e.getValue())).append("\n");
for (Node node : nodes) {
if (!node.isTemporary()) {
continue;
}
sb.append("&3> ").append(node.getValue() ? "&a" : "&c");
sb.append(node.getPermission());
if (node.isServerSpecific()) {
sb.append(" &7(&f").append(node.getServer().get()).append("&7)");
}
if (node.isWorldSpecific()) {
sb.append(" &7(&f").append(node.getWorld().get()).append("&7)");
}
sb.append("&6 - expires in ").append(DateUtil.formatDateDiff(node.getExpiryUnixTime())).append("\n");
}
if (sb.length() == 0) {
return "&3None";
}
return sb.toString();
}
public static String permGroupsToString(SortedSet<LocalizedNode> nodes) {
StringBuilder sb = new StringBuilder();
for (Node node : nodes) {
if (!node.isGroupNode()) {
continue;
}
if (node.isTemporary()) {
continue;
}
sb.append("&3> &f").append(node.getGroupName());
if (node.isServerSpecific()) {
sb.append(" &7(&f").append(node.getServer().get()).append("&7)");
}
if (node.isWorldSpecific()) {
sb.append(" &7(&f").append(node.getWorld().get()).append("&7)");
}
sb.append("\n");
}
if (sb.length() == 0) {
return "&3None";
}
return sb.toString();
}
public static String tempGroupsToString(SortedSet<LocalizedNode> nodes) {
StringBuilder sb = new StringBuilder();
for (Node node : nodes) {
if (!node.isGroupNode()) {
continue;
}
if (!node.isTemporary()) {
continue;
}
sb.append("&3> &f").append(node.getGroupName());
if (node.isServerSpecific()) {
sb.append(" &7(&f").append(node.getServer().get()).append("&7)");
}
if (node.isWorldSpecific()) {
sb.append(" &7(&f").append(node.getWorld().get()).append("&7)");
}
sb.append("&6 - expires in ").append(DateUtil.formatDateDiff(node.getExpiryUnixTime())).append("\n");
}
if (sb.length() == 0) {
return "&3None";
}
return sb.toString();
@@ -161,4 +265,25 @@ public class Util {
}
}
}
public static synchronized MetaComparator getMetaComparator() {
if (metaComparator == null) {
metaComparator = new MetaComparator();
}
return metaComparator;
}
private static MetaComparator metaComparator = null;
public class MetaComparator implements Comparator<Map.Entry<Integer, String>> {
@Override
public int compare(Map.Entry<Integer, String> o1, Map.Entry<Integer, String> o2) {
int result = Integer.compare(o1.getKey(), o2.getKey());
if (result == 0) {
result = o1.getValue().compareTo(o2.getValue());
}
return result;
}
}
}
@@ -71,7 +71,7 @@ public class DeleteGroup extends SingleMainCommand {
return CommandResult.FAILURE;
}
Message.DELETE_SUCCESS.send(sender, groupName);
Message.DELETE_SUCCESS.send(sender, group.getDisplayName());
LogEntry.build().actor(sender).actedName(groupName).type('G').action("delete").build().submit(plugin, sender);
plugin.runUpdateTask();
return CommandResult.SUCCESS;
@@ -39,6 +39,7 @@ public class GroupMainCommand extends MainCommand<Group> {
super("Group", "/%s group <group>", 2, ImmutableList.<SubCommand<Group>>builder()
.add(new GroupInfo())
.add(new GroupListNodes())
.add(new GroupListParents())
.add(new GroupHasPerm())
.add(new GroupInheritsPerm())
.add(new GroupSetPermission())
@@ -50,8 +51,19 @@ public class GroupMainCommand extends MainCommand<Group> {
.add(new GroupSetTempInherit())
.add(new GroupUnsetTempInherit())
.add(new GroupShowTracks())
.add(new GroupChatMeta())
.add(new GroupAddPrefix())
.add(new GroupAddSuffix())
.add(new GroupRemovePrefix())
.add(new GroupRemoveSuffix())
.add(new GroupAddTempPrefix())
.add(new GroupAddTempSuffix())
.add(new GroupRemoveTempPrefix())
.add(new GroupRemoveTempSuffix())
.add(new GroupBulkChange())
.add(new GroupClear())
.add(new GroupRename())
.add(new GroupClone())
.build()
);
}
@@ -29,9 +29,11 @@ import me.lucko.luckperms.commands.SingleMainCommand;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.groups.Group;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ListGroups extends SingleMainCommand {
public ListGroups() {
@@ -45,7 +47,13 @@ public class ListGroups extends SingleMainCommand {
return CommandResult.LOADING_ERROR;
}
Message.GROUPS_LIST.send(sender, Util.listToCommaSep(new ArrayList<>(plugin.getGroupManager().getAll().keySet())));
Message.GROUPS_LIST.send(
sender,
Util.listToCommaSep(new ArrayList<>(plugin.getGroupManager().getAll().values().stream()
.map(Group::getDisplayName)
.collect(Collectors.toList()))
)
);
return CommandResult.SUCCESS;
}
}
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupAddPrefix extends SubCommand<Group> {
public GroupAddPrefix() {
super("addprefix", "Adds a prefix to the group", Permission.GROUP_ADDPREFIX, Predicate.notInRange(2, 4),
Arg.list(
Arg.create("priority", true, "the priority to add the prefix at"),
Arg.create("prefix", true, "the prefix string"),
Arg.create("server", false, "the server to add the prefix on"),
Arg.create("world", false, "the world to add the prefix on")
)
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.setPermission(node, true, server);
Message.ADDPREFIX_SERVER_SUCCESS.send(sender, group.getDisplayName(), prefix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("addprefix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.setPermission(node, true, server, world);
Message.ADDPREFIX_SERVER_WORLD_SUCCESS.send(sender, group.getDisplayName(), prefix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("addprefix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.setPermission(node, true);
Message.ADDPREFIX_SUCCESS.send(sender, group.getDisplayName(), prefix, priority);
LogEntry.build().actor(sender).acted(group)
.action("addprefix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_PREFIX.send(sender, group.getDisplayName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupAddSuffix extends SubCommand<Group> {
public GroupAddSuffix() {
super("addsuffix", "Adds a suffix to the group", Permission.GROUP_ADDSUFFIX, Predicate.notInRange(2, 4),
Arg.list(
Arg.create("priority", true, "the priority to add the suffix at"),
Arg.create("suffix", true, "the suffix string"),
Arg.create("server", false, "the server to add the suffix on"),
Arg.create("world", false, "the world to add the suffix on")
)
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.setPermission(node, true, server);
Message.ADDSUFFIX_SERVER_SUCCESS.send(sender, group.getDisplayName(), suffix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("addsuffix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.setPermission(node, true, server, world);
Message.ADDSUFFIX_SERVER_WORLD_SUCCESS.send(sender, group.getDisplayName(), suffix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("addsuffix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.setPermission(node, true);
Message.ADDSUFFIX_SUCCESS.send(sender, group.getDisplayName(), suffix, priority);
LogEntry.build().actor(sender).acted(group)
.action("addsuffix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_SUFFIX.send(sender, group.getDisplayName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import me.lucko.luckperms.utils.DateUtil;
import java.util.List;
public class GroupAddTempPrefix extends SubCommand<Group> {
public GroupAddTempPrefix() {
super("addtempprefix", "Adds a prefix to the group temporarily", Permission.GROUP_ADD_TEMP_PREFIX, Predicate.notInRange(3, 5),
Arg.list(
Arg.create("priority", true, "the priority to add the prefix at"),
Arg.create("prefix", true, "the prefix string"),
Arg.create("duration", true, "the duration until the prefix expires"),
Arg.create("server", false, "the server to add the prefix on"),
Arg.create("world", false, "the world to add the prefix on")
)
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
long duration;
try {
duration = Long.parseLong(args.get(2));
} catch (NumberFormatException e) {
try {
duration = DateUtil.parseDateDiff(args.get(2), true);
} catch (DateUtil.IllegalDateException e1) {
Message.ILLEGAL_DATE_ERROR.send(sender, args.get(2));
return CommandResult.INVALID_ARGS;
}
}
if (DateUtil.shouldExpire(duration)) {
Message.PAST_DATE_ERROR.send(sender);
return CommandResult.INVALID_ARGS;
}
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 4) {
final String server = args.get(3).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 4) {
group.setPermission(node, true, server, duration);
Message.ADD_TEMP_PREFIX_SERVER_SUCCESS.send(sender, group.getDisplayName(), prefix, priority, server, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(4).toLowerCase();
group.setPermission(node, true, server, world, duration);
Message.ADD_TEMP_PREFIX_SERVER_WORLD_SUCCESS.send(sender, group.getDisplayName(), prefix, priority, server, world, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.setPermission(node, true, duration);
Message.ADD_TEMP_PREFIX_SUCCESS.send(sender, group.getDisplayName(), prefix, priority, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempprefix " + priority + " " + args.get(1) + " " + duration)
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_PREFIX.send(sender, group.getDisplayName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import me.lucko.luckperms.utils.DateUtil;
import java.util.List;
public class GroupAddTempSuffix extends SubCommand<Group> {
public GroupAddTempSuffix() {
super("addtempsuffix", "Adds a suffix to the group temporarily", Permission.GROUP_ADD_TEMP_SUFFIX, Predicate.notInRange(3, 5),
Arg.list(
Arg.create("priority", true, "the priority to add the suffix at"),
Arg.create("suffix", true, "the suffix string"),
Arg.create("duration", true, "the duration until the suffix expires"),
Arg.create("server", false, "the server to add the suffix on"),
Arg.create("world", false, "the world to add the suffix on")
)
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String suffix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
long duration;
try {
duration = Long.parseLong(args.get(2));
} catch (NumberFormatException e) {
try {
duration = DateUtil.parseDateDiff(args.get(2), true);
} catch (DateUtil.IllegalDateException e1) {
Message.ILLEGAL_DATE_ERROR.send(sender, args.get(2));
return CommandResult.INVALID_ARGS;
}
}
if (DateUtil.shouldExpire(duration)) {
Message.PAST_DATE_ERROR.send(sender);
return CommandResult.INVALID_ARGS;
}
final String node = "suffix." + priority + "." + ArgumentChecker.escapeCharacters(suffix);
try {
if (args.size() >= 4) {
final String server = args.get(3).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 4) {
group.setPermission(node, true, server, duration);
Message.ADD_TEMP_SUFFIX_SERVER_SUCCESS.send(sender, group.getDisplayName(), suffix, priority, server, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(4).toLowerCase();
group.setPermission(node, true, server, world, duration);
Message.ADD_TEMP_SUFFIX_SERVER_WORLD_SUCCESS.send(sender, group.getDisplayName(), suffix, priority, server, world, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.setPermission(node, true, duration);
Message.ADD_TEMP_SUFFIX_SUCCESS.send(sender, group.getDisplayName(), suffix, priority, DateUtil.formatDateDiff(duration));
LogEntry.build().actor(sender).acted(group)
.action("addtempsuffix " + priority + " " + args.get(1) + " " + duration)
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectAlreadyHasException e) {
Message.ALREADY_HAS_SUFFIX.send(sender, group.getDisplayName());
return CommandResult.STATE_ERROR;
}
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class GroupBulkChange extends SubCommand<Group> {
public GroupBulkChange() {
super("bulkchange", "Applies a bulk permission change to the group's permissions", Permission.GROUP_BULKCHANGE, Predicate.not(3),
Arg.list(
Arg.create("server|world", true, "if the bulk change is modifying a 'server' or a 'world'"),
Arg.create("from", true, "the server/world to be changed from. can be 'global' or 'null' respectively"),
Arg.create("to", true, "the server/world to replace 'from' (can be 'null')")
)
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
String type = args.get(0).toLowerCase();
String from = args.get(1);
String to = args.get(2);
if (to.equals("null")) {
to = null;
}
Set<Node> toAdd = new HashSet<>();
Set<Node> toRemove = new HashSet<>();
if (!type.equals("world") && !type.equals("server")) {
Message.BULK_CHANGE_TYPE_ERROR.send(sender);
return CommandResult.FAILURE;
}
Iterator<Node> iterator = group.getNodes().iterator();
if (type.equals("world")) {
while (iterator.hasNext()) {
Node element = iterator.next();
String world = element.getWorld().orElse("null");
if (!world.equals(from)) {
continue;
}
toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setWorld(to).build());
}
} else {
while (iterator.hasNext()) {
Node element = iterator.next();
String server = element.getServer().orElse("global");
if (!server.equals(from)) {
continue;
}
toRemove.add(element);
toAdd.add(me.lucko.luckperms.core.Node.builderFromExisting(element).setServer(to).build());
}
}
toRemove.forEach(n -> {
try {
group.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
toAdd.forEach(n -> {
try {
group.setPermission(n);
} catch (ObjectAlreadyHasException ignored) {}
});
save(group, sender, plugin);
Message.BULK_CHANGE_SUCCESS.send(sender, toAdd.size());
return CommandResult.SUCCESS;
}
}
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.groups.Group;
import java.util.*;
public class GroupChatMeta extends SubCommand<Group> {
public GroupChatMeta() {
super("chatmeta", "Lists the group's chat meta", Permission.GROUP_CHATMETA, Predicate.alwaysFalse(), null);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
SortedSet<Map.Entry<Integer, String>> prefixes = new TreeSet<>(Util.getMetaComparator().reversed());
SortedSet<Map.Entry<Integer, String>> suffixes = new TreeSet<>(Util.getMetaComparator().reversed());
for (Node node : group.getAllNodes(null, Contexts.allowAll())) {
if (!node.isSuffix() && !node.isPrefix()) {
continue;
}
StringBuilder sb = new StringBuilder();
if (node.isServerSpecific()) {
if (node.isWorldSpecific()) {
sb.append("&6W=").append(node.getWorld().get()).append(" ");
}
sb.append("&6S=").append(node.getServer().get()).append(" ");
}
if (node.isPrefix()) {
sb.append(node.getPrefix().getValue());
prefixes.add(new AbstractMap.SimpleEntry<>(node.getPrefix().getKey(), sb.toString()));
}
if (node.isSuffix()) {
sb.append(node.getSuffix().getValue());
suffixes.add(new AbstractMap.SimpleEntry<>(node.getSuffix().getKey(), sb.toString()));
}
}
if (prefixes.isEmpty()) {
Message.CHAT_META_PREFIX_NONE.send(sender, group.getDisplayName());
} else {
Message.CHAT_META_PREFIX_HEADER.send(sender, group.getDisplayName());
for (Map.Entry<Integer, String> e : prefixes) {
Message.CHAT_META_ENTRY.send(sender, e.getKey(), e.getValue());
}
}
if (suffixes.isEmpty()) {
Message.CHAT_META_SUFFIX_NONE.send(sender, group.getDisplayName());
} else {
Message.CHAT_META_SUFFIX_HEADER.send(sender, group.getDisplayName());
for (Map.Entry<Integer, String> e : suffixes) {
Message.CHAT_META_ENTRY.send(sender, e.getKey(), e.getValue());
}
}
return CommandResult.SUCCESS;
}
}
@@ -36,14 +36,13 @@ import java.util.List;
public class GroupClear extends SubCommand<Group> {
public GroupClear() {
super("clear", "Clears a groups permissions", "/%s group <group> clear", Permission.GROUP_CLEAR,
Predicate.alwaysFalse());
super("clear", "Clears the group's permissions", Permission.GROUP_CLEAR, Predicate.alwaysFalse(), null);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
group.clearNodes();
Message.CLEAR_SUCCESS.send(sender, group.getName());
Message.CLEAR_SUCCESS.send(sender, group.getDisplayName());
LogEntry.build().actor(sender).acted(group).action("clear").build().submit(plugin, sender);
save(group, sender, plugin);
return CommandResult.SUCCESS;
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.List;
public class GroupClone extends SubCommand<Group> {
public GroupClone() {
super("clone", "Clone the group", Permission.GROUP_CLONE, Predicate.not(1),
Arg.list(Arg.create("name", true, "the name of the clone"))
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
String newGroupName = args.get(0).toLowerCase();
if (ArgumentChecker.checkName(newGroupName)) {
Message.GROUP_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (plugin.getDatastore().loadGroup(newGroupName)) {
Message.GROUP_ALREADY_EXISTS.send(sender);
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().createAndLoadGroup(newGroupName)) {
Message.CREATE_GROUP_ERROR.send(sender);
return CommandResult.FAILURE;
}
Group newGroup = plugin.getGroupManager().get(newGroupName);
if (newGroup == null) {
Message.GROUP_LOAD_ERROR.send(sender);
return CommandResult.LOADING_ERROR;
}
newGroup.setNodes(group.getNodes());
Message.CLONE_SUCCESS.send(sender, group.getName(), newGroup.getName());
LogEntry.build().actor(sender).acted(group).action("clone " + newGroup.getName()).build().submit(plugin, sender);
save(newGroup, sender, plugin);
return CommandResult.SUCCESS;
}
}
@@ -26,6 +26,7 @@ import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.Node;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
@@ -33,9 +34,14 @@ import java.util.List;
public class GroupHasPerm extends SubCommand<Group> {
public GroupHasPerm() {
super("haspermission", "Checks to see if a group has a certain permission node",
"/%s group <group> haspermission <node> [server] [world]", Permission.GROUP_HASPERMISSION,
Predicate.notInRange(1, 3));
super("haspermission", "Checks to see if the group has a certain permission node", Permission.GROUP_HASPERMISSION,
Predicate.notInRange(1, 3),
Arg.list(
Arg.create("node", true, "the permission node to check for"),
Arg.create("server", false, "the server to check on"),
Arg.create("world", false, "the world to check on")
)
);
}
@Override
@@ -47,13 +53,13 @@ public class GroupHasPerm extends SubCommand<Group> {
}
if (args.size() == 2) {
Util.sendBoolean(sender, args.get(0), group.hasPermission(args.get(0), true, args.get(1)));
Util.sendTristate(sender, args.get(0), group.hasPermission(new Node.Builder(args.get(0)).setServer(args.get(1)).build()));
} else {
Util.sendBoolean(sender, args.get(0), group.hasPermission(args.get(0), true, args.get(1), args.get(2)));
Util.sendTristate(sender, args.get(0), group.hasPermission(new Node.Builder(args.get(0)).setServer(args.get(1)).setWorld(args.get(2)).build()));
}
} else {
Util.sendBoolean(sender, args.get(0), group.hasPermission(args.get(0), true, "global"));
Util.sendTristate(sender, args.get(0), group.hasPermission(new Node.Builder(args.get(0)).build()));
}
return CommandResult.SUCCESS;
}
@@ -35,14 +35,13 @@ import java.util.List;
public class GroupInfo extends SubCommand<Group> {
public GroupInfo() {
super("info", "Gives info about the group", "/%s group <group> info", Permission.GROUP_INFO,
Predicate.alwaysFalse());
super("info", "Gives info about the group", Permission.GROUP_INFO, Predicate.alwaysFalse(), null);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
Message.GROUP_INFO.send(sender,
group.getName(),
group.getDisplayName(),
group.getPermanentNodes().size(),
group.getTemporaryNodes().size(),
label,
@@ -26,6 +26,8 @@ import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.core.InheritanceInfo;
import me.lucko.luckperms.core.Node;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
@@ -33,13 +35,19 @@ import java.util.List;
public class GroupInheritsPerm extends SubCommand<Group> {
public GroupInheritsPerm() {
super("inheritspermission", "Checks to see if a group inherits a certain permission node",
"/%s group <group> inheritspermission <node> [server] [world]", Permission.GROUP_INHERITSPERMISSION,
Predicate.notInRange(1, 3));
super("inheritspermission", "Checks to see if the group inherits a certain permission node",
Permission.GROUP_INHERITSPERMISSION, Predicate.notInRange(1, 3),
Arg.list(
Arg.create("node", true, "the permission node to check for"),
Arg.create("server", false, "the server to check on"),
Arg.create("world", false, "the world to check on")
)
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
InheritanceInfo result;
if (args.size() >= 2) {
if (ArgumentChecker.checkServer(args.get(1))) {
Message.SERVER_INVALID_ENTRY.send(sender);
@@ -47,14 +55,26 @@ public class GroupInheritsPerm extends SubCommand<Group> {
}
if (args.size() == 2) {
Util.sendBoolean(sender, args.get(0), group.inheritsPermission(args.get(0), true, args.get(1)));
result = group.inheritsPermissionInfo(new Node.Builder(args.get(0)).setServer(args.get(1)).build());
} else {
Util.sendBoolean(sender, args.get(0), group.inheritsPermission(args.get(0), true, args.get(1), args.get(2)));
result = group.inheritsPermissionInfo(new Node.Builder(args.get(0)).setServer(args.get(1)).setWorld(args.get(2)).build());
}
} else {
Util.sendBoolean(sender, args.get(0), group.inheritsPermission(args.get(0), true));
result = group.inheritsPermissionInfo(new Node.Builder(args.get(0)).build());
}
String location = null;
if (result.getLocation().isPresent()) {
if (result.getLocation().get().equals(group.getObjectName())) {
location = "self";
} else {
location = result.getLocation().get();
}
}
Util.sendPluginMessage(sender, "&b" + args.get(0) + ": " + Util.formatTristate(result.getResult()) +
(result.getLocation().isPresent() ? " &7(inherited from &a" + location + "&7)" : ""));
return CommandResult.SUCCESS;
}
}
@@ -30,18 +30,15 @@ import me.lucko.luckperms.groups.Group;
import java.util.List;
import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
public class GroupListNodes extends SubCommand<Group> {
public GroupListNodes() {
super("listnodes", "Lists the permission nodes the group has", "/%s group <group> listnodes",
Permission.GROUP_LISTNODES, Predicate.alwaysFalse());
super("listnodes", "Lists the permission nodes the group has", Permission.GROUP_LISTNODES, Predicate.alwaysFalse(), null);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
Message.LISTNODES.send(sender, group.getName(), Util.permNodesToString(exportToLegacy(group.getPermanentNodes())));
Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getTemporaryNodesLegacy()));
Message.LISTNODES.send(sender, group.getDisplayName(), Util.permNodesToString(group.getPermissions(false)));
Message.LISTNODES_TEMP.send(sender, group.getDisplayName(), Util.tempNodesToString(group.getPermissions(false)));
return CommandResult.SUCCESS;
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.groups.Group;
import java.util.List;
public class GroupListParents extends SubCommand<Group> {
public GroupListParents() {
super("listparents", "Lists the groups that this group inherits from", Permission.GROUP_LISTPARENTS, Predicate.alwaysFalse(), null);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
Message.LISTPARENTS.send(sender, group.getDisplayName(), Util.permGroupsToString(group.getPermissions(false)));
Message.LISTPARENTS_TEMP.send(sender, group.getDisplayName(), Util.tempGroupsToString(group.getPermissions(false)));
return CommandResult.SUCCESS;
}
}
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.commands.group.subcommands;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.commands.*;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.constants.Permission;
import me.lucko.luckperms.data.LogEntry;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.utils.ArgumentChecker;
import java.util.ArrayList;
import java.util.List;
public class GroupRemovePrefix extends SubCommand<Group> {
public GroupRemovePrefix() {
super("removeprefix", "Removes a prefix from the group", Permission.GROUP_REMOVEPREFIX, Predicate.notInRange(2, 4),
Arg.list(
Arg.create("priority", true, "the priority to remove the prefix at"),
Arg.create("prefix", true, "the prefix string to remove"),
Arg.create("server", false, "the server to remove the prefix on"),
Arg.create("world", false, "the world to remove the prefix on")
)
);
}
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
final String prefix = args.get(1).replace("{SPACE}", " ");
int priority;
try {
priority = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
Message.META_INVALID_PRIORITY.send(sender, args.get(0));
return CommandResult.INVALID_ARGS;
}
if (prefix.equalsIgnoreCase("null")) {
String server = null;
String world = null;
if (args.size() >= 3) {
server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() != 3) {
world = args.get(3).toLowerCase();
}
}
List<Node> toRemove = new ArrayList<>();
for (Node node : group.getNodes()) {
if (!node.isPrefix()) continue;
if (node.getPrefix().getKey() != priority) continue;
if (node.isTemporary()) continue;
if (node.getServer().isPresent()) {
if (server == null) continue;
if (!node.getServer().get().equalsIgnoreCase(server)) continue;
} else {
if (server != null) continue;
}
if (node.getWorld().isPresent()) {
if (world == null) continue;
if (!node.getWorld().get().equalsIgnoreCase(world)) continue;
} else {
if (world != null) continue;
}
toRemove.add(node);
}
toRemove.forEach(n -> {
try {
group.unsetPermission(n);
} catch (ObjectLacksException ignored) {}
});
Message.BULK_CHANGE_SUCCESS.send(sender, toRemove.size());
save(group, sender, plugin);
return CommandResult.SUCCESS;
} else {
final String node = "prefix." + priority + "." + ArgumentChecker.escapeCharacters(prefix);
try {
if (args.size() >= 3) {
final String server = args.get(2).toLowerCase();
if (ArgumentChecker.checkServer(server)) {
Message.SERVER_INVALID_ENTRY.send(sender);
return CommandResult.INVALID_ARGS;
}
if (args.size() == 3) {
group.unsetPermission(node, server);
Message.REMOVEPREFIX_SERVER_SUCCESS.send(sender, group.getDisplayName(), prefix, priority, server);
LogEntry.build().actor(sender).acted(group)
.action("removeprefix " + priority + " " + args.get(1) + " " + server)
.build().submit(plugin, sender);
} else {
final String world = args.get(3).toLowerCase();
group.unsetPermission(node, server, world);
Message.REMOVEPREFIX_SERVER_WORLD_SUCCESS.send(sender, group.getDisplayName(), prefix, priority, server, world);
LogEntry.build().actor(sender).acted(group)
.action("removeprefix " + priority + " " + args.get(1) + " " + server + " " + world)
.build().submit(plugin, sender);
}
} else {
group.unsetPermission(node);
Message.REMOVEPREFIX_SUCCESS.send(sender, group.getDisplayName(), prefix, priority);
LogEntry.build().actor(sender).acted(group)
.action("removeprefix " + priority + " " + args.get(1))
.build().submit(plugin, sender);
}
save(group, sender, plugin);
return CommandResult.SUCCESS;
} catch (ObjectLacksException e) {
Message.DOES_NOT_HAVE_PREFIX.send(sender, group.getDisplayName());
return CommandResult.STATE_ERROR;
}
}
}
}
Loaded 100 of 242 files, more files were not shown because too many files have changed in this diff. Show more