diff --git a/api/pom.xml b/api/pom.xml
index a44da7cd8..c2b8c3cef 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 2.5-SNAPSHOT
+ 2.6-SNAPSHOT
4.0.0
diff --git a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java
index ceb4a9cc9..ebde682bd 100644
--- a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java
+++ b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java
@@ -43,12 +43,16 @@ public interface LPConfiguration {
/**
* @return the default group, in a node representation
+ * @deprecated as of 1.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
String getDefaultGroupName();
/**
diff --git a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java
index b29199b35..4b4cd162f 100644
--- a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java
+++ b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java
@@ -175,4 +175,12 @@ public interface LuckPermsApi {
*/
boolean isTrackLoaded(String name);
+ /**
+ * Returns a permission builder instance
+ * @param permission the main permission node to build
+ * @return a {@link Node.Builder} instance
+ * @throws IllegalArgumentException if the permission is invalid
+ */
+ Node.Builder buildNode(String permission) throws IllegalArgumentException;
+
}
diff --git a/api/src/main/java/me/lucko/luckperms/api/Node.java b/api/src/main/java/me/lucko/luckperms/api/Node.java
new file mode 100644
index 000000000..c7cfd3053
--- /dev/null
+++ b/api/src/main/java/me/lucko/luckperms/api/Node.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.api;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Represents an immutable node object
+ */
+public interface Node extends Map.Entry {
+
+ /**
+ * @return the actual permission node
+ */
+ String getPermission();
+
+ /**
+ * Get what value the permission is set to. A negated node would return false
.
+ * @return the permission's value
+ */
+ @Override
+ Boolean getValue();
+
+ /**
+ * @return true if the node is negated
+ */
+ boolean isNegated();
+
+ /**
+ * Gets the server this node applies on, if the node is server specific
+ * @return an {@link Optional} containing the server, if one is defined
+ */
+ Optional getServer();
+
+ /**
+ * Gets the world this node applies on, if the node is world specific
+ * @return an {@link Optional} containing the world, if one is defined
+ */
+ Optional getWorld();
+
+ /**
+ * @return true if this node is server specific
+ */
+ boolean isServerSpecific();
+
+ /**
+ * @return true if this node is server specific
+ */
+ boolean isWorldSpecific();
+
+ /**
+ * If this node should apply on a specific server
+ * @param server the name of the server
+ * @param includeGlobal if global permissions should apply
+ * @param applyRegex if regex should be applied
+ * @return true if the node should apply
+ */
+ boolean shouldApplyOnServer(String server, boolean includeGlobal, boolean applyRegex);
+
+ /**
+ * If this node should apply on a specific world
+ * @param world the name of the world
+ * @param includeGlobal if global permissions should apply
+ * @param applyRegex if regex should be applied
+ * @return true if the node should apply
+ */
+ boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex);
+
+ /**
+ * If this node should apply given the specific context
+ * @param context the context key value pairs
+ * @return true if the node should apply
+ */
+ boolean shouldApplyWithContext(Map context);
+
+ /**
+ * Similar to {@link #shouldApplyOnServer(String, boolean, boolean)}, except this method accepts a List
+ * @param servers the list of servers
+ * @param includeGlobal if global permissions should apply
+ * @return true if the node should apply
+ */
+ boolean shouldApplyOnAnyServers(List servers, boolean includeGlobal);
+
+ /**
+ * Similar to {@link #shouldApplyOnWorld(String, boolean, boolean)}, except this method accepts a List
+ * @param worlds the list of world
+ * @param includeGlobal if global permissions should apply
+ * @return true if the node should apply
+ */
+ boolean shouldApplyOnAnyWorlds(List worlds, boolean includeGlobal);
+
+ /**
+ * Resolves a list of wildcards that match this node
+ * @param possibleNodes a list of possible permission nodes
+ * @return a list of permissions that match this wildcard
+ */
+ List resolveWildcard(List possibleNodes);
+
+ /**
+ * Resolves any shorthand parts of this node and returns the full list
+ * @return a list of full nodes
+ */
+ List resolveShorthand();
+
+ /**
+ * @return true if this node will expire in the future
+ */
+ boolean isTemporary();
+
+ /**
+ * @return true if this node will not expire
+ */
+ boolean isPermanent();
+
+ /**
+ * @return the time in Unix time when this node will expire
+ */
+ long getExpiryUnixTime();
+
+ /**
+ * @return the {@link Date} when this node will expire
+ */
+ Date getExpiry();
+
+ /**
+ * @return the number of seconds until this permission will expire
+ */
+ long getSecondsTilExpiry();
+
+ /**
+ * @return true if this node has expired
+ */
+ boolean hasExpired();
+
+ /**
+ * @return the extra contexts required for this node to apply
+ */
+ Map getExtraContexts();
+
+ /**
+ * Converts this node into a serialized form
+ * @return a serialized node string
+ */
+ String toSerializedNode();
+
+ /**
+ * @return true if this is a group node
+ */
+ boolean isGroupNode();
+
+ /**
+ * @return the name of the group
+ * @throws IllegalStateException if this is not a group node. See {@link #isGroupNode()}
+ */
+ String getGroupName();
+
+ /**
+ * @return true is this node is a wildcard node
+ */
+ boolean isWildcard();
+
+ /**
+ * Gets the level of this wildcard, higher is more specific
+ * @return the wildcard level
+ * @throws IllegalStateException if this is not a wildcard
+ */
+ int getWildcardLevel();
+
+ /**
+ * Similar to {@link #equals(Object)}, except doesn't take note of the expiry time or value
+ * @param node the other node
+ * @return true if the two nodes are almost equal
+ */
+ boolean almostEquals(Node node);
+
+ interface Builder {
+ Builder setNegated(boolean negated);
+ Builder setValue(boolean value);
+ Builder setExpiry(long expireAt);
+ Builder setWorld(String world);
+ Builder setServer(String server) throws IllegalArgumentException;
+ Builder withExtraContext(String key, String value);
+ Node build();
+ }
+
+}
diff --git a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java
index d5617a240..7ec60189b 100644
--- a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java
+++ b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java
@@ -27,33 +27,61 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
- * Wrapper interface for internal PermissionHolder (user/group) instances
+ * Wrapper interface for internal PermissionHolder (object/group) instances
*/
@SuppressWarnings("unused")
public interface PermissionHolder {
/**
* @return the identifier for this object. either a uuid string or name
- * However, you should really use {@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()}
+ * However, you should really just use {@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()}
*/
String getObjectName();
+ /**
+ * Gets an immutable Set of the objects permission nodes
+ * @return an immutable set of permissions
+ * @since 1.6
+ */
+ Set getPermissionNodes();
+
+ /**
+ * Gets an immutable set of the nodes that this object has and inherits
+ * @return an immutable set of permissions
+ * @since 1.6
+ */
+ Set getAllNodes();
+
/**
* Gets an immutable Map of the objects permission nodes
* @return an immutable map of permissions
+ * @deprecated in favour of {@link #getPermissionNodes()}
*/
+ @Deprecated
Map getNodes();
+ /**
+ * Checks to see if the object has a certain permission
+ * @param node the node to check for
+ * @return true if the object has the permission
+ * @throws NullPointerException if the node is null
+ * @since 1.6
+ */
+ boolean hasPermission(Node node);
+
/**
* Checks to see if the object has a certain permission
* @param node The permission node
* @param b If the node is true/false(negated)
- * @return true if the user has the permission
+ * @return true if the object has the permission
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
+ * @deprecated in favour of {@link #hasPermission(Node)}
*/
+ @Deprecated
boolean hasPermission(String node, boolean b);
/**
@@ -61,10 +89,12 @@ public interface PermissionHolder {
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
- * @return true if the user has the permission
+ * @return true if the object has the permission
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
+ * @deprecated in favour of {@link #hasPermission(Node)}
*/
+ @Deprecated
boolean hasPermission(String node, boolean b, String server);
/**
@@ -73,10 +103,12 @@ public interface PermissionHolder {
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
- * @return true if the user has the permission
+ * @return true if the object has the permission
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
+ * @deprecated in favour of {@link #hasPermission(Node)}
*/
+ @Deprecated
boolean hasPermission(String node, boolean b, String server, String world);
/**
@@ -84,10 +116,12 @@ public interface PermissionHolder {
* @param node The permission node
* @param b If the node is true/false(negated)
* @param temporary if the permission is temporary
- * @return true if the user has the permission
+ * @return true if the object has the permission
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
+ * @deprecated in favour of {@link #hasPermission(Node)}
*/
+ @Deprecated
boolean hasPermission(String node, boolean b, boolean temporary);
/**
@@ -96,10 +130,12 @@ public interface PermissionHolder {
* @param b If the node is true/false(negated)
* @param server The server to check on
* @param temporary if the permission is temporary
- * @return true if the user has the permission
+ * @return true if the object has the permission
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
+ * @deprecated in favour of {@link #hasPermission(Node)}
*/
+ @Deprecated
boolean hasPermission(String node, boolean b, String server, boolean temporary);
/**
@@ -109,20 +145,33 @@ public interface PermissionHolder {
* @param server The server to check on
* @param world The world to check on
* @param temporary if the permission is temporary
- * @return true if the user has the permission
+ * @return true if the object has the permission
* @throws NullPointerException if the node, server or world is null
- * @throws IllegalArgumentException if the node, server or world is invalud
+ * @throws IllegalArgumentException if the node, server or world is invalid
+ * @deprecated in favour of {@link #hasPermission(Node)}
*/
+ @Deprecated
boolean hasPermission(String node, boolean b, String server, String world, boolean temporary);
+ /**
+ * Cheks to see if the object inherits a certain permission
+ * @param node the node to check for
+ * @return true if the object inherits the permission
+ * @throws NullPointerException if the node is null
+ * @since 1.6
+ */
+ boolean inheritsPermission(Node node);
+
/**
* Checks to see if the object inherits a certain permission
* @param node The permission node
* @param b If the node is true/false(negated)
- * @return true if the user inherits the permission
+ * @return true if the object inherits the permission
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
+ * @deprecated in favour of {@link #inheritsPermission(Node)}
*/
+ @Deprecated
boolean inheritsPermission(String node, boolean b);
/**
@@ -130,10 +179,12 @@ public interface PermissionHolder {
* @param node The permission node
* @param b If the node is true/false(negated)
* @param server The server
- * @return true if the user inherits the permission
+ * @return true if the object inherits the permission
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
+ * @deprecated in favour of {@link #inheritsPermission(Node)}
*/
+ @Deprecated
boolean inheritsPermission(String node, boolean b, String server);
/**
@@ -142,10 +193,12 @@ public interface PermissionHolder {
* @param b If the node is true/false(negated)
* @param server The server
* @param world The world
- * @return true if the user inherits the permission
+ * @return true if the object inherits the permission
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node server or world is invalid
+ * @deprecated in favour of {@link #inheritsPermission(Node)}
*/
+ @Deprecated
boolean inheritsPermission(String node, boolean b, String server, String world);
/**
@@ -153,10 +206,12 @@ public interface PermissionHolder {
* @param node The permission node
* @param b If the node is true/false(negated)
* @param temporary if the permission is temporary
- * @return true if the user inherits the permission
+ * @return true if the object inherits the permission
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
+ * @deprecated in favour of {@link #inheritsPermission(Node)}
*/
+ @Deprecated
boolean inheritsPermission(String node, boolean b, boolean temporary);
/**
@@ -165,10 +220,12 @@ public interface PermissionHolder {
* @param b If the node is true/false(negated)
* @param server The server
* @param temporary if the permission is temporary
- * @return true if the user inherits the permission
+ * @return true if the object inherits the permission
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
+ * @deprecated in favour of {@link #inheritsPermission(Node)}
*/
+ @Deprecated
boolean inheritsPermission(String node, boolean b, String server, boolean temporary);
/**
@@ -178,12 +235,23 @@ public interface PermissionHolder {
* @param server The server
* @param world The world
* @param temporary if the permission is temporary
- * @return true if the user inherits the permission
+ * @return true if the object inherits the permission
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world if invalid
+ * @deprecated in favour of {@link #inheritsPermission(Node)}
*/
+ @Deprecated
boolean inheritsPermission(String node, boolean b, String server, String world, boolean temporary);
+ /**
+ * Sets a permission for the object
+ * @param node The node to be set
+ * @throws ObjectAlreadyHasException if the object already has the permission
+ * @throws NullPointerException if the node is null
+ * @since 1.6
+ */
+ void setPermission(Node node) throws ObjectAlreadyHasException;
+
/**
* Sets a permission for the object
* @param node The node to be set
@@ -191,7 +259,9 @@ public interface PermissionHolder {
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
+ * @deprecated in favour of {@link #setPermission(Node)}
*/
+ @Deprecated
void setPermission(String node, boolean value) throws ObjectAlreadyHasException;
/**
@@ -202,7 +272,9 @@ public interface PermissionHolder {
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
+ * @deprecated in favour of {@link #setPermission(Node)}
*/
+ @Deprecated
void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException;
/**
@@ -214,7 +286,9 @@ public interface PermissionHolder {
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
+ * @deprecated in favour of {@link #setPermission(Node)}
*/
+ @Deprecated
void setPermission(String node, boolean value, String server, String world) throws ObjectAlreadyHasException;
/**
@@ -225,7 +299,9 @@ public interface PermissionHolder {
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid or if the expiry time is in the past
+ * @deprecated in favour of {@link #setPermission(Node)}
*/
+ @Deprecated
void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException;
/**
@@ -237,7 +313,9 @@ public interface PermissionHolder {
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node/server is invalid or if the expiry time is in the past
+ * @deprecated in favour of {@link #setPermission(Node)}
*/
+ @Deprecated
void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException;
/**
@@ -250,9 +328,20 @@ public interface PermissionHolder {
* @throws ObjectAlreadyHasException if the object already has the permission
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node/server/world is invalid, or if the expiry time is in the past
+ * @deprecated in favour of {@link #setPermission(Node)}
*/
+ @Deprecated
void setPermission(String node, boolean value, String server, String world, long expireAt) throws ObjectAlreadyHasException;
+ /**
+ * Unsets a permission for the object
+ * @param node The node to be unset
+ * @throws ObjectLacksException if the node wasn't already set
+ * @throws NullPointerException if the node is null
+ * @since 1.6
+ */
+ void unsetPermission(Node node) throws ObjectLacksException;
+
/**
* Unsets a permission for the object
* @param node The node to be unset
@@ -260,7 +349,9 @@ public interface PermissionHolder {
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
+ * @deprecated in favour of {@link #unsetPermission(Node)}
*/
+ @Deprecated
void unsetPermission(String node, boolean temporary) throws ObjectLacksException;
/**
@@ -269,7 +360,9 @@ public interface PermissionHolder {
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node is null
* @throws IllegalArgumentException if the node is invalid
+ * @deprecated in favour of {@link #unsetPermission(Node)}
*/
+ @Deprecated
void unsetPermission(String node) throws ObjectLacksException;
/**
@@ -279,7 +372,9 @@ public interface PermissionHolder {
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
+ * @deprecated in favour of {@link #unsetPermission(Node)}
*/
+ @Deprecated
void unsetPermission(String node, String server) throws ObjectLacksException;
/**
@@ -290,7 +385,9 @@ public interface PermissionHolder {
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
+ * @deprecated in favour of {@link #unsetPermission(Node)}
*/
+ @Deprecated
void unsetPermission(String node, String server, String world) throws ObjectLacksException;
/**
@@ -301,7 +398,9 @@ public interface PermissionHolder {
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node or server is null
* @throws IllegalArgumentException if the node or server is invalid
+ * @deprecated in favour of {@link #unsetPermission(Node)}
*/
+ @Deprecated
void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException;
/**
@@ -313,7 +412,9 @@ public interface PermissionHolder {
* @throws ObjectLacksException if the node wasn't already set
* @throws NullPointerException if the node, server or world is null
* @throws IllegalArgumentException if the node, server or world is invalid
+ * @deprecated in favour of {@link #unsetPermission(Node)}
*/
+ @Deprecated
void unsetPermission(String node, String server, String world, boolean temporary) throws ObjectLacksException;
/**
@@ -323,7 +424,9 @@ public interface PermissionHolder {
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @param possibleNodes A list of possible permission nodes for wildcard permission handling
* @return a {@link Map} of the permissions
+ * @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)}
*/
+ @Deprecated
Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes);
/**
@@ -332,7 +435,9 @@ public interface PermissionHolder {
* @param world The world to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @return a {@link Map} of the permissions
+ * @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)}
*/
+ @Deprecated
Map getLocalPermissions(String server, String world, List excludedGroups);
/**
@@ -341,7 +446,9 @@ public interface PermissionHolder {
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @param possibleNodes A list of possible permission nodes for wildcard permission handling
* @return a {@link Map} of the permissions
+ * @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)}
*/
+ @Deprecated
Map getLocalPermissions(String server, List excludedGroups, List possibleNodes);
/**
@@ -349,21 +456,54 @@ public interface PermissionHolder {
* @param server The server to get nodes for
* @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
* @return a {@link Map} of the permissions
+ * @deprecated in favour of {@link #getPermissions(String, String, Map, boolean, List, boolean)}
*/
+ @Deprecated
Map getLocalPermissions(String server, List excludedGroups);
+ /**
+ * Convert the holders nodes into a Map of permissions to be applied on the platform
+ * @param server the server
+ * @param world the world
+ * @param extraContext any extra context to filter by
+ * @param includeGlobal whether to include global nodes
+ * @param possibleNodes a list of possible permissions for resolving wildcards
+ * @param applyGroups if inherited group permissions should be included
+ * @return a map of permissions
+ * @since 1.6
+ */
+ Map getPermissions(String server, String world, Map extraContext, boolean includeGlobal, List possibleNodes, boolean applyGroups);
+
/**
* Processes the nodes and returns the temporary ones.
* @return a map of temporary nodes
+ * @deprecated in favour of {@link #getTemporaryPermissionNodes()}
*/
+ @Deprecated
Map, Long> getTemporaryNodes();
+ /**
+ * Processes the nodes and returns the temporary ones.
+ * @return a set of temporary nodes
+ * @since 1.6
+ */
+ Set getTemporaryPermissionNodes();
+
/**
* Processes the nodes and returns the non-temporary ones.
* @return a map of permanent nodes
+ * @deprecated in favour of {@link #getPermanentPermissionNodes()}
*/
+ @Deprecated
Map getPermanentNodes();
+ /**
+ * Processes the nodes and returns the non-temporary ones.
+ * @return a set of permanent nodes
+ * @since 1.6
+ */
+ Set getPermanentPermissionNodes();
+
/**
* Removes temporary permissions that have expired
*/
diff --git a/api/src/main/java/me/lucko/luckperms/api/event/AbstractPermissionEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/AbstractPermissionEvent.java
new file mode 100644
index 000000000..f0240386e
--- /dev/null
+++ b/api/src/main/java/me/lucko/luckperms/api/event/AbstractPermissionEvent.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.api.event;
+
+import me.lucko.luckperms.api.Node;
+import me.lucko.luckperms.api.PermissionHolder;
+
+/**
+ * @since 1.6
+ */
+public class AbstractPermissionEvent extends TargetedEvent {
+
+ private final Node node;
+
+ protected AbstractPermissionEvent(String eventName, PermissionHolder target, Node node) {
+ super(eventName, target);
+ this.node = node;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+}
diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionExpireEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionExpireEvent.java
index 5f1bca90f..7569d0a48 100644
--- a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionExpireEvent.java
+++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionExpireEvent.java
@@ -27,7 +27,9 @@ import me.lucko.luckperms.api.event.TargetedEvent;
/**
* Called when a permission expires for an object.
+ * @deprecated in favour of {@link PermissionNodeExpireEvent}
*/
+@Deprecated
public class PermissionExpireEvent extends TargetedEvent {
private final String node;
diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeExpireEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeExpireEvent.java
new file mode 100644
index 000000000..82456d123
--- /dev/null
+++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeExpireEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.api.event.events;
+
+import me.lucko.luckperms.api.Node;
+import me.lucko.luckperms.api.PermissionHolder;
+import me.lucko.luckperms.api.event.AbstractPermissionEvent;
+
+/**
+ * Called when a temporary permission node expires
+ * @since 1.6
+ */
+public class PermissionNodeExpireEvent extends AbstractPermissionEvent {
+ public PermissionNodeExpireEvent(PermissionHolder target, Node node) {
+ super("Permission Node Expire Event", target, node);
+ }
+}
diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeSetEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeSetEvent.java
new file mode 100644
index 000000000..b0473e5b4
--- /dev/null
+++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeSetEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.api.event.events;
+
+import me.lucko.luckperms.api.Node;
+import me.lucko.luckperms.api.PermissionHolder;
+import me.lucko.luckperms.api.event.AbstractPermissionEvent;
+
+/**
+ * Called when a permission node is set on a holder
+ * @since 1.6
+ */
+public class PermissionNodeSetEvent extends AbstractPermissionEvent {
+ public PermissionNodeSetEvent(PermissionHolder target, Node node) {
+ super("Permission Node Set Event", target, node);
+ }
+}
diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeUnsetEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeUnsetEvent.java
new file mode 100644
index 000000000..8f6ebdf6a
--- /dev/null
+++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionNodeUnsetEvent.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.api.event.events;
+
+import me.lucko.luckperms.api.Node;
+import me.lucko.luckperms.api.PermissionHolder;
+import me.lucko.luckperms.api.event.AbstractPermissionEvent;
+
+/**
+ * Called when a permission node is unset from a holder
+ * @since 1.6
+ */
+public class PermissionNodeUnsetEvent extends AbstractPermissionEvent {
+ public PermissionNodeUnsetEvent(PermissionHolder target, Node node) {
+ super("Permission Node Unset Event", target, node);
+ }
+}
diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionSetEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionSetEvent.java
index 66a30ba59..44f7678ac 100644
--- a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionSetEvent.java
+++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionSetEvent.java
@@ -30,7 +30,9 @@ import java.util.Map;
/**
* Called whenever a user or group has a permission set.
+ * @deprecated in favour of {@link PermissionNodeSetEvent}
*/
+@Deprecated
public class PermissionSetEvent extends AbstractPermissionAddEvent {
private final String node;
diff --git a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionUnsetEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionUnsetEvent.java
index 65db10c14..23a50bf74 100644
--- a/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionUnsetEvent.java
+++ b/api/src/main/java/me/lucko/luckperms/api/event/events/PermissionUnsetEvent.java
@@ -27,7 +27,9 @@ import me.lucko.luckperms.api.event.AbstractPermissionRemoveEvent;
/**
* Called whenever a user or group has a permission unset.
+ * @deprecated in favour of {@link PermissionNodeUnsetEvent}
*/
+@Deprecated
public class PermissionUnsetEvent extends AbstractPermissionRemoveEvent {
private final String node;
diff --git a/bukkit-placeholders/pom.xml b/bukkit-placeholders/pom.xml
index 366c8ab03..5a200e7a8 100644
--- a/bukkit-placeholders/pom.xml
+++ b/bukkit-placeholders/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 2.5-SNAPSHOT
+ 2.6-SNAPSHOT
4.0.0
diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index 122b936fb..725b58048 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 2.5-SNAPSHOT
+ 2.6-SNAPSHOT
4.0.0
diff --git a/bungee/pom.xml b/bungee/pom.xml
index 144c7f81f..e89cc19f4 100644
--- a/bungee/pom.xml
+++ b/bungee/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 2.5-SNAPSHOT
+ 2.6-SNAPSHOT
4.0.0
diff --git a/common/pom.xml b/common/pom.xml
index 6d484959e..2c8e8fbe2 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 2.5-SNAPSHOT
+ 2.6-SNAPSHOT
4.0.0
diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java
index 173f5ba54..8a46df6d1 100644
--- a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java
+++ b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java
@@ -27,6 +27,7 @@ import lombok.AllArgsConstructor;
import lombok.NonNull;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.*;
+import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.event.LPEvent;
import me.lucko.luckperms.api.event.LPListener;
import me.lucko.luckperms.api.implementation.internal.*;
@@ -36,6 +37,8 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
+import static me.lucko.luckperms.api.implementation.internal.Utils.checkNode;
+
/**
* Provides static access to LuckPerms
*/
@@ -187,4 +190,9 @@ public class ApiProvider implements LuckPermsApi {
public boolean isTrackLoaded(@NonNull String name) {
return plugin.getTrackManager().isLoaded(name);
}
+
+ @Override
+ public Node.Builder buildNode(String permission) throws IllegalArgumentException {
+ return new me.lucko.luckperms.utils.Node.Builder(checkNode(permission));
+ }
}
diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java
index 2b21a28c7..16d873dd2 100644
--- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java
+++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/PermissionHolderLink.java
@@ -24,15 +24,15 @@ 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.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import static me.lucko.luckperms.api.implementation.internal.Utils.*;
+import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy;
/**
* Provides a link between {@link PermissionHolder} and {@link me.lucko.luckperms.core.PermissionHolder}
@@ -49,9 +49,24 @@ public class PermissionHolderLink implements PermissionHolder {
return master.getObjectName();
}
+ @Override
+ public Set getPermissionNodes() {
+ return Collections.unmodifiableSet(master.getNodes());
+ }
+
+ @Override
+ public Set getAllNodes() {
+ return Collections.unmodifiableSet(master.getAllNodes(null));
+ }
+
@Override
public Map getNodes() {
- return Collections.unmodifiableMap(master.getNodes());
+ return convertToLegacy(master.getNodes());
+ }
+
+ @Override
+ public boolean hasPermission(@NonNull Node node) {
+ return master.hasPermission(node);
}
@Override
@@ -84,6 +99,11 @@ public class PermissionHolderLink implements PermissionHolder {
return master.hasPermission(node, b, checkServer(server), world, temporary);
}
+ @Override
+ public boolean inheritsPermission(@NonNull Node node) {
+ return master.inheritsPermission(node);
+ }
+
@Override
public boolean inheritsPermission(@NonNull String node, @NonNull boolean b) {
return master.inheritsPermission(node, b);
@@ -114,6 +134,11 @@ public class PermissionHolderLink implements PermissionHolder {
return master.inheritsPermission(node, b, checkServer(server), world, temporary);
}
+ @Override
+ public void setPermission(@NonNull Node node) throws ObjectAlreadyHasException {
+ master.setPermission(node);
+ }
+
@Override
public void setPermission(@NonNull String node, @NonNull boolean value) throws ObjectAlreadyHasException {
master.setPermission(checkNode(node), value);
@@ -144,6 +169,11 @@ public class PermissionHolderLink implements PermissionHolder {
master.setPermission(checkNode(node), value, checkServer(server), world, checkTime(expireAt));
}
+ @Override
+ public void unsetPermission(@NonNull Node node) throws ObjectLacksException {
+ master.unsetPermission(node);
+ }
+
@Override
public void unsetPermission(@NonNull String node, @NonNull boolean temporary) throws ObjectLacksException {
master.unsetPermission(checkNode(node), temporary);
@@ -194,13 +224,34 @@ public class PermissionHolderLink implements PermissionHolder {
return master.getLocalPermissions(server, excludedGroups);
}
+ @Override
+ public Map getPermissions(String server, String world, Map extraContext, boolean includeGlobal, List possibleNodes, boolean applyGroups) {
+ return master.getPermissions(server, world, extraContext, includeGlobal, possibleNodes, applyGroups);
+ }
+
@Override
public Map, Long> getTemporaryNodes() {
+ Map, Long> m = new HashMap<>();
+
+ for (Node node : master.getTemporaryNodes()) {
+ m.put(new AbstractMap.SimpleEntry<>(node.getKey(), node.getValue()), node.getExpiryUnixTime());
+ }
+
+ return m;
+ }
+
+ @Override
+ public Set getTemporaryPermissionNodes() {
return master.getTemporaryNodes();
}
@Override
public Map getPermanentNodes() {
+ return convertToLegacy(master.getPermanentNodes());
+ }
+
+ @Override
+ public Set getPermanentPermissionNodes() {
return master.getPermanentNodes();
}
diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java
index e43a7c171..5b9469670 100644
--- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java
+++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/Utils.java
@@ -29,27 +29,27 @@ import me.lucko.luckperms.api.User;
import me.lucko.luckperms.utils.ArgumentChecker;
@UtilityClass
-class Utils {
+public class Utils {
- static void checkUser(User user) {
+ public static void checkUser(User user) {
if (!(user instanceof UserLink)) {
throw new IllegalStateException("User instance cannot be handled by this implementation.");
}
}
- static void checkGroup(Group group) {
+ public static void checkGroup(Group group) {
if (!(group instanceof GroupLink)) {
throw new IllegalStateException("Group instance cannot be handled by this implementation.");
}
}
- static void checkTrack(Track track) {
+ public static void checkTrack(Track track) {
if (!(track instanceof TrackLink)) {
throw new IllegalStateException("Track instance cannot be handled by this implementation.");
}
}
- static String checkUsername(String s) {
+ public static String checkUsername(String s) {
if (ArgumentChecker.checkUsername(s)) {
throw new IllegalArgumentException("Invalid username entry '" + s + "'. Usernames must be less than 16 chars" +
" and only contain 'a-z A-Z 1-9 _'.");
@@ -57,7 +57,7 @@ class Utils {
return s;
}
- static String checkName(String s) {
+ public static String checkName(String s) {
if (ArgumentChecker.checkName(s)) {
throw new IllegalArgumentException("Invalid name entry '" + s + "'. Names must be less than 37 chars" +
" and only contain 'a-z A-Z 1-9'.");
@@ -65,21 +65,21 @@ class Utils {
return s.toLowerCase();
}
- static String checkServer(String s) {
+ public static String checkServer(String s) {
if (ArgumentChecker.checkServer(s)) {
throw new IllegalArgumentException("Invalid server entry '" + s + "'. Server names can only contain alphanumeric characters.");
}
return s;
}
- static String checkNode(String s) {
+ public static String checkNode(String s) {
if (ArgumentChecker.checkNode(s)) {
throw new IllegalArgumentException("Invalid node entry '" + s + "'. Nodes cannot contain '/' or '$' characters.");
}
return s;
}
- static long checkTime(long l) {
+ public static long checkTime(long l) {
if (ArgumentChecker.checkTime(l)) {
throw new IllegalArgumentException("Unix time '" + l + "' is invalid, as it has already passed.");
}
diff --git a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupInfo.java b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupInfo.java
index b317967d0..8da31ef54 100644
--- a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupInfo.java
+++ b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupInfo.java
@@ -43,8 +43,8 @@ public class GroupInfo extends SubCommand {
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List args, String label) {
Message.GROUP_INFO.send(sender,
group.getName(),
- group.getPermanentNodes().keySet().size(),
- group.getTemporaryNodes().keySet().size(),
+ group.getPermanentNodes().size(),
+ group.getTemporaryNodes().size(),
label,
group.getName()
);
diff --git a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupListNodes.java b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupListNodes.java
index bdefa900e..f4137543a 100644
--- a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupListNodes.java
+++ b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupListNodes.java
@@ -30,6 +30,8 @@ import me.lucko.luckperms.groups.Group;
import java.util.List;
+import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy;
+
public class GroupListNodes extends SubCommand {
public GroupListNodes() {
super("listnodes", "Lists the permission nodes the group has", "/%s group listnodes",
@@ -38,8 +40,8 @@ public class GroupListNodes extends SubCommand {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List args, String label) {
- Message.LISTNODES.send(sender, group.getName(), Util.permNodesToString(group.getPermanentNodes()));
- Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getTemporaryNodes()));
+ Message.LISTNODES.send(sender, group.getName(), Util.permNodesToString(convertToLegacy(group.getPermanentNodes())));
+ Message.LISTNODES_TEMP.send(sender, group.getName(), Util.tempNodesToString(group.getTemporaryNodesLegacy()));
return CommandResult.SUCCESS;
}
}
diff --git a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserInfo.java b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserInfo.java
index feb4b89fc..b692abc62 100644
--- a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserInfo.java
+++ b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserInfo.java
@@ -43,8 +43,8 @@ public class UserInfo extends SubCommand {
plugin.getPlayerStatus(user.getUuid()),
Util.listToCommaSep(user.getGroupNames()),
user.getPrimaryGroup(),
- user.getPermanentNodes().keySet().size(),
- user.getTemporaryNodes().keySet().size(),
+ user.getPermanentNodes().size(),
+ user.getTemporaryNodes().size(),
label,
user.getName()
);
diff --git a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserListNodes.java b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserListNodes.java
index 9d0176f9e..80a15847d 100644
--- a/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserListNodes.java
+++ b/common/src/main/java/me/lucko/luckperms/commands/user/subcommands/UserListNodes.java
@@ -26,10 +26,13 @@ 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.PermissionHolder;
import me.lucko.luckperms.users.User;
import java.util.List;
+import static me.lucko.luckperms.core.PermissionHolder.*;
+
public class UserListNodes extends SubCommand {
public UserListNodes() {
super("listnodes", "Lists the permission nodes the user has", "/%s user listnodes",
@@ -38,8 +41,8 @@ public class UserListNodes extends SubCommand {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List args, String label) {
- Message.LISTNODES.send(sender, user.getName(), Util.permNodesToString(user.getPermanentNodes()));
- Message.LISTNODES_TEMP.send(sender, user.getName(), Util.tempNodesToString(user.getTemporaryNodes()));
+ Message.LISTNODES.send(sender, user.getName(), Util.permNodesToString(convertToLegacy(user.getPermanentNodes())));
+ Message.LISTNODES_TEMP.send(sender, user.getName(), Util.tempNodesToString(user.getTemporaryNodesLegacy()));
return CommandResult.SUCCESS;
}
}
diff --git a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java
index 03198de49..c652be3eb 100644
--- a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java
+++ b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java
@@ -26,20 +26,18 @@ import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.LuckPermsPlugin;
+import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.event.events.GroupRemoveEvent;
-import me.lucko.luckperms.api.event.events.PermissionExpireEvent;
-import me.lucko.luckperms.api.event.events.PermissionSetEvent;
-import me.lucko.luckperms.api.event.events.PermissionUnsetEvent;
+import me.lucko.luckperms.api.event.events.PermissionNodeExpireEvent;
+import me.lucko.luckperms.api.event.events.PermissionNodeSetEvent;
+import me.lucko.luckperms.api.event.events.PermissionNodeUnsetEvent;
import me.lucko.luckperms.api.implementation.internal.PermissionHolderLink;
-import me.lucko.luckperms.constants.Patterns;
import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
import me.lucko.luckperms.exceptions.ObjectLacksException;
import me.lucko.luckperms.groups.Group;
-import me.lucko.luckperms.utils.DateUtil;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@@ -66,31 +64,42 @@ public abstract class PermissionHolder {
* The user/group's permissions
*/
@Getter
- private Map nodes = new ConcurrentHashMap<>();
+ private Set nodes = ConcurrentHashMap.newKeySet();
- public void setNodes(Map nodes) {
+ public void setNodes(Set nodes) {
this.nodes.clear();
- this.nodes.putAll(nodes);
+ this.nodes.addAll(nodes);
auditTemporaryPermissions();
}
- /**
- * Utility method for checking if a map has a certain permission. Used by both #hasPermission and #inheritsPermission
- */
- private static boolean hasPermission(Map toQuery, String node, boolean b) {
- // Not temporary
- if (!node.contains("$")) {
- return b ? toQuery.containsKey(node) && toQuery.get(node) : toQuery.containsKey(node) && !toQuery.get(node);
+ @Deprecated
+ public static Map convertToLegacy(Set nodes) {
+ Map m = new HashMap<>();
+ for (Node node : nodes) {
+ m.put(node.toSerializedNode(), node.getValue());
}
+ return Collections.unmodifiableMap(m);
+ }
- node = Patterns.TEMP_DELIMITER.split(node)[0];
+ private static Node.Builder buildNode(String permission) {
+ return new me.lucko.luckperms.utils.Node.Builder(permission);
+ }
- for (Map.Entry e : toQuery.entrySet()) {
- if (e.getKey().contains("$")) {
- String[] parts = Patterns.TEMP_DELIMITER.split(e.getKey());
- if (parts[0].equalsIgnoreCase(node)) {
- return b ? e.getValue() : !e.getValue();
- }
+ @Deprecated
+ public void setNodes(Map nodes) {
+ this.nodes.clear();
+
+ this.nodes.addAll(nodes.entrySet().stream()
+ .map(e -> me.lucko.luckperms.utils.Node.fromSerialisedNode(e.getKey(), e.getValue()))
+ .collect(Collectors.toList()));
+
+ auditTemporaryPermissions();
+ }
+
+ private static boolean hasPermission(Set toQuery, Node node) {
+ for (Node n : toQuery) {
+ if (n.almostEquals(node)) {
+ return true;
}
}
@@ -98,436 +107,171 @@ public abstract class PermissionHolder {
}
/**
- * Checks to see if the object has a certain permission
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @return true if the user has the permission
+ * Check if the holder has a permission node
+ * @param node the node to check
+ * @return true if the holder has the node
*/
+ public boolean hasPermission(Node node) {
+ return hasPermission(this.nodes, node);
+ }
+
public boolean hasPermission(String node, boolean b) {
- if (node.startsWith("global/")) node = node.replace("global/", "");
- return hasPermission(this.nodes, node, b);
+ return hasPermission(buildNode(node).setValue(b).build());
}
- /**
- * Checks to see the the object has a permission on a certain server
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param server The server
- * @return true if the user has the permission
- */
public boolean hasPermission(String node, boolean b, String server) {
- return hasPermission(server + "/" + node, b);
+ return hasPermission(buildNode(node).setValue(b).setServer(server).build());
}
- /**
- * Checks to see the the object has a permission on a certain server
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param server The server
- * @param world The world
- * @return true if the user has the permission
- */
public boolean hasPermission(String node, boolean b, String server, String world) {
- return hasPermission(server + "-" + world + "/" + node, b);
+ return hasPermission(buildNode(node).setValue(b).setServer(server).setWorld(world).build());
}
- /**
- * Checks to see the the object has a permission on a certain server
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param temporary if the permission is temporary
- * @return true if the user has the permission
- */
public boolean hasPermission(String node, boolean b, boolean temporary) {
- return hasPermission(node + (temporary ? "$a" : ""), b);
+ return hasPermission(buildNode(node).setValue(b).setExpiry(temporary ? 10L : 0L).build());
}
- /**
- * Checks to see the the object has a permission on a certain server
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param server The server to check on
- * @param temporary if the permission is temporary
- * @return true if the user has the permission
- */
public boolean hasPermission(String node, boolean b, String server, boolean temporary) {
- return hasPermission(server + "/" + node + (temporary ? "$a" : ""), b);
+ return hasPermission(buildNode(node).setValue(b).setServer(server).setExpiry(temporary ? 10L : 0L).build());
}
- /**
- * Checks to see the the object has a permission on a certain server
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param server The server to check on
- * @param world The world to check on
- * @param temporary if the permission is temporary
- * @return true if the user has the permission
- */
public boolean hasPermission(String node, boolean b, String server, String world, boolean temporary) {
- return hasPermission(server + "-" + world + "/" + node + (temporary ? "$a" : ""), b);
+ return hasPermission(buildNode(node).setValue(b).setServer(server).setWorld(world).setExpiry(temporary ? 10L : 0L).build());
}
/**
- * Checks to see if the object inherits a certain permission
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @return true if the user inherits the permission
+ * Check if the holder inherits a node
+ * @param node the node to check
+ * @return true if the holder inherits the node
*/
+ public boolean inheritsPermission(Node node) {
+ return hasPermission(getAllNodes(null), node);
+ }
+
public boolean inheritsPermission(String node, boolean b) {
- if (node.contains("/")) {
- // Use other method
- final String[] parts = Patterns.SERVER_DELIMITER.split(node, 2);
- return inheritsPermission(parts[1], b, parts[0]);
- }
-
- return inheritsPermission(node, b, "global");
+ return inheritsPermission(buildNode(node).setValue(b).build());
}
- /**
- * Checks to see the the object inherits a permission on a certain server
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param server The server
- * @return true if the user inherits the permission
- */
public boolean inheritsPermission(String node, boolean b, String server) {
- if (server.contains("-")) {
- // Use other method
- final String[] parts = Patterns.WORLD_DELIMITER.split(server, 2);
- return inheritsPermission(node, b, parts[0], parts[1]);
- }
-
- final Map local = getLocalPermissions(server, null);
- return hasPermission(local, node, b);
+ return inheritsPermission(buildNode(node).setValue(b).setServer(server).build());
}
- /**
- * Checks to see the the object inherits a permission on a certain server
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param server The server
- * @param world The world
- * @return true if the user inherits the permission
- */
public boolean inheritsPermission(String node, boolean b, String server, String world) {
- final Map local = getLocalPermissions(server, world, null);
- return hasPermission(local, node, b);
+ return inheritsPermission(buildNode(node).setValue(b).setServer(server).setWorld(world).build());
}
- /**
- * Checks to see if the object inherits a certain permission
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param temporary if the permission is temporary
- * @return true if the user inherits the permission
- */
public boolean inheritsPermission(String node, boolean b, boolean temporary) {
- return inheritsPermission(node + (temporary ? "$a" : ""), b);
+ return inheritsPermission(buildNode(node).setValue(b).setExpiry(temporary ? 10L : 0L).build());
}
- /**
- * Checks to see if the object inherits a certain permission
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param server The server
- * @param temporary if the permission is temporary
- * @return true if the user inherits the permission
- */
public boolean inheritsPermission(String node, boolean b, String server, boolean temporary) {
- return inheritsPermission(server + "/" + node + (temporary ? "$a" : ""), b);
+ return inheritsPermission(buildNode(node).setValue(b).setServer(server).setExpiry(temporary ? 10L : 0L).build());
}
- /**
- * Checks to see if the object inherits a certain permission
- * @param node The permission node
- * @param b If the node is true/false(negated)
- * @param server The server
- * @param world The world
- * @param temporary if the permission is temporary
- * @return true if the user inherits the permission
- */
public boolean inheritsPermission(String node, boolean b, String server, String world, boolean temporary) {
- return inheritsPermission(server + "-" + world + "/" + node + (temporary ? "$a" : ""), b);
+ return inheritsPermission(buildNode(node).setValue(b).setServer(server).setWorld(world).setExpiry(temporary ? 10L : 0L).build());
}
- /**
- * Sets a permission for the object
- * @param node The node to be set
- * @param value What to set the node to - true/false(negated)
- * @throws ObjectAlreadyHasException if the object already has the permission
- */
- public void setPermission(String node, boolean value) throws ObjectAlreadyHasException {
- setPermission(node, value, null, null, 0L);
- }
-
- /**
- * Sets a permission for the object
- * @param node The node to set
- * @param value What to set the node to - true/false(negated)
- * @param server The server to set the permission on
- * @throws ObjectAlreadyHasException if the object already has the permission
- */
- public void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException {
- setPermission(node, value, server, null, 0L);
- }
-
- /**
- * Sets a permission for the object
- * @param node The node to set
- * @param value What to set the node to - true/false(negated)
- * @param server The server to set the permission on
- * @param world The world to set the permission on
- * @throws ObjectAlreadyHasException if the object already has the permission
- */
- public void setPermission(String node, boolean value, String server, String world) throws ObjectAlreadyHasException {
- setPermission(node, value, server, world, 0L);
- }
-
- /**
- * Sets a permission for the object
- * @param node The node to set
- * @param value What to set the node to - true/false(negated)
- * @param expireAt The time in unixtime when the permission will expire
- * @throws ObjectAlreadyHasException if the object already has the permission
- */
- public void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException {
- setPermission(node, value, null, null, expireAt);
- }
-
- /**
- * Sets a permission for the object
- * @param node The node to set
- * @param value What to set the node to - true/false(negated)
- * @param server The server to set the permission on
- * @param expireAt The time in unixtime when the permission will expire
- * @throws ObjectAlreadyHasException if the object already has the permission
- */
- public void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException {
- setPermission(node, value, server, null, expireAt);
- }
-
- /**
- * Sets a permission for the object
- * @param node The node to set
- * @param value What to set the node to - true/false(negated)
- * @param server The server to set the permission on
- * @param world The world to set the permission on
- * @param expireAt The time in unixtime when the permission will expire
- * @throws ObjectAlreadyHasException if the object already has the permission
- */
- public void setPermission(String node, boolean value, String server, String world, long expireAt) throws ObjectAlreadyHasException {
- if (node.startsWith("global/")) node = node.replace("global/", "");
-
- if (server != null && server.equals("")) server = null;
- if (world != null && world.equals("")) world = null;
-
- StringBuilder builder = new StringBuilder();
-
- if (server != null) {
- builder.append(server);
-
- if (world != null) {
- builder.append("-").append(world);
- }
- builder.append("/");
- } else {
- if (world != null) {
- builder.append("global-").append(world).append("/");
- }
- }
-
- builder.append(node);
-
- if (expireAt != 0L) {
- builder.append("$").append(expireAt);
- }
-
- final String finalNode = builder.toString();
-
-
- if (hasPermission(finalNode, value)) {
+ public void setPermission(Node node) throws ObjectAlreadyHasException {
+ if (hasPermission(node)) {
throw new ObjectAlreadyHasException();
}
- this.nodes.put(finalNode, value);
- plugin.getApiProvider().fireEventAsync(new PermissionSetEvent(
- new PermissionHolderLink(this), node, value, server, world, expireAt));
+ nodes.add(node);
+ plugin.getApiProvider().fireEventAsync(new PermissionNodeSetEvent(new PermissionHolderLink(this), node));
+ }
+
+ public void setPermission(String node, boolean value) throws ObjectAlreadyHasException {
+ setPermission(buildNode(node).setValue(value).build());
+ }
+
+ public void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException {
+ setPermission(buildNode(node).setValue(value).setServer(server).build());
+ }
+
+ public void setPermission(String node, boolean value, String server, String world) throws ObjectAlreadyHasException {
+ setPermission(buildNode(node).setValue(value).setServer(server).setWorld(world).build());
+ }
+
+ public void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException {
+ setPermission(buildNode(node).setValue(value).setExpiry(expireAt).build());
+ }
+
+ public void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException {
+ setPermission(buildNode(node).setValue(value).setServer(server).setExpiry(expireAt).build());
+ }
+
+ public void setPermission(String node, boolean value, String server, String world, long expireAt) throws ObjectAlreadyHasException {
+ setPermission(buildNode(node).setValue(value).setServer(server).setWorld(world).setExpiry(expireAt).build());
}
/**
- * Unsets a permission for the object
- * @param node The node to be unset
- * @param temporary if the permission being removed is temporary
- * @throws ObjectLacksException if the node wasn't already set
+ * Unsets a permission node
+ * @param node the node to unset
+ * @throws ObjectLacksException if the holder doesn't have this node already
*/
- public void unsetPermission(String node, boolean temporary) throws ObjectLacksException {
- unsetPermission(node, null, null, temporary);
- }
-
- /**
- * Unsets a permission for the object
- * @param node The node to be unset
- * @throws ObjectLacksException if the node wasn't already set
- */
- public void unsetPermission(String node) throws ObjectLacksException {
- unsetPermission(node, null, null, false);
- }
-
- /**
- * Unsets a permission for the object
- * @param node The node to be unset
- * @param server The server to unset the node on
- * @throws ObjectLacksException if the node wasn't already set
- */
- public void unsetPermission(String node, String server) throws ObjectLacksException {
- unsetPermission(node, server, null, false);
- }
-
- /**
- * Unsets a permission for the object
- * @param node The node to be unset
- * @param server The server to unset the node on
- * @param world The world to unset the node on
- * @throws ObjectLacksException if the node wasn't already set
- */
- public void unsetPermission(String node, String server, String world) throws ObjectLacksException {
- unsetPermission(node, server, world, false);
- }
-
- /**
- * Unsets a permission for the object
- * @param node The node to be unset
- * @param server The server to unset the node on
- * @param temporary if the permission being unset is temporary
- * @throws ObjectLacksException if the node wasn't already set
- */
- public void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException {
- unsetPermission(node, server, null, temporary);
- }
-
- /**
- * Unsets a permission for the object
- * @param node The node to be unset
- * @param server The server to unset the node on
- * @param world The world to unset the node on
- * @param temporary if the permission being unset is temporary
- * @throws ObjectLacksException if the node wasn't already set
- */
- public void unsetPermission(String node, String server, String world, boolean temporary) throws ObjectLacksException {
- if (node.startsWith("global/")) node = node.replace("global/", "");
-
- if (server != null && server.equals("")) server = null;
- if (world != null && world.equals("")) world = null;
-
- StringBuilder builder = new StringBuilder();
-
- if (server != null) {
- builder.append(server);
-
- if (world != null) {
- builder.append("-").append(world);
- }
- builder.append("/");
- } else {
- if (world != null) {
- builder.append("global-").append(world).append("/");
- }
- }
-
- builder.append(node);
-
- final String finalNode = builder.toString();
- Optional match = Optional.empty();
-
- if (temporary) {
- match = this.nodes.keySet().stream()
- .filter(n -> n.contains("$"))
- .filter(n -> Patterns.TEMP_DELIMITER.split(n)[0].equalsIgnoreCase(finalNode))
- .findFirst();
- } else {
- if (this.nodes.containsKey(finalNode)) {
- match = Optional.of(finalNode);
- }
- }
-
- if (match.isPresent()) {
- this.nodes.remove(match.get());
- plugin.getApiProvider().fireEventAsync(new PermissionUnsetEvent(
- new PermissionHolderLink(this), node, server, world, temporary));
- if (node.startsWith("group.")) {
- plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(
- new PermissionHolderLink(this), Patterns.DOT.split(node, 2)[1], server, world, temporary));
- }
-
- } else {
+ public void unsetPermission(Node node) throws ObjectLacksException {
+ if (!hasPermission(node)) {
throw new ObjectLacksException();
}
+
+ nodes.remove(node);
+
+ if (node.isGroupNode()) {
+ plugin.getApiProvider().fireEventAsync(new GroupRemoveEvent(new PermissionHolderLink(this),
+ node.getGroupName(), node.getServer().orElse(null), node.getWorld().orElse(null), node.isTemporary()));
+ } else {
+ plugin.getApiProvider().fireEventAsync(new PermissionNodeUnsetEvent(new PermissionHolderLink(this), node));
+ }
+ }
+
+ public void unsetPermission(String node, boolean temporary) throws ObjectLacksException {
+ unsetPermission(buildNode(node).setExpiry(temporary ? 10L : 0L).build());
+ }
+
+ public void unsetPermission(String node) throws ObjectLacksException {
+ unsetPermission(buildNode(node).build());
+ }
+
+ public void unsetPermission(String node, String server) throws ObjectLacksException {
+ unsetPermission(buildNode(node).setServer(server).build());
+ }
+
+ public void unsetPermission(String node, String server, String world) throws ObjectLacksException {
+ unsetPermission(buildNode(node).setServer(server).setWorld(world).build());
+ }
+
+ public void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException {
+ unsetPermission(buildNode(node).setServer(server).setExpiry(temporary ? 10L : 0L).build());
+ }
+
+ public void unsetPermission(String node, String server, String world, boolean temporary) throws ObjectLacksException {
+ unsetPermission(buildNode(node).setServer(server).setWorld(world).setExpiry(temporary ? 10L : 0L).build());
}
/**
- * Gets the permissions and inherited permissions that apply to a specific server
- * @param server The server to get nodes for
- * @param world The world to get nodes for
- * @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
- * @param possibleNodes A list of possible permission nodes for wildcard permission handling
- * @return a {@link Map} of the permissions
+ * @return The temporary nodes held by the holder
*/
- public Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes) {
- return getPermissions(server, world, excludedGroups, plugin.getConfiguration().getIncludeGlobalPerms(), possibleNodes);
+ public Set getTemporaryNodes() {
+ return nodes.stream().filter(Node::isTemporary).collect(Collectors.toSet());
+ }
+
+ @Deprecated
+ public Map, Long> getTemporaryNodesLegacy() {
+ Map, Long> m = new HashMap<>();
+
+ for (Node node : getTemporaryNodes()) {
+ m.put(new AbstractMap.SimpleEntry<>(node.getKey(), node.getValue()), node.getExpiryUnixTime());
+ }
+
+ return m;
}
/**
- * Gets the permissions and inherited permissions that apply to a specific server
- * @param server The server to get nodes for
- * @param world The world to get nodes for
- * @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
- * @return a {@link Map} of the permissions
+ * @return The permanent nodes held by the holder
*/
- public Map getLocalPermissions(String server, String world, List excludedGroups) {
- return getPermissions(server, world, excludedGroups, plugin.getConfiguration().getIncludeGlobalPerms(), null);
- }
-
- /**
- * Gets the permissions and inherited permissions that apply to a specific server
- * @param server The server to get nodes for
- * @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
- * @param possibleNodes A list of possible permission nodes for wildcard permission handling
- * @return a {@link Map} of the permissions
- */
- public Map getLocalPermissions(String server, List excludedGroups, List possibleNodes) {
- return getLocalPermissions(server, null, excludedGroups, possibleNodes);
- }
-
- /**
- * Gets the permissions and inherited permissions that apply to a specific server
- * @param server The server to get nodes for
- * @param excludedGroups Groups that shouldn't be inherited (to prevent circular inheritance issues)
- * @return a {@link Map} of the permissions
- */
- public Map getLocalPermissions(String server, List excludedGroups) {
- return getLocalPermissions(server, null, excludedGroups, null);
- }
-
- /**
- * Processes the objects and returns the temporary ones.
- * @return a map of temporary nodes
- */
- public Map, Long> getTemporaryNodes() {
- return this.nodes.entrySet().stream().filter(e -> e.getKey().contains("$")).map(e -> {
- final String[] parts = Patterns.TEMP_DELIMITER.split(e.getKey());
- final long expiry = Long.parseLong(parts[1]);
- return new AbstractMap.SimpleEntry, Long>(new AbstractMap.SimpleEntry<>(parts[0], e.getValue()), expiry);
-
- }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
- }
-
- /**
- * Processes the objects and returns the non-temporary ones.
- * @return a map of permanent nodes
- */
- public Map getPermanentNodes() {
- return this.nodes.entrySet().stream().filter(e -> !e.getKey().contains("$"))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ public Set getPermanentNodes() {
+ return nodes.stream().filter(Node::isPermanent).collect(Collectors.toSet());
}
/**
@@ -535,384 +279,183 @@ public abstract class PermissionHolder {
* @return true if permissions had expired and were removed
*/
public boolean auditTemporaryPermissions() {
- List toExpire = this.nodes.keySet().stream()
- .filter(s -> s.contains("$"))
- .filter(s -> DateUtil.shouldExpire(Long.parseLong(Patterns.TEMP_DELIMITER.split(s)[1])))
- .collect(Collectors.toList());
+ boolean work = false;
+ Iterator iterator = nodes.iterator();
- toExpire.forEach(s -> {
- plugin.getApiProvider().fireEventAsync(new PermissionExpireEvent(new PermissionHolderLink(this), s));
- this.nodes.remove(s);
- });
- return !toExpire.isEmpty();
- }
+ while (iterator.hasNext()) {
+ Node element = iterator.next();
+ if (element.hasExpired()) {
+ iterator.remove();
- public Map convertTemporaryPerms() {
- auditTemporaryPermissions();
-
- Map nodes = new HashMap<>();
- Map tempNodes = new HashMap<>();
-
- for (Map.Entry e : this.nodes.entrySet()) {
- if (e.getKey().contains("$")) {
- tempNodes.put(e.getKey(), e.getValue());
- } else {
- nodes.put(e.getKey(), e.getValue());
+ work = true;
+ plugin.getApiProvider().fireEventAsync(new PermissionNodeExpireEvent(new PermissionHolderLink(this), element));
}
}
- // temporary permissions override non-temporary permissions
- tempNodes.entrySet().forEach(e -> nodes.put(stripTime(e.getKey()), e.getValue()));
- return nodes;
+ return work;
}
- public Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal, List possibleNodes) {
- return getPermissions(server, world, excludedGroups, includeGlobal, possibleNodes, true);
- }
+ /**
+ * Gets all of the nodes that this holder has and inherits
+ * @param excludedGroups a list of groups to exclude
+ * @return a set of nodes
+ */
+ public Set getAllNodes(List excludedGroups) {
+ Set all = ConcurrentHashMap.newKeySet();
+ all.addAll(nodes);
- public Map getPermissions(String server, String world, List excludedGroups, boolean includeGlobal, List possibleNodes, boolean applyGroups) {
if (excludedGroups == null) {
excludedGroups = new ArrayList<>();
}
- excludedGroups.add(getObjectName());
+ excludedGroups.add(getObjectName().toLowerCase());
+
+ Set parents = nodes.stream()
+ .filter(Node::isGroupNode)
+ .map(Node::getGroupName)
+ .collect(Collectors.toSet());
+
+ for (String parent : parents) {
+ Group group = plugin.getGroupManager().get(parent);
+ if (group == null) {
+ continue;
+ }
+
+ if (excludedGroups.contains(group.getObjectName())) {
+ continue;
+ }
+
+ inherited:
+ for (Node inherited : group.getAllNodes(excludedGroups)) {
+ for (Node existing : all) {
+ if (existing.almostEquals(inherited)) {
+ continue inherited;
+ }
+ }
+
+ all.add(inherited);
+ }
+ }
+
+ return all;
+ }
+
+ /*
+ * Don't use these methods, only here for compat reasons
+ */
+ public Map getLocalPermissions(String server, String world, List excludedGroups, List possibleNodes) {
+ return getPermissions(server, world, null, plugin.getConfiguration().getIncludeGlobalPerms(), possibleNodes, true);
+ }
+
+ public Map getLocalPermissions(String server, String world, List excludedGroups) {
+ return getPermissions(server, world, null, plugin.getConfiguration().getIncludeGlobalPerms(), null, true);
+ }
+
+ public Map getLocalPermissions(String server, List excludedGroups, List possibleNodes) {
+ return getLocalPermissions(server, null, excludedGroups, possibleNodes);
+ }
+
+ public Map getLocalPermissions(String server, List excludedGroups) {
+ return getLocalPermissions(server, null, excludedGroups, null);
+ }
+
+ /**
+ * Convert the holders nodes into a Map of permissions to be applied on the platform
+ * @param server the server
+ * @param world the world
+ * @param extraContext any extra context to filter by
+ * @param includeGlobal whether to include global nodes
+ * @param possibleNodes a list of possible permissions for resolving wildcards
+ * @param applyGroups if inherited group permissions should be included
+ * @return a map of permissions
+ */
+ public Map getPermissions(String server, String world, Map extraContext, boolean includeGlobal, List possibleNodes, boolean applyGroups) {
Map perms = new HashMap<>();
-
- if (server == null || server.equals("")) {
- server = "global";
- }
-
- if (world != null && world.equalsIgnoreCase("")) {
- world = null;
- }
-
- /*
- Priority:
-
- 1. server+world specific nodes
- 2. server specific nodes
- 3. user nodes
- 4. server+world specific group nodes
- 5. server specific group nodes
- 6. group nodes
- */
-
- final Map serverWorldSpecificNodes = new HashMap<>();
- final Map serverSpecificNodes = new HashMap<>();
- final Map userNodes = new HashMap<>();
- final Map serverWorldSpecificGroups = new HashMap<>();
- final Map serverSpecificGroups = new HashMap<>();
- final Map groupNodes = new HashMap<>();
-
- // Sorts the permissions and puts them into a priority order
- for (Map.Entry node : convertTemporaryPerms().entrySet()) {
- serverSpecific:
- if (node.getKey().contains("/")) {
- String[] parts = Patterns.SERVER_DELIMITER.split(node.getKey(), 2);
- // 0=server(+world) 1=node
-
- // WORLD SPECIFIC
- if (parts[0].contains("-")) {
- String[] serverParts = Patterns.WORLD_DELIMITER.split(parts[0], 2);
- // 0=server 1=world
-
- if ((!serverParts[0].equalsIgnoreCase("global") || !includeGlobal) && (!matches(server, serverParts[0]))) {
- // GLOBAL AND UNWANTED OR SERVER SPECIFIC BUT DOES NOT APPLY :(((
- continue;
- }
-
- if (world != null && !matches(world, serverParts[1])) {
- // WORLD SPECIFIC BUT DOES NOT APPLY
- continue;
- }
-
- if (Patterns.GROUP_MATCH.matcher(parts[1]).matches()) {
- // SERVER+WORLD SPECIFIC AND GROUP
- serverWorldSpecificGroups.put(node.getKey(), node.getValue());
- continue;
- }
-
- // SERVER+WORLD SPECIFIC
- serverWorldSpecificNodes.put(node.getKey(), node.getValue());
- continue;
- }
-
- if (parts[0].equalsIgnoreCase("global")) {
- // REGULAR
- break serverSpecific;
- }
-
- if (!matches(server, parts[0])) {
- // SERVER SPECIFIC BUT DOES NOT APPLY
- continue;
- }
-
- if (Patterns.GROUP_MATCH.matcher(parts[1]).matches()) {
- // SERVER SPECIFIC AND GROUP
- serverSpecificGroups.put(node.getKey(), node.getValue());
- continue;
- }
-
- // SERVER SPECIFIC
- serverSpecificNodes.put(node.getKey(), node.getValue());
- continue;
- }
-
- // Skip adding global permissions if they are not requested
- if (!includeGlobal) continue;
-
- // Could be here if the server was set to global.
- String n = node.getKey();
- if (n.contains("/")) {
- n = Patterns.SERVER_DELIMITER.split(n, 2)[1];
- }
-
- if (Patterns.GROUP_MATCH.matcher(n).matches()) {
- // GROUP
- groupNodes.put(n, node.getValue());
- continue;
- }
-
- // JUST NORMAL
- userNodes.put(n, node.getValue());
- }
-
- // If a group is negated at a higher priority, the group should not then be applied at a lower priority
- serverWorldSpecificGroups.entrySet().stream().filter(node -> !node.getValue()).forEach(node -> {
- groupNodes.remove(node.getKey());
- groupNodes.remove(Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]);
- serverSpecificGroups.remove(node.getKey());
- serverSpecificGroups.remove(Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]);
- serverSpecificGroups.remove(Patterns.WORLD_DELIMITER.split(node.getKey(), 2)[0] + "/" + Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]);
- });
- serverSpecificGroups.entrySet().stream().filter(node -> !node.getValue()).forEach(node -> {
- groupNodes.remove(node.getKey());
- groupNodes.remove(Patterns.SERVER_DELIMITER.split(node.getKey(), 2)[1]);
- });
+ SortedSet allNodes;
if (applyGroups) {
- // Apply lowest priority: groupNodes
- for (Map.Entry groupNode : groupNodes.entrySet()) {
- // Add the actual group perm node, so other plugins can hook
- perms.put(groupNode.getKey(), groupNode.getValue());
-
- // Don't add negated groups
- if (!groupNode.getValue()) continue;
-
- String groupName = Patterns.DOT.split(groupNode.getKey(), 2)[1];
- if (!excludedGroups.contains(groupName)) {
- Group group = plugin.getGroupManager().get(groupName);
- if (group != null) {
- perms.putAll(group.getLocalPermissions(server, excludedGroups));
- } else {
- plugin.getLog().warn("Error whilst refreshing the permissions of '" + objectName + "'." +
- "\n The group '" + groupName + "' is not loaded.");
- }
- }
- }
-
- applyShorthandIfEnabled(perms);
-
- // Apply next priorities: serverSpecificGroups and then serverWorldSpecificGroups
- for (Map m : Arrays.asList(serverSpecificGroups, serverWorldSpecificGroups)) {
- for (Map.Entry groupNode : m.entrySet()) {
- final String rawNode = Patterns.SERVER_DELIMITER.split(groupNode.getKey())[1];
-
- // Add the actual group perm node, so other plugins can hook
- perms.put(rawNode, groupNode.getValue());
-
- // Don't add negated groups
- if (!groupNode.getValue()) continue;
-
- String groupName = Patterns.DOT.split(rawNode, 2)[1];
- if (!excludedGroups.contains(groupName)) {
- Group group = plugin.getGroupManager().get(groupName);
- if (group != null) {
- perms.putAll(group.getLocalPermissions(server, excludedGroups));
- } else {
- plugin.getLog().warn("Error whilst refreshing the permissions of '" + objectName + "'." +
- "\n The group '" + groupName + "' is not loaded.");
- }
- }
- }
- applyShorthandIfEnabled(perms);
- }
+ allNodes = sort(getAllNodes(null), true);
+ } else {
+ allNodes = sort(nodes, true);
}
- // Apply next priority: userNodes
- perms.putAll(userNodes);
- applyShorthandIfEnabled(perms);
-
- // Apply final priorities: serverSpecificNodes and then serverWorldSpecificNodes
- for (Map m : Arrays.asList(serverSpecificNodes, serverWorldSpecificNodes)) {
- for (Map.Entry node : m.entrySet()) {
- final String rawNode = Patterns.SERVER_DELIMITER.split(node.getKey())[1];
- perms.put(rawNode, node.getValue());
+ for (Node node : allNodes) {
+ if (!node.shouldApplyOnServer(server, includeGlobal, plugin.getConfiguration().getApplyRegex())) {
+ continue;
}
- applyShorthandIfEnabled(perms);
- }
- if (plugin.getConfiguration().getApplyRegex()) {
- if (possibleNodes != null && !possibleNodes.isEmpty()) {
- perms = applyRegex(perms, possibleNodes);
- } else {
- perms = applyRegex(perms, plugin.getPossiblePermissions());
+ if (!node.shouldApplyOnWorld(world, includeGlobal, plugin.getConfiguration().getApplyRegex())) {
+ continue;
}
- }
- applyShorthandIfEnabled(perms);
+ if (!node.shouldApplyWithContext(extraContext)) {
+ continue;
+ }
- if (plugin.getConfiguration().getApplyWildcards()) {
- if (possibleNodes != null && !possibleNodes.isEmpty()) {
- perms = applyWildcards(perms, possibleNodes);
- } else {
- perms = applyWildcards(perms, plugin.getPossiblePermissions());
+ perms.put(node.getPermission(), node.getValue());
+
+ if (plugin.getConfiguration().getApplyShorthand()) {
+ node.resolveShorthand().stream()
+ .filter(s -> !perms.containsKey(s))
+ .forEach(s -> perms.put(s, node.getValue()));
+ }
+
+ if (plugin.getConfiguration().getApplyWildcards()) {
+ node.resolveWildcard(possibleNodes).stream()
+ .filter(s -> !perms.containsKey(s))
+ .forEach(s -> perms.put(s, node.getValue()));
}
}
return perms;
}
- private boolean matches(String entry, String possibleRegex) {
- if (possibleRegex.toLowerCase().startsWith("r=") && plugin.getConfiguration().getApplyRegex()) {
- Pattern p = Patterns.compile(possibleRegex.substring(2));
- if (p == null) {
- return false;
+ public static SortedSet sort(Set toSort, boolean reversed) {
+ TreeSet set = new TreeSet<>(reversed ? PRIORITY_COMPARATOR.reversed() : PRIORITY_COMPARATOR);
+ set.addAll(toSort);
+ return set;
+ }
+
+ private static final PriorityComparator PRIORITY_COMPARATOR = new PriorityComparator();
+ private static class PriorityComparator implements Comparator {
+
+ @Override
+ public int compare(Node o1, Node o2) {
+ if (takesPriority(o1, o2)) {
+ return 1;
}
- return p.matcher(entry).matches();
+
+ if (takesPriority(o2, o1)) {
+ return -1;
+ }
+
+ return 1;
}
- if (possibleRegex.startsWith("(") && possibleRegex.endsWith(")") && possibleRegex.contains("|")) {
- final String bits = possibleRegex.substring(1, possibleRegex.length() - 1);
- String[] parts = Patterns.VERTICAL_BAR.split(bits);
+ public boolean takesPriority(Node target, Node over) {
+ if (target.isTemporary() && !over.isTemporary()) {
+ return true;
+ }
- for (String s : parts) {
- if (s.equalsIgnoreCase(entry)) {
+ if (target.isWorldSpecific() && !over.isWorldSpecific()) {
+ return true;
+ }
+
+ if (target.isServerSpecific() && !over.isServerSpecific()) {
+ return true;
+ }
+
+ if (!target.isWildcard() && over.isWildcard()) {
+ return true;
+ }
+
+ if (target.isWildcard() && over.isWildcard()) {
+ if (target.getWildcardLevel() > over.getWildcardLevel()) {
return true;
}
}
return false;
}
-
- return entry.equalsIgnoreCase(possibleRegex);
- }
-
- private Map applyRegex(Map input, List possibleNodes) {
- for (Map.Entry e : input.entrySet()) {
- if (!e.getKey().startsWith("r=") && !e.getKey().startsWith("R=")) {
- continue;
- }
-
- final Pattern node = Patterns.compile(e.getKey().substring(2));
- if (node == null) continue;
- possibleNodes.stream()
- .filter(n -> node.matcher(n).matches())
- .filter(n -> !input.containsKey(n))
- .forEach(n -> input.put(n, e.getValue()));
- }
-
- return input;
- }
-
- // TODO Support the "Sponge way" of doing wildcards
- private static Map applyWildcards(Map input, List possibleNodes) {
- SortedMap> wildcards = new TreeMap<>(Collections.reverseOrder());
- for (Map.Entry e : input.entrySet()) {
- if (e.getKey().equals("*") || e.getKey().equals("'*'")) {
- wildcards.put(0, Collections.singletonMap("*", e.getValue()));
- continue;
- }
-
- if (!e.getKey().endsWith(".*")) {
- continue;
- }
-
- final String node = e.getKey().substring(0, e.getKey().length() - 2);
- final String[] parts = Patterns.DOT.split(node);
-
- if (!wildcards.containsKey(parts.length)) {
- wildcards.put(parts.length, new HashMap<>());
- }
-
- wildcards.get(parts.length).put(node, e.getValue());
- }
-
- for (Map.Entry> e : wildcards.entrySet()) {
- if (e.getKey() == 0) {
- // Apply all permissions
- possibleNodes.stream()
- .filter(n -> !input.containsKey(n)) // Don't override existing nodes
- .forEach(n -> input.put(n, e.getValue().get("*")));
- break;
- }
-
- for (Map.Entry wc : e.getValue().entrySet()) {
- possibleNodes.stream()
- .filter(n -> n.startsWith(wc.getKey() + ".")) // Only nodes that match the wildcard are applied
- .filter(n -> !input.containsKey(n)) // Don't override existing nodes
- .forEach(n -> input.put(n, wc.getValue()));
- }
- }
-
- return input;
- }
-
- private void applyShorthandIfEnabled(Map map) {
- if (plugin.getConfiguration().getApplyShorthand()) {
- applyShorthand(map);
- }
- }
-
- private static Map applyShorthand(Map input) {
- for (Map.Entry e : input.entrySet()) {
- if (!Patterns.SHORTHAND_NODE.matcher(e.getKey()).find()) {
- continue;
- }
-
- if (!e.getKey().contains(".")) {
- continue;
- }
-
- String[] parts = Patterns.DOT.split(e.getKey());
- List> nodeParts = new ArrayList<>();
-
- for (String s : parts) {
- if ((!s.startsWith("(") || !s.endsWith(")")) || !s.contains("|")) {
- nodeParts.add(Collections.singleton(s));
- continue;
- }
-
- final String bits = s.substring(1, s.length() - 1);
- nodeParts.add(new HashSet<>(Arrays.asList(Patterns.VERTICAL_BAR.split(bits))));
- }
-
- Set nodes = new HashSet<>();
- for (Set set : nodeParts) {
- final Set newNodes = new HashSet<>();
- if (nodes.isEmpty()) {
- newNodes.addAll(set);
- } else {
- nodes.forEach(str -> newNodes.addAll(set.stream()
- .map(add -> str + "." + add)
- .collect(Collectors.toList()))
- );
- }
- nodes = newNodes;
- }
-
- nodes.stream()
- .filter(n -> !input.containsKey(n)) // Don't override existing nodes
- .forEach(n -> input.put(n, e.getValue()));
- }
-
- return input;
- }
-
- private static String stripTime(String s) {
- if (s.contains("$")) {
- return Patterns.TEMP_DELIMITER.split(s)[0];
- }
- return s;
}
}
diff --git a/common/src/main/java/me/lucko/luckperms/groups/Group.java b/common/src/main/java/me/lucko/luckperms/groups/Group.java
index 286e0bc90..a613062c0 100644
--- a/common/src/main/java/me/lucko/luckperms/groups/Group.java
+++ b/common/src/main/java/me/lucko/luckperms/groups/Group.java
@@ -321,7 +321,7 @@ public class Group extends PermissionHolder implements Identifiable {
*/
private List getGroups(String server, String world, boolean includeGlobal) {
// Call super #getPermissions method, and just sort through those
- Map perms = getPermissions(server, world, null, includeGlobal, null);
+ Map perms = getPermissions(server, world, null, includeGlobal, null, true);
return perms.keySet().stream()
.filter(s -> Patterns.GROUP_MATCH.matcher(s).matches())
.map(s -> Patterns.DOT.split(s, 2)[1])
diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java
index 926084af5..c20c0b652 100644
--- a/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java
+++ b/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java
@@ -28,11 +28,13 @@ import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.constants.Constants;
+import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.Log;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.users.User;
+import me.lucko.luckperms.utils.Node;
import java.io.*;
import java.util.*;
@@ -41,6 +43,8 @@ import java.util.logging.*;
import java.util.logging.Formatter;
import java.util.stream.Collectors;
+import static me.lucko.luckperms.core.PermissionHolder.*;
+
@SuppressWarnings({"ResultOfMethodCallIgnored", "UnnecessaryLocalVariable"})
public class FlatfileDatastore extends Datastore {
private static final String LOG_FORMAT = "%s(%s): [%s] %s(%s) --> %s";
@@ -184,7 +188,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
- for (Map.Entry e : user.getNodes().entrySet()) {
+ for (Map.Entry e : convertToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -209,7 +213,7 @@ public class FlatfileDatastore extends Datastore {
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
- user.getNodes().put(node, b);
+ user.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
@@ -225,7 +229,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
- for (Map.Entry e : user.getNodes().entrySet()) {
+ for (Map.Entry e : convertToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -260,7 +264,7 @@ public class FlatfileDatastore extends Datastore {
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
- user.getNodes().put(node, b);
+ user.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
@@ -291,7 +295,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms");
writer.beginObject();
- for (Map.Entry e : user.getNodes().entrySet()) {
+ for (Map.Entry e : convertToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -319,7 +323,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
- for (Map.Entry e : group.getNodes().entrySet()) {
+ for (Map.Entry e : convertToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
@@ -339,7 +343,7 @@ public class FlatfileDatastore extends Datastore {
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
- group.getNodes().put(node, b);
+ group.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
@@ -369,7 +373,7 @@ public class FlatfileDatastore extends Datastore {
while (reader.hasNext()) {
String node = reader.nextName();
boolean b = reader.nextBoolean();
- group.getNodes().put(node, b);
+ group.getNodes().add(Node.fromSerialisedNode(node, b));
}
reader.endObject();
@@ -411,7 +415,7 @@ public class FlatfileDatastore extends Datastore {
writer.name("name").value(group.getName());
writer.name("perms");
writer.beginObject();
- for (Map.Entry e : group.getNodes().entrySet()) {
+ for (Map.Entry e : convertToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java
index 8fb0ba102..f4ea4b62a 100644
--- a/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java
+++ b/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java
@@ -31,6 +31,7 @@ import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.InsertOneOptions;
import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.api.LogEntry;
+import me.lucko.luckperms.core.PermissionHolder;
import me.lucko.luckperms.data.Log;
import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.groups.GroupManager;
@@ -45,6 +46,8 @@ import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
+import static me.lucko.luckperms.core.PermissionHolder.convertToLegacy;
+
@SuppressWarnings("unchecked")
public class MongoDBDatastore extends Datastore {
@@ -427,7 +430,7 @@ public class MongoDBDatastore extends Datastore {
.append("primaryGroup", user.getPrimaryGroup());
Document perms = new Document();
- for (Map.Entry e : convert(user.getNodes()).entrySet()) {
+ for (Map.Entry e : convert(convertToLegacy(user.getNodes())).entrySet()) {
perms.append(e.getKey(), e.getValue());
}
@@ -439,7 +442,7 @@ public class MongoDBDatastore extends Datastore {
Document main = new Document("_id", group.getName());
Document perms = new Document();
- for (Map.Entry e : convert(group.getNodes()).entrySet()) {
+ for (Map.Entry e : convert(convertToLegacy(group.getNodes())).entrySet()) {
perms.append(e.getKey(), e.getValue());
}
diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java
index 497a346ff..42d675e55 100644
--- a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java
+++ b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java
@@ -109,7 +109,8 @@ abstract class SQLDatastore extends Datastore {
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
user.setName(resultSet.getString("name"));
- user.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
+ Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
+ user.setNodes(nodes);
user.setPrimaryGroup(resultSet.getString("primary_group"));
return true;
}
@@ -193,7 +194,8 @@ abstract class SQLDatastore extends Datastore {
}
});
} else {
- user.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
+ Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
+ user.setNodes(nodes);
user.setPrimaryGroup(resultSet.getString("primary_group"));
if (!resultSet.getString("name").equals(user.getName())) {
@@ -251,7 +253,8 @@ abstract class SQLDatastore extends Datastore {
}
});
} else {
- group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
+ Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
+ group.setNodes(nodes);
}
return success;
}
@@ -273,7 +276,8 @@ abstract class SQLDatastore extends Datastore {
@Override
boolean onResult(ResultSet resultSet) throws SQLException {
if (resultSet.next()) {
- group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
+ Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
+ group.setNodes(nodes);
return true;
}
return false;
@@ -297,7 +301,8 @@ abstract class SQLDatastore extends Datastore {
boolean onResult(ResultSet resultSet) throws SQLException {
while (resultSet.next()) {
Group group = plugin.getGroupManager().make(resultSet.getString("name"));
- group.setNodes(gson.fromJson(resultSet.getString("perms"), NM_TYPE));
+ Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
+ group.setNodes(nodes);
groups.add(group);
}
return true;
diff --git a/common/src/main/java/me/lucko/luckperms/users/User.java b/common/src/main/java/me/lucko/luckperms/users/User.java
index 26fdcc508..8e57f4175 100644
--- a/common/src/main/java/me/lucko/luckperms/users/User.java
+++ b/common/src/main/java/me/lucko/luckperms/users/User.java
@@ -288,7 +288,9 @@ public abstract class User extends PermissionHolder implements Identifiable getGroups(String server, String world, boolean includeGlobal) {
// Call super #getPermissions method, and just sort through those
- Map perms = getPermissions(server, world, null, includeGlobal, null);
+ Map perms = getPermissions(server, world, null, includeGlobal, null, true);
return perms.keySet().stream()
.filter(s -> Patterns.GROUP_MATCH.matcher(s).matches())
.map(s -> Patterns.DOT.split(s, 2)[1])
diff --git a/common/src/main/java/me/lucko/luckperms/utils/Node.java b/common/src/main/java/me/lucko/luckperms/utils/Node.java
new file mode 100644
index 000000000..b7c24e445
--- /dev/null
+++ b/common/src/main/java/me/lucko/luckperms/utils/Node.java
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 2016 Lucko (Luck)
+ *
+ * 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.utils;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.*;
+import me.lucko.luckperms.constants.Patterns;
+
+import java.util.*;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * An immutable permission node
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+@ToString
+@EqualsAndHashCode
+public class Node implements me.lucko.luckperms.api.Node {
+ public static me.lucko.luckperms.api.Node fromSerialisedNode(String s, Boolean b) {
+ return builderFromSerialisedNode(s, b).build();
+ }
+
+ public static me.lucko.luckperms.api.Node.Builder builderFromSerialisedNode(String s, Boolean b) {
+ if (s.contains("/")) {
+ String[] parts = Patterns.SERVER_DELIMITER.split(s, 2);
+ // 0=server(+world) 1=node
+
+ // WORLD SPECIFIC
+ if (parts[0].contains("-")) {
+ String[] serverParts = Patterns.WORLD_DELIMITER.split(parts[0], 2);
+ // 0=server 1=world
+
+ if (parts[1].contains("$")) {
+ String[] tempParts = Patterns.TEMP_DELIMITER.split(parts[1], 2);
+ return new Node.Builder(tempParts[0], true).setServerRaw(serverParts[0]).setWorld(serverParts[1])
+ .setExpiry(Long.parseLong(tempParts[1])).setValue(b);
+ } else {
+ return new Node.Builder(parts[1], true).setServerRaw(serverParts[0]).setWorld(serverParts[1]).setValue(b);
+ }
+
+ } else {
+ // SERVER BUT NOT WORLD SPECIFIC
+ if (parts[1].contains("$")) {
+ String[] tempParts = Patterns.TEMP_DELIMITER.split(parts[1], 2);
+ return new Node.Builder(tempParts[0], true).setServerRaw(parts[0]).setExpiry(Long.parseLong(tempParts[1])).setValue(b);
+ } else {
+ return new Node.Builder(parts[1], true).setServerRaw(parts[0]).setValue(b);
+ }
+ }
+ } else {
+ // NOT SERVER SPECIFIC
+ if (s.contains("$")) {
+ String[] tempParts = Patterns.TEMP_DELIMITER.split(s, 2);
+ return new Node.Builder(tempParts[0], true).setExpiry(Long.parseLong(tempParts[1])).setValue(b);
+ } else {
+ return new Node.Builder(s, true).setValue(b);
+ }
+ }
+ }
+
+ @Getter
+ private final String permission;
+
+ @Getter
+ private Boolean value;
+
+ private String server = null;
+ private String world = null;
+
+ private long expireAt = 0L;
+
+ private final Map extraContexts = new HashMap<>();
+
+ /**
+ * Make an immutable node instance
+ * @param permission the actual permission node
+ * @param value the value (if it's *not* negated)
+ * @param expireAt the time when the node will expire
+ * @param server the server this node applies on
+ * @param world the world this node applies on
+ * @param extraContexts any additional contexts applying to this node
+ */
+ public Node(String permission, boolean value, long expireAt, String server, String world, Map extraContexts) {
+ if (permission == null || permission.equals("")) {
+ throw new IllegalArgumentException("Empty permission");
+ }
+
+ if (server != null && (server.equalsIgnoreCase("global") || server.equals(""))) {
+ server = null;
+ }
+
+ if (world != null && world.equals("")) {
+ world = null;
+ }
+
+ if (world != null && server == null) {
+ server = "global";
+ }
+
+ this.permission = permission;
+ this.value = value;
+ this.expireAt = expireAt;
+ this.server = server;
+ this.world = world;
+
+ if (extraContexts != null) {
+ this.extraContexts.putAll(extraContexts);
+ }
+ }
+
+ public boolean isNegated() {
+ return !value;
+ }
+
+ public Optional getServer() {
+ return Optional.ofNullable(server);
+ }
+
+ public Optional getWorld() {
+ return Optional.ofNullable(world);
+ }
+
+ public boolean isServerSpecific() {
+ return getServer().isPresent();
+ }
+
+ public boolean isWorldSpecific() {
+ return getWorld().isPresent();
+ }
+
+ @Override
+ public boolean shouldApplyOnServer(String server, boolean includeGlobal, boolean applyRegex) {
+ if (server == null || server.equals("")) {
+ return true;
+ }
+
+ if (isServerSpecific()) {
+ if (server.toLowerCase().startsWith("r=") && applyRegex) {
+ Pattern p = Patterns.compile(server.substring(2));
+ if (p == null) {
+ return false;
+ }
+ return p.matcher(this.server).matches();
+ }
+
+ if (server.startsWith("(") && server.endsWith(")") && server.contains("|")) {
+ final String bits = server.substring(1, server.length() - 1);
+ String[] parts = Patterns.VERTICAL_BAR.split(bits);
+
+ for (String s : parts) {
+ if (s.equalsIgnoreCase(this.server)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return this.server.equalsIgnoreCase(server);
+ } else {
+ return includeGlobal;
+ }
+ }
+
+ @Override
+ public boolean shouldApplyOnWorld(String world, boolean includeGlobal, boolean applyRegex) {
+ if (world == null || world.equals("")) {
+ return true;
+ }
+
+ if (isWorldSpecific()) {
+ if (world.toLowerCase().startsWith("r=") && applyRegex) {
+ Pattern p = Patterns.compile(world.substring(2));
+ if (p == null) {
+ return false;
+ }
+ return p.matcher(this.world).matches();
+ }
+
+ if (world.startsWith("(") && world.endsWith(")") && world.contains("|")) {
+ final String bits = world.substring(1, world.length() - 1);
+ String[] parts = Patterns.VERTICAL_BAR.split(bits);
+
+ for (String s : parts) {
+ if (s.equalsIgnoreCase(this.world)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return this.world.equalsIgnoreCase(world);
+ } else {
+ return includeGlobal;
+ }
+ }
+
+ @Override
+ public boolean shouldApplyWithContext(Map context) {
+ if (context == null || context.isEmpty()) {
+ return true;
+ }
+
+ for (Map.Entry c : context.entrySet()) {
+ if (!getExtraContexts().containsKey(c.getKey())) {
+ return false;
+ }
+
+ if (!getExtraContexts().get(c.getKey()).equalsIgnoreCase(c.getValue())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean shouldApplyOnAnyServers(List servers, boolean includeGlobal) {
+ for (String s : servers) {
+ if (shouldApplyOnServer(s, includeGlobal, false)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean shouldApplyOnAnyWorlds(List worlds, boolean includeGlobal) {
+ for (String s : worlds) {
+ if (shouldApplyOnWorld(s, includeGlobal, false)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public List resolveWildcard(List possibleNodes) {
+ if (!isWildcard() || possibleNodes == null) {
+ return Collections.emptyList();
+ }
+
+ String match = getPermission().substring(0, getPermission().length() - 2);
+ return possibleNodes.stream().filter(pn -> pn.startsWith(match)).collect(Collectors.toList());
+ }
+
+ @Override
+ public List resolveShorthand() {
+ if (!Patterns.SHORTHAND_NODE.matcher(getPermission()).find()) {
+ return Collections.emptyList();
+ }
+
+ if (!getPermission().contains(".")) {
+ return Collections.emptyList();
+ }
+
+ String[] parts = Patterns.DOT.split(getPermission());
+ List> nodeParts = new ArrayList<>();
+
+ for (String s : parts) {
+ if ((!s.startsWith("(") || !s.endsWith(")")) || !s.contains("|")) {
+ nodeParts.add(Collections.singleton(s));
+ continue;
+ }
+
+ final String bits = s.substring(1, s.length() - 1);
+ nodeParts.add(new HashSet<>(Arrays.asList(Patterns.VERTICAL_BAR.split(bits))));
+ }
+
+ Set nodes = new HashSet<>();
+ for (Set set : nodeParts) {
+ final Set newNodes = new HashSet<>();
+ if (nodes.isEmpty()) {
+ newNodes.addAll(set);
+ } else {
+ nodes.forEach(str -> newNodes.addAll(set.stream()
+ .map(add -> str + "." + add)
+ .collect(Collectors.toList()))
+ );
+ }
+ nodes = newNodes;
+ }
+
+ return new ArrayList<>(nodes);
+ }
+
+ public boolean isTemporary() {
+ return expireAt != 0L;
+ }
+
+ public boolean isPermanent() {
+ return !isTemporary();
+ }
+
+ public long getExpiryUnixTime(){
+ return expireAt;
+ }
+
+ public Date getExpiry() {
+ return new Date(expireAt * 1000L);
+ }
+
+ public long getSecondsTilExpiry() {
+ return expireAt - (System.currentTimeMillis() / 1000L);
+ }
+
+ public boolean hasExpired() {
+ return expireAt < (System.currentTimeMillis() / 1000L);
+ }
+
+ public Map getExtraContexts() {
+ return ImmutableMap.copyOf(extraContexts);
+ }
+
+ public String toSerializedNode() {
+ StringBuilder builder = new StringBuilder();
+
+ if (server != null) {
+ builder.append(server);
+
+ if (world != null) {
+ builder.append("-").append(world);
+ }
+ builder.append("/");
+ } else {
+ if (world != null) {
+ builder.append("global-").append(world).append("/");
+ }
+ }
+
+ if (!extraContexts.isEmpty()) {
+ builder.append("(");
+ for (Map.Entry entry : extraContexts.entrySet()) {
+ builder.append(entry.getKey()).append("=").append(entry.getValue()).append(",");
+ }
+
+ builder.deleteCharAt(builder.length() - 1);
+ builder.append(")");
+ }
+
+ builder.append(permission);
+
+ if (expireAt != 0L) {
+ builder.append("$").append(expireAt);
+ }
+
+ return builder.toString();
+ }
+
+ @Override
+ public boolean isGroupNode() {
+ return Patterns.GROUP_MATCH.matcher(getPermission()).matches();
+ }
+
+ @Override
+ public String getGroupName() {
+ if (!isGroupNode()) {
+ throw new IllegalStateException("This is not a group node");
+ }
+
+ return getPermission().substring("group.".length());
+ }
+
+ @Override
+ public boolean isWildcard() {
+ return getPermission().endsWith(".*");
+ }
+
+ @Override
+ public int getWildcardLevel() {
+ return (int) getPermission().chars().filter(num -> num == Character.getNumericValue('.')).count();
+ }
+
+ @Override
+ public boolean almostEquals(me.lucko.luckperms.api.Node other) {
+ if (!other.getPermission().equalsIgnoreCase(this.getPermission())) {
+ return false;
+ }
+
+ if (other.getServer().isPresent() != this.getServer().isPresent()) {
+ if (other.getServer().isPresent()) {
+ if (!other.getServer().get().equalsIgnoreCase(this.getServer().get())) {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ if (other.getWorld().isPresent() != this.getWorld().isPresent()) {
+ if (other.getWorld().isPresent()) {
+ if (!other.getWorld().get().equalsIgnoreCase(this.getWorld().get())) {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ if (!other.getExtraContexts().equals(this.getExtraContexts())) {
+ return false;
+ }
+
+ if (other.isTemporary() != this.isTemporary()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public Boolean setValue(Boolean value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getKey() {
+ return getPermission();
+ }
+
+ @RequiredArgsConstructor
+ public static class Builder implements me.lucko.luckperms.api.Node.Builder {
+ private final String permission;
+ private Boolean value = true;
+ private String server = null;
+ private String world = null;
+ private long expireAt = 0L;
+
+ private final Map extraContexts = new HashMap<>();
+
+ Builder(String permission, boolean shouldConvertContexts) {
+ if (!shouldConvertContexts) {
+ this.permission = permission;
+ } else {
+ if (!Patterns.NODE_CONTEXTS.matcher(permission).matches()) {
+ this.permission = permission;
+ } else {
+ String[] contextParts = permission.substring(1).split("\\)", 2);
+ // 0 = context, 1 = node
+ this.permission = contextParts[1];
+
+ for (String s : contextParts[0].split("\\,")) {
+ if (!s.contains("=")) {
+ // Not valid
+ continue;
+ }
+
+ String[] context = s.split("\\=", 2);
+ extraContexts.put(context[0], context[1]);
+ }
+ }
+ }
+ }
+
+ @Override
+ public me.lucko.luckperms.api.Node.Builder setNegated(boolean negated) {
+ value = !negated;
+ return this;
+ }
+
+ @Override
+ public me.lucko.luckperms.api.Node.Builder setValue(boolean value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public me.lucko.luckperms.api.Node.Builder setExpiry(long expireAt) {
+ this.expireAt = expireAt;
+ return this;
+ }
+
+ @Override
+ public me.lucko.luckperms.api.Node.Builder setWorld(String world) {
+ this.world = world;
+ return this;
+ }
+
+ @Override
+ public me.lucko.luckperms.api.Node.Builder setServer(String server) {
+ if (server != null && ArgumentChecker.checkServer(server)) {
+ throw new IllegalArgumentException("Server name invalid.");
+ }
+
+ this.server = server;
+ return this;
+ }
+
+ public me.lucko.luckperms.api.Node.Builder setServerRaw(String server) {
+ this.server = server;
+ return this;
+ }
+
+ @Override
+ public me.lucko.luckperms.api.Node.Builder withExtraContext(@NonNull String key, @NonNull String value) {
+ this.extraContexts.put(key, value);
+ return this;
+ }
+
+ @Override
+ public me.lucko.luckperms.api.Node build() {
+ return new Node(permission, value, expireAt, server, world, extraContexts);
+ }
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index f4a108835..d75a8ca30 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
me.lucko.luckperms
luckperms
- 2.5-SNAPSHOT
+ 2.6-SNAPSHOT
common
api
diff --git a/sponge/pom.xml b/sponge/pom.xml
index 9770299c8..44c49e3b8 100644
--- a/sponge/pom.xml
+++ b/sponge/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 2.5-SNAPSHOT
+ 2.6-SNAPSHOT
4.0.0
diff --git a/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java b/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java
index a30dd1d5b..7f2043385 100644
--- a/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java
+++ b/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java
@@ -153,6 +153,10 @@ public class LuckPermsSubject implements Subject {
@Override
public Map, Map> getAllPermissions() {
+ return null;
+ // TODO
+
+ /*
Map nodes = holder.convertTemporaryPerms();
Map, Map> permissions = new HashMap<>();
@@ -286,6 +290,7 @@ public class LuckPermsSubject implements Subject {
}
return permissions;
+ */
}
@Override