diff --git a/api/pom.xml b/api/pom.xml
index 4d62b889..8e17c0eb 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
4.0.0
diff --git a/api/src/main/java/me/lucko/luckperms/LuckPerms.java b/api/src/main/java/me/lucko/luckperms/LuckPerms.java
index 71de6dc6..e6d71d66 100644
--- a/api/src/main/java/me/lucko/luckperms/LuckPerms.java
+++ b/api/src/main/java/me/lucko/luckperms/LuckPerms.java
@@ -32,49 +32,59 @@ import java.util.Optional;
import javax.annotation.Nonnull;
/**
- * Singleton for the {@link LuckPermsApi}.
+ * Provides static access to the {@link LuckPermsApi}.
*
- *
Ideally, the ServiceManager for the platform should be used to obtain and cache an instance, however, this can be
- * used if you need static access.
+ * Ideally, the ServiceManager for the platform should be used to obtain an instance,
+ * however, this provider can be used if you need static access.
*/
public final class LuckPerms {
- private static LuckPermsApi api = null;
+ private static LuckPermsApi instance = null;
/**
- * Gets an instance of {@link LuckPermsApi}, throwing {@link IllegalStateException} if the API is not loaded.
+ * Gets an instance of the {@link LuckPermsApi},
+ * throwing {@link IllegalStateException} if an instance is not yet loaded.
+ *
+ * Will never return null.
*
* @return an api instance
* @throws IllegalStateException if the api is not loaded
*/
@Nonnull
public static LuckPermsApi getApi() {
- if (api == null) {
+ if (instance == null) {
throw new IllegalStateException("API is not loaded.");
}
- return api;
+ return instance;
}
/**
* Gets an instance of {@link LuckPermsApi}, if it is loaded.
*
- * Unlike {@link LuckPerms#getApi}, this method will not throw an {@link IllegalStateException} if the API is
- * not loaded, rather return an empty {@link Optional}.
+ *
Unlike {@link LuckPerms#getApi}, this method will not throw an
+ * {@link IllegalStateException} if an instance is not yet loaded, rather return
+ * an empty {@link Optional}.
*
* @return an optional api instance
*/
@Nonnull
public static Optional getApiSafe() {
- return Optional.ofNullable(api);
+ return Optional.ofNullable(instance);
}
- /* method used by the implementation to set the singleton instance */
- static void registerProvider(LuckPermsApi luckPermsApi) {
- api = luckPermsApi;
+ /**
+ * Registers an instance of the {@link LuckPermsApi} with this provider.
+ *
+ * @param instance the instance
+ */
+ static void registerProvider(LuckPermsApi instance) {
+ LuckPerms.instance = instance;
}
- /* method used by the implementation to remove any previous instance */
+ /**
+ * Removes the current instance from this provider.
+ */
static void unregisterProvider() {
- api = null;
+ LuckPerms.instance = null;
}
private LuckPerms() {
diff --git a/api/src/main/java/me/lucko/luckperms/api/ChatMetaType.java b/api/src/main/java/me/lucko/luckperms/api/ChatMetaType.java
index 6aca7ab9..27a55761 100644
--- a/api/src/main/java/me/lucko/luckperms/api/ChatMetaType.java
+++ b/api/src/main/java/me/lucko/luckperms/api/ChatMetaType.java
@@ -28,6 +28,7 @@ package me.lucko.luckperms.api;
import com.google.common.base.Preconditions;
import java.util.Map;
+import java.util.Optional;
import javax.annotation.Nonnull;
@@ -104,4 +105,22 @@ public enum ChatMetaType {
@Nonnull
public abstract Map.Entry getEntry(@Nonnull Node node);
+ /**
+ * Parses a ChatMetaType from the given node.
+ *
+ * @param node the node
+ * @return the parsed chat meta type
+ * @since 3.4
+ */
+ @Nonnull
+ public static Optional ofNode(@Nonnull Node node) {
+ if (node.isPrefix()) {
+ return Optional.of(PREFIX);
+ } else if (node.isSuffix()) {
+ return Optional.of(SUFFIX);
+ } else {
+ return Optional.empty();
+ }
+ }
+
}
diff --git a/api/src/main/java/me/lucko/luckperms/api/DataMutateResult.java b/api/src/main/java/me/lucko/luckperms/api/DataMutateResult.java
index 5ff35bff..a5b42114 100644
--- a/api/src/main/java/me/lucko/luckperms/api/DataMutateResult.java
+++ b/api/src/main/java/me/lucko/luckperms/api/DataMutateResult.java
@@ -30,11 +30,29 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.function.Supplier;
+/**
+ * Represents the result of a mutation call.
+ */
public enum DataMutateResult {
+ /**
+ * Indicates the mutation was a success
+ */
SUCCESS(true, null),
+
+ /**
+ * Indicates the mutation failed because the subject already has something
+ */
ALREADY_HAS(false, ObjectAlreadyHasException::new),
+
+ /**
+ * Indicates the mutation failed because the subject lacks something
+ */
LACKS(false, ObjectLacksException::new),
+
+ /**
+ * Indicates the mutation failed
+ */
FAIL(false, RuntimeException::new);
private boolean value;
@@ -51,10 +69,35 @@ public enum DataMutateResult {
}
}
+ /**
+ * Gets a boolean representation of the result.
+ *
+ * @return a boolean representation
+ */
public boolean asBoolean() {
return value;
}
+ /**
+ * Gets if the result indicates a success
+ *
+ * @return if the result indicates a success
+ * @since 3.4
+ */
+ public boolean wasSuccess() {
+ return value;
+ }
+
+ /**
+ * Gets if the result indicates a failure
+ *
+ * @return if the result indicates a failure
+ * @since 3.4
+ */
+ public boolean wasFailure() {
+ return !value;
+ }
+
// allows us to throw checked exceptions without declaring it, as #throwException throws a number of
// exception types.
private static void sneakyThrow(Throwable t) {
diff --git a/api/src/main/java/me/lucko/luckperms/api/Group.java b/api/src/main/java/me/lucko/luckperms/api/Group.java
index 90847c3d..5d02b05b 100644
--- a/api/src/main/java/me/lucko/luckperms/api/Group.java
+++ b/api/src/main/java/me/lucko/luckperms/api/Group.java
@@ -35,7 +35,7 @@ import java.util.OptionalInt;
import javax.annotation.Nonnull;
/**
- * A group which holds permission data.
+ * An inheritable holder of permission data.
*/
public interface Group extends PermissionHolder {
diff --git a/api/src/main/java/me/lucko/luckperms/api/HeldPermission.java b/api/src/main/java/me/lucko/luckperms/api/HeldPermission.java
index 13d8f446..1f170538 100644
--- a/api/src/main/java/me/lucko/luckperms/api/HeldPermission.java
+++ b/api/src/main/java/me/lucko/luckperms/api/HeldPermission.java
@@ -27,13 +27,15 @@ package me.lucko.luckperms.api;
import com.google.common.collect.Multimap;
+import me.lucko.luckperms.api.context.ContextSet;
+
import java.util.Optional;
import java.util.OptionalLong;
import javax.annotation.Nonnull;
/**
- * A relationship between a Holder and a permission
+ * A relationship between a PermissionHolder and a permission
*
* @param the identifier type of the holder
* @since 2.17
@@ -90,10 +92,19 @@ public interface HeldPermission {
* Gets the context for the permission.
*
* @return the context
+ * @deprecated in favour of {@link #getContexts()}.
*/
@Nonnull
+ @Deprecated
Multimap getContext();
+ /**
+ * Gets the extra context for the permission.
+ *
+ * @return the extra context
+ */
+ ContextSet getContexts();
+
/**
* Converts this permission into a Node
*
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 07876b54..91748e69 100644
--- a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java
+++ b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java
@@ -32,104 +32,119 @@ import java.util.Map;
import javax.annotation.Nonnull;
/**
- * Read-only access to the LuckPerms configuration settings
+ * Wrapper around parts of the LuckPerms configuration file
*/
public interface LPConfiguration {
/**
- * Returns the name of this server
+ * Gets the name of this server
+ *
* @return the name of this server
*/
@Nonnull
String getServer();
/**
- * Returns how often a sync task will run in minutes
+ * Gets how often a sync task will run in minutes
+ *
* @return how often a sync task will run in minutes
*/
int getSyncTime();
/**
- * Returns if the users on this server will have their global permissions applied
+ * Gets if the users on this server will have their global permissions applied
+ *
* @return if the users on this server will have their global permissions applied
*/
boolean getIncludeGlobalPerms();
/**
- * Returns if the users on this server will have their global world permissions applied
+ * Gets if the users on this server will have their global world permissions applied
+ *
* @return if the users on this server will have their global world permissions applied
* @since 2.9
*/
boolean getIncludeGlobalWorldPerms();
/**
- * Returns true if the platform is applying global groups
+ * Gets if the platform is applying global groups
+ *
* @return true if the platform is applying global groups
* @since 2.9
*/
boolean getApplyGlobalGroups();
/**
- * Returns true if the platform is applying global world groups
+ * Gets if the platform is applying global world groups
+ *
* @return true if the platform is applying global world groups
* @since 2.9
*/
boolean getApplyGlobalWorldGroups();
/**
- * Returns the online mode setting
+ * Gets the online mode setting
+ *
* @return the online mode setting
*/
boolean getOnlineMode();
/**
- * Returns if LuckPerms is applying wildcard permissions
+ * Gets if LuckPerms is applying wildcard permissions
+ *
* @return if LuckPerms is applying wildcard permissions
*/
boolean getApplyWildcards();
/**
* Returns if LuckPerms is resolving and applying regex permissions
+ *
* @return if LuckPerms is resolving and applying regex permissions
*/
boolean getApplyRegex();
/**
- * Returns if LuckPerms is expanding shorthand permissions
+ * Gets if LuckPerms is expanding shorthand permissions
+ *
* @return if LuckPerms is expanding shorthand permissions
*/
boolean getApplyShorthand();
/**
- * Returns if LuckPerms will send notifications to users when permissions are modified
+ * Gets if LuckPerms will send notifications to users when permissions are modified
+ *
* @return if LuckPerms will send notifications to users when permissions are modified
* @since 2.7
*/
boolean getLogNotify();
/**
- * Returns true if the vanilla op system is enabled
+ * Gets if the vanilla op system is enabled
+ *
* @return true if the vanilla op system is enabled
* @since 2.8
*/
boolean getEnableOps();
/**
- * Returns true if opped players are allowed to use LuckPerms commands
+ * Gets if opped players are allowed to use LuckPerms commands
+ *
* @return true if opped players are allowed to use LuckPerms commands
* @since 2.8
*/
boolean getCommandsAllowOp();
/**
- * Returns true if auto op is enabled
+ * Gets if auto op is enabled
+ *
* @return true if auto op is enabled
* @since 2.9
*/
boolean getAutoOp();
/**
- * Returns the name of the server used within Vault operations
+ * Gets the name of the server used within Vault operations
+ *
* @return the name of the server used within Vault operations
* @since 2.7
*/
@@ -137,35 +152,40 @@ public interface LPConfiguration {
String getVaultServer();
/**
- * Returns true if global permissions should be considered when retrieving meta or player groups
+ * Gets if global permissions should be considered when retrieving meta or player groups
+ *
* @return true if global permissions should be considered when retrieving meta or player groups
* @since 2.7
*/
boolean getVaultIncludeGlobal();
/**
- * Returns the values set for data storage in the configuration
+ * Gets the values set for data storage in the configuration
+ *
* @return the values set for data storage in the configuration
*/
@Nonnull
DatastoreConfiguration getDatastoreConfig();
/**
- * Returns the storage method string from the configuration
+ * Gets the storage method string from the configuration
+ *
* @return the storage method string from the configuration
*/
@Nonnull
String getStorageMethod();
/**
- * Returns true if split storage is enabled
+ * Gets true if split storage is enabled
+ *
* @return true if split storage is enabled
* @since 2.7
*/
boolean getSplitStorage();
/**
- * Returns a map of split storage options
+ * Gets a map of split storage options
+ *
* @return a map of split storage options, where the key is the storage section, and the value is the storage
* method. For example: key = user, value = json
* @since 2.7
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 dc209881..5f641cad 100644
--- a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java
+++ b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java
@@ -25,6 +25,7 @@
package me.lucko.luckperms.api;
+import me.lucko.luckperms.LuckPerms;
import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.event.EventBus;
@@ -38,7 +39,13 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
- * The root API interface for LuckPerms
+ * The LuckPerms API.
+ *
+ * This interface is the base of the entire API package. All API functions
+ * are accessed via this interface.
+ *
+ * An instance can be obtained via {@link LuckPerms#getApi()}, or the platforms
+ * Services Manager.
*/
public interface LuckPermsApi {
diff --git a/api/src/main/java/me/lucko/luckperms/api/MetaUtils.java b/api/src/main/java/me/lucko/luckperms/api/MetaUtils.java
index c73b7969..a3ef2876 100644
--- a/api/src/main/java/me/lucko/luckperms/api/MetaUtils.java
+++ b/api/src/main/java/me/lucko/luckperms/api/MetaUtils.java
@@ -41,6 +41,7 @@ import java.util.Set;
*/
@Deprecated
public class MetaUtils {
+ private static final String[] DELIMS = new String[]{".", "/", "-", "$"};
private static String escapeDelimiters(String s, String... delims) {
for (String delim : delims) {
@@ -68,7 +69,7 @@ public class MetaUtils {
throw new NullPointerException();
}
- return escapeDelimiters(s, ".", "/", "-", "$");
+ return escapeDelimiters(s, DELIMS);
}
/**
@@ -86,7 +87,7 @@ public class MetaUtils {
s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$");
- s = unescapeDelimiters(s, ".", "/", "-", "$");
+ s = unescapeDelimiters(s, DELIMS);
return s;
}
@@ -161,7 +162,7 @@ public class MetaUtils {
node = escapeCharacters(node);
for (Node n : holder.getPermissions()) {
- if (!n.getValue() || !n.isMeta()) continue;
+ if (!n.getValuePrimitive() || !n.isMeta()) continue;
if (!n.shouldApplyOnServer(server, includeGlobal, false)) continue;
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) continue;
@@ -233,7 +234,7 @@ public class MetaUtils {
int priority = Integer.MIN_VALUE;
String meta = null;
for (Node n : holder.getAllNodes(Contexts.allowAll())) {
- if (!n.getValue()) continue;
+ if (!n.getValuePrimitive()) continue;
if (!n.shouldApplyOnServer(server, includeGlobal, false)) continue;
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) continue;
diff --git a/api/src/main/java/me/lucko/luckperms/api/Node.java b/api/src/main/java/me/lucko/luckperms/api/Node.java
index eac4aa8c..20f9b687 100644
--- a/api/src/main/java/me/lucko/luckperms/api/Node.java
+++ b/api/src/main/java/me/lucko/luckperms/api/Node.java
@@ -37,16 +37,18 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
- * An immutable permission node
+ * Represents a permission node.
*
- * Use {@link LuckPermsApi#buildNode(String)} to get an instance.
+ * All implementations of this interface must be immutable.
+ *
+ * Use the {@link NodeFactory} to obtain and construct instances.
*
* @since 2.6
*/
public interface Node extends Map.Entry {
/**
- * Returns the actual permission string
+ * Gets the permission string
*
* @return the actual permission node
*/
@@ -54,36 +56,50 @@ public interface Node extends Map.Entry {
String getPermission();
/**
- * Gets what value the permission is set to. A negated node would return false
.
+ * Gets the value.
+ *
+ * A negated node would return a value of false
.
*
* @return the permission's value
*/
@Override
@Nonnull
- Boolean getValue();
+ default Boolean getValue() {
+ return getValuePrimitive();
+ }
/**
- * Returns the value of this node as a tristate
+ * Gets the value.
+ *
+ * A negated node would return a value of false
.
+ *
+ * @return the permission's value
+ */
+ boolean getValuePrimitive();
+
+ /**
+ * Gets the value of this node as a {@link Tristate}
*
* @return the value of this node as a Tristate
*/
@Nonnull
default Tristate getTristate() {
- return Tristate.fromBoolean(getValue());
+ return Tristate.fromBoolean(getValuePrimitive());
}
/**
- * Returns if the node is negated
+ * Gets if the node is negated
*
* @return true if the node is negated
*/
default boolean isNegated() {
- return !getValue();
+ return !getValuePrimitive();
}
/**
- * If this node is set to override explicitly.
- * This value does not persist across saves, and is therefore only useful for transient nodes
+ * Gets if this node is set to override explicitly.
+ *
+ * This value does not persist across saves, and is therefore only useful for transient nodes
*
* @return true if this node is set to override explicitly
*/
@@ -106,21 +122,21 @@ public interface Node extends Map.Entry {
Optional getWorld();
/**
- * Returns if this node is server specific
+ * Gets if this node is server specific
*
* @return true if this node is server specific
*/
boolean isServerSpecific();
/**
- * Returns if this node is server specific
+ * Gets if this node is server specific
*
* @return true if this node is server specific
*/
boolean isWorldSpecific();
/**
- * Returns if this node applies globally, and has no specific context
+ * Gets if this node applies globally, and therefore has no specific context
*
* @return true if this node applies globally, and has no specific context
* @since 3.1
@@ -128,7 +144,7 @@ public interface Node extends Map.Entry {
boolean appliesGlobally();
/**
- * Returns if this node has any specific context in order to apply.
+ * Gets if this node has any specific context in order for it to apply
*
* @return true if this node has specific context
* @since 3.1
@@ -136,7 +152,7 @@ public interface Node extends Map.Entry {
boolean hasSpecificContext();
/**
- * Returns if this node is able to apply in the given context
+ * Gets if this node is able to apply in the given context
*
* @param includeGlobal if global server values should apply
* @param includeGlobalWorld if global world values should apply
@@ -150,7 +166,7 @@ public interface Node extends Map.Entry {
boolean shouldApply(boolean includeGlobal, boolean includeGlobalWorld, @Nullable String server, @Nullable String world, @Nullable ContextSet context, boolean applyRegex);
/**
- * If this node should apply on a specific server
+ * Gets if this node should apply on a specific server
*
* @param server the name of the server
* @param includeGlobal if global permissions should apply
@@ -160,7 +176,7 @@ public interface Node extends Map.Entry {
boolean shouldApplyOnServer(@Nullable String server, boolean includeGlobal, boolean applyRegex);
/**
- * If this node should apply on a specific world
+ * Gets if this node should apply on a specific world
*
* @param world the name of the world
* @param includeGlobal if global permissions should apply
@@ -170,7 +186,7 @@ public interface Node extends Map.Entry {
boolean shouldApplyOnWorld(@Nullable String world, boolean includeGlobal, boolean applyRegex);
/**
- * If this node should apply in the given context
+ * Gets if this node should apply in the given context
*
* @param context the context key value pairs
* @param worldAndServer if world and server contexts should be checked
@@ -180,7 +196,7 @@ public interface Node extends Map.Entry {
boolean shouldApplyWithContext(@Nonnull ContextSet context, boolean worldAndServer);
/**
- * If this node should apply in the given context
+ * Gets if this node should apply in the given context
*
* @param context the context key value pairs
* @return true if the node should apply
@@ -211,8 +227,10 @@ public interface Node extends Map.Entry {
*
* @param possibleNodes a list of possible permission nodes
* @return a list of permissions that match this wildcard
+ * @deprecated as this is no longer used internally to resolve wildcards
*/
@Nonnull
+ @Deprecated
List resolveWildcard(@Nonnull List possibleNodes);
/**
@@ -224,14 +242,14 @@ public interface Node extends Map.Entry {
List resolveShorthand();
/**
- * Returns if this node will expire in the future
+ * Gets if this node will expire in the future
*
* @return true if this node will expire in the future
*/
boolean isTemporary();
/**
- * Returns if this node will not expire
+ * Gets if this node will not expire
*
* @return true if this node will not expire
*/
@@ -240,7 +258,7 @@ public interface Node extends Map.Entry {
}
/**
- * Returns a unix timestamp in seconds when this node will expire
+ * Gets a unix timestamp in seconds when this node will expire
*
* @return the time in Unix time when this node will expire
* @throws IllegalStateException if the node is not temporary
@@ -248,7 +266,7 @@ public interface Node extends Map.Entry {
long getExpiryUnixTime() throws IllegalStateException;
/**
- * Returns the date when this node will expire
+ * Gets the date when this node will expire
*
* @return the {@link Date} when this node will expire
* @throws IllegalStateException if the node is not temporary
@@ -257,7 +275,7 @@ public interface Node extends Map.Entry {
Date getExpiry() throws IllegalStateException;
/**
- * Return the number of seconds until this permission will expire
+ * Gets the number of seconds until this permission will expire
*
* @return the number of seconds until this permission will expire
* @throws IllegalStateException if the node is not temporary
@@ -265,8 +283,9 @@ public interface Node extends Map.Entry {
long getSecondsTilExpiry() throws IllegalStateException;
/**
- * Return true if the node has expired.
- * This also returns false if the node is not temporary
+ * Gets if the node has expired.
+ *
+ * This also returns false if the node is not temporary.
*
* @return true if this node has expired
*/
@@ -301,14 +320,14 @@ public interface Node extends Map.Entry {
String toSerializedNode();
/**
- * Returns if this is a group node
+ * Gets if this is a group node
*
* @return true if this is a group node
*/
boolean isGroupNode();
/**
- * Returns the name of the group
+ * Gets the name of the group, if this is a group node.
*
* @return the name of the group
* @throws IllegalStateException if this is not a group node. See {@link #isGroupNode()}
@@ -317,7 +336,7 @@ public interface Node extends Map.Entry {
String getGroupName() throws IllegalStateException;
/**
- * Returns if this node is a wildcard node
+ * Gets if this node is a wildcard node
*
* @return true if this node is a wildcard node
*/
@@ -332,7 +351,7 @@ public interface Node extends Map.Entry {
int getWildcardLevel() throws IllegalStateException;
/**
- * Returns if this node is a meta node
+ * Gets if this node is a meta node
*
* @return true if this node is a meta node
*/
@@ -348,7 +367,7 @@ public interface Node extends Map.Entry {
Map.Entry getMeta() throws IllegalStateException;
/**
- * Returns if this node is a prefix node
+ * Gets if this node is a prefix node
*
* @return true if this node is a prefix node
*/
@@ -364,7 +383,7 @@ public interface Node extends Map.Entry {
Map.Entry getPrefix() throws IllegalStateException;
/**
- * Returns if this node is a suffix node
+ * Gets if this node is a suffix node
*
* @return true if this node is a suffix node
*/
diff --git a/api/src/main/java/me/lucko/luckperms/api/NodeFactory.java b/api/src/main/java/me/lucko/luckperms/api/NodeFactory.java
index 5c69a5af..5164e040 100644
--- a/api/src/main/java/me/lucko/luckperms/api/NodeFactory.java
+++ b/api/src/main/java/me/lucko/luckperms/api/NodeFactory.java
@@ -28,27 +28,12 @@ package me.lucko.luckperms.api;
import javax.annotation.Nonnull;
/**
- * Builds {@link Node} instances
+ * Assists with constructing {@link Node} instances.
*
* @since 2.17
*/
public interface NodeFactory {
- /**
- * Creates a node from a serialised node string
- *
- * @param serialisedPermission the serialised permission string
- * @param value the value of the node
- * @return a node instance
- * @throws NullPointerException if the permission is null
- * @deprecated since this format isn't used internally for permissions anymore
- * @see Node#toSerializedNode()
- */
- @Deprecated
- @Nonnull
- Node fromSerialisedNode(@Nonnull String serialisedPermission, boolean value);
-
-
/**
* Creates a new node builder from a given base permission string
*
@@ -69,20 +54,6 @@ public interface NodeFactory {
@Nonnull
Node.Builder newBuilderFromExisting(@Nonnull Node other);
- /**
- * Creates a node builder from a serialised node string
- *
- * @param serialisedPermission the serialised permission string
- * @param value the value of the node
- * @return a node builder instance
- * @throws NullPointerException if the permission is null
- * @deprecated since this format isn't used internally for permissions anymore
- * @see Node#toSerializedNode()
- */
- @Deprecated
- @Nonnull
- Node.Builder newBuilderFromSerialisedNode(@Nonnull String serialisedPermission, boolean value);
-
/**
* Creates a node builder from a group
@@ -141,4 +112,35 @@ public interface NodeFactory {
@Nonnull
Node.Builder makeSuffixNode(int priority, @Nonnull String suffix);
+
+ /**
+ * Creates a node from a serialised node string
+ *
+ * This format is what was previously used in YAML/JSON storage files.
+ *
+ * @param serialisedPermission the serialised permission string
+ * @param value the value of the node
+ * @return a node instance
+ * @throws NullPointerException if the permission is null
+ * @deprecated since this format isn't used internally for permissions anymore
+ * @see Node#toSerializedNode()
+ */
+ @Deprecated
+ @Nonnull
+ Node fromSerialisedNode(@Nonnull String serialisedPermission, boolean value);
+
+ /**
+ * Creates a node builder from a serialised node string
+ *
+ * @param serialisedPermission the serialised permission string
+ * @param value the value of the node
+ * @return a node builder instance
+ * @throws NullPointerException if the permission is null
+ * @deprecated since this format isn't used internally for permissions anymore
+ * @see Node#toSerializedNode()
+ */
+ @Deprecated
+ @Nonnull
+ Node.Builder newBuilderFromSerialisedNode(@Nonnull String serialisedPermission, boolean value);
+
}
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 0d4f977f..8b66c66d 100644
--- a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java
+++ b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java
@@ -43,14 +43,15 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
- * An object capable of holding permissions
+ * An object which holds permissions.
*
- * Any changes made will be lost unless the instance is saved back to the {@link Storage}.
+ * Any changes made to permission holding objects will be lost unless the
+ * instance is saved back to the {@link Storage}.
*/
public interface PermissionHolder {
/**
- * Gets the objects name
+ * Gets the objects name.
*
* {@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} should normally be used instead of
* this method.
@@ -123,7 +124,7 @@ public interface PermissionHolder {
SortedSet extends Node> getPermissions();
/**
- * Similar to {@link #getPermissions()}, except without transient permissions
+ * Similar to {@link #getPermissions()}, except without transient permissions.
*
* Unlike transient permissions, enduring permissions will be saved to storage, and exist after the session.
*
@@ -134,7 +135,7 @@ public interface PermissionHolder {
Set extends Node> getEnduringPermissions();
/**
- * Similar to {@link #getPermissions()}, except without enduring permissions
+ * Similar to {@link #getPermissions()}, except without enduring permissions.
*
* Transient permissions only exist for the duration of the session.
*
diff --git a/api/src/main/java/me/lucko/luckperms/api/PlatformType.java b/api/src/main/java/me/lucko/luckperms/api/PlatformType.java
index 5d74d08a..3a9f1dd2 100644
--- a/api/src/main/java/me/lucko/luckperms/api/PlatformType.java
+++ b/api/src/main/java/me/lucko/luckperms/api/PlatformType.java
@@ -28,7 +28,7 @@ package me.lucko.luckperms.api;
import javax.annotation.Nonnull;
/**
- * The platforms which LuckPerms can run on
+ * Represents a type of platform which LuckPerms can run on.
*
* @since 2.7
*/
@@ -44,6 +44,11 @@ public enum PlatformType {
this.friendlyName = friendlyName;
}
+ /**
+ * Gets a readable name for the platform type.
+ *
+ * @return a readable name
+ */
@Nonnull
public String getFriendlyName() {
return friendlyName;
diff --git a/api/src/main/java/me/lucko/luckperms/api/Storage.java b/api/src/main/java/me/lucko/luckperms/api/Storage.java
index 1bdb6ff4..2b8cd6fe 100644
--- a/api/src/main/java/me/lucko/luckperms/api/Storage.java
+++ b/api/src/main/java/me/lucko/luckperms/api/Storage.java
@@ -36,14 +36,14 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
- * A means of loading and saving data to/from the Storage provider.
+ * A means of loading and saving permission data to/from the backend.
*
- * All methods return {@link CompletableFuture}s, which will be populated with the result once the data has been
- * loaded asynchronously. Care should be taken when using the methods to ensure that the main server thread is not
+ *
All blocking methods return {@link CompletableFuture}s, which will be populated with the result once the data has been
+ * loaded/saved asynchronously. Care should be taken when using such methods to ensure that the main server thread is not
* blocked.
*
* Methods such as {@link CompletableFuture#get()} and equivalent should not be called on the main
- * server thread. If you need to use the result of these operations on the main server thread, please register a
+ * server thread. If you need to use the result of these operations on the main server thread, register a
* callback using {@link CompletableFuture#thenAcceptAsync(Consumer, Executor)} and {@link #getSyncExecutor()}.
*
* @since 2.14
@@ -59,7 +59,7 @@ public interface Storage {
String getName();
/**
- * Return whether the storage instance is allowing logins on the platform.
+ * Gets whether the storage instance is allowing logins on the platform.
*
* @return true if logins are enabled
*/
@@ -144,7 +144,8 @@ public interface Storage {
/**
* Gets a set all "unique" user UUIDs.
- * "Unique" meaning the user isn't just a member of the "default" group.
+ *
+ * "Unique" meaning the user isn't just a member of the "default" group.
*
* @return a set of uuids, or null if the operation failed.
*/
diff --git a/api/src/main/java/me/lucko/luckperms/api/Track.java b/api/src/main/java/me/lucko/luckperms/api/Track.java
index a6f9ddf6..aa26ecb6 100644
--- a/api/src/main/java/me/lucko/luckperms/api/Track.java
+++ b/api/src/main/java/me/lucko/luckperms/api/Track.java
@@ -34,21 +34,24 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
- * An ordered collection of groups for easy promotions and demotions
+ * An ordered chain of {@link Group}s.
*/
public interface Track {
/**
* Gets the name of this track
+ *
* @return the name of this track
*/
@Nonnull
String getName();
/**
- * Gets an ordered list of the groups on this track
+ * Gets a list of the groups on this track
*
- * Index 0 is the first/lowest group in (or start of) the track
+ * Index 0 is the first/lowest group in (or start of) the track.
+ *
+ * The returned collection is immutable, and cannot be modified.
*
* @return an ordered {@link List} of the groups on this track
*/
diff --git a/api/src/main/java/me/lucko/luckperms/api/Tristate.java b/api/src/main/java/me/lucko/luckperms/api/Tristate.java
index 587b1c5e..34016e6f 100644
--- a/api/src/main/java/me/lucko/luckperms/api/Tristate.java
+++ b/api/src/main/java/me/lucko/luckperms/api/Tristate.java
@@ -28,37 +28,42 @@ package me.lucko.luckperms.api;
import javax.annotation.Nonnull;
/**
- * Represents a permission setting.
+ * Represents three different states of a setting.
*
- * Consider a value of {@link #TRUE} to be a positive setting, {@link #FALSE} to be a "negated" setting,
- * and a value of {@link #UNDEFINED} to be a non-existent value.
+ * Possible values:
+ *
+ *
+ * - {@link #TRUE} - a positive setting
+ * - {@link #FALSE} - a negative (negated) setting
+ * - {@link #UNDEFINED} - a non-existent setting
+ *
*/
public enum Tristate {
/**
- * A value indicating a holder has a permission set.
+ * A value indicating a positive setting
*/
TRUE(true),
/**
- * A value indicating a holder has a negated value for a permission.
+ * A value indicating a negative (negated) setting
*/
FALSE(false),
/**
- * A value indicating a holder doesn't have a value for a permission set.
+ * A value indicating a non-existent setting
*/
UNDEFINED(false);
/**
- * Converts from {@link Boolean} a boolean
+ * Returns a {@link Tristate} from a boolean
*
- * @param b the boolean
+ * @param val the boolean value
* @return {@link #TRUE} or {@link #FALSE}, depending on the value of the boolean.
*/
@Nonnull
- public static Tristate fromBoolean(boolean b) {
- return b ? TRUE : FALSE;
+ public static Tristate fromBoolean(boolean val) {
+ return val ? TRUE : FALSE;
}
private final boolean booleanValue;
diff --git a/api/src/main/java/me/lucko/luckperms/api/User.java b/api/src/main/java/me/lucko/luckperms/api/User.java
index 752003cf..bf4c1d66 100644
--- a/api/src/main/java/me/lucko/luckperms/api/User.java
+++ b/api/src/main/java/me/lucko/luckperms/api/User.java
@@ -38,7 +38,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
- * A player holding permission data
+ * A player which holds permission data.
*/
public interface User extends PermissionHolder {
@@ -51,7 +51,9 @@ public interface User extends PermissionHolder {
UUID getUuid();
/**
- * Gets the users username, or null if no username is associated with this user
+ * Gets the users username
+ *
+ * Returns null if no username is associated with this user.
*
* @return the users username
*/
@@ -96,6 +98,16 @@ public interface User extends PermissionHolder {
@Nonnull
UserData getCachedData();
+ /**
+ * Pre-calculates some values in the user's data cache.
+ *
+ * Is it not necessary to call this method before
+ * using {@link #getCachedData()}.
+ *
+ * @since 2.17
+ */
+ void setupDataCache();
+
/**
* Check to see if the user is a direct member of a group
*
@@ -128,16 +140,6 @@ public interface User extends PermissionHolder {
@Nonnull
Optional getUserDataCache();
- /**
- * Pre-calculates some values in the user's data cache.
- *
- * Is it not necessary to call this method before
- * using {@link #getCachedData()}.
- *
- * @since 2.17
- */
- void setupDataCache();
-
/**
* Check to see if a user is a member of a group on a specific server
*
diff --git a/api/src/main/java/me/lucko/luckperms/api/UuidCache.java b/api/src/main/java/me/lucko/luckperms/api/UuidCache.java
index d792c517..d637b276 100644
--- a/api/src/main/java/me/lucko/luckperms/api/UuidCache.java
+++ b/api/src/main/java/me/lucko/luckperms/api/UuidCache.java
@@ -32,34 +32,34 @@ import javax.annotation.Nonnull;
/**
* A UUID cache for online users, between external Mojang UUIDs, and internal LuckPerms UUIDs.
*
- * This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode
- * servers or mixed offline mode and online mode servers. Platforms running in offline mode generate a UUID for a
- * user when they first join the server, but this UUID will then not be consistent across the network. LuckPerms will
- * instead check the datastore cache, to get a UUID for a user that is consistent across an entire network.
+ * A user's internal LuckPerms UUID is always the same as their Mojang one,
+ * unless use-server-uuids
is disabled.
*
- * If you want to get a user object from the Storage using the api on a server in offline mode, you will need to use
- * this cache, OR use Storage#getUUID, for users that are not online.
+ * When this setting is disabled, this cache becomes active, and allows you to convert
+ * between 'internal' and 'server provided' uuids.
*
* This is only effective for online players. Use {@link Storage#getUUID(String)} for offline players.
*/
public interface UuidCache {
/**
- * Gets a users internal "LuckPerms" UUID, from the one given by the server.
+ * Gets a users "internal" LuckPerms UUID, from the one given by the server.
*
- * @param external the UUID assigned by the server, through Player#getUniqueId or ProxiedPlayer#getUniqueId
+ * When use-server-uuids
is true, this returns the same UUID instance.
+ *
+ * @param mojangUuid the UUID assigned by the server, through Player#getUniqueId
or ProxiedPlayer#getUniqueId
* @return the corresponding internal UUID
*/
@Nonnull
- UUID getUUID(@Nonnull UUID external);
+ UUID getUUID(@Nonnull UUID mojangUuid);
/**
- * Gets a users external, server assigned or Mojang assigned unique id, from the internal one used within LuckPerms.
+ * Gets a users "external", server assigned unique id, from the internal one used within LuckPerms.
*
- * @param internal the UUID used within LuckPerms, through User#getUuid
+ * @param internalUuid the UUID used within LuckPerms, through User#getUuid
* @return the corresponding external UUID
*/
@Nonnull
- UUID getExternalUUID(@Nonnull UUID internal);
+ UUID getExternalUUID(@Nonnull UUID internalUuid);
}
diff --git a/api/src/main/java/me/lucko/luckperms/api/caching/MetaContexts.java b/api/src/main/java/me/lucko/luckperms/api/caching/MetaContexts.java
index baf2f3fa..9538ec97 100644
--- a/api/src/main/java/me/lucko/luckperms/api/caching/MetaContexts.java
+++ b/api/src/main/java/me/lucko/luckperms/api/caching/MetaContexts.java
@@ -33,8 +33,10 @@ import me.lucko.luckperms.api.metastacking.MetaStackDefinition;
import javax.annotation.Nonnull;
/**
- * Represents the context for a meta lookup, consisting of a standard {@link Contexts} element, plus options to define how
- * the meta stack should be constructed.
+ * Represents the context for a meta lookup.
+ *
+ * Consisting of a standard {@link Contexts} element, plus options to define how
+ * the meta stack should be constructed.
*
* @since 3.2
*/
diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java
index 1b825064..0435be75 100644
--- a/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java
+++ b/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java
@@ -35,7 +35,13 @@ import java.util.Set;
import javax.annotation.Nonnull;
/**
- * Holder of contexts.
+ * A set of context pairs.
+ *
+ * You can think of ContextSets as a wrapped Multimap<String, String>
.
+ * Each key can be mapped to multiple values.
+ *
+ * Keys are automatically converted to lowercase when added, and are therefore
+ * case-insensitive. Values however are not.
*
* Implementations may be either mutable or immutable.
*
@@ -72,18 +78,6 @@ public interface ContextSet {
return ImmutableContextSet.of(key1, value1, key2, value2);
}
- /**
- * Creates an ImmutableContextSet from an existing map
- *
- * @param map the map to copy from
- * @return a new ImmutableContextSet representing the pairs from the map
- * @throws NullPointerException if the map is null
- */
- @Nonnull
- static ImmutableContextSet fromMap(@Nonnull Map map) {
- return ImmutableContextSet.fromMap(map);
- }
-
/**
* Creates an ImmutableContextSet from an existing iterable of Map Entries
*
@@ -96,6 +90,18 @@ public interface ContextSet {
return ImmutableContextSet.fromEntries(iterable);
}
+ /**
+ * Creates an ImmutableContextSet from an existing map
+ *
+ * @param map the map to copy from
+ * @return a new ImmutableContextSet representing the pairs from the map
+ * @throws NullPointerException if the map is null
+ */
+ @Nonnull
+ static ImmutableContextSet fromMap(@Nonnull Map map) {
+ return ImmutableContextSet.fromMap(map);
+ }
+
/**
* Creates an ImmutableContextSet from an existing multimap
*
@@ -133,7 +139,7 @@ public interface ContextSet {
}
/**
- * Check to see if this set is in an immutable form
+ * Gets if this set is in an immutable form
*
* @return true if the set is immutable
*/
@@ -167,12 +173,14 @@ public interface ContextSet {
/**
* Converts this ContextSet to an immutable {@link Map}
*
- * NOTE: Use of this method may result in data being lost. ContextSets can contain lots of different values for
+ * IMPORTANT: Use of this method may result in data being lost. ContextSets can contain lots of different values for
* one key.
*
* @return an immutable map
+ * @deprecated because the resultant map may not contain all data in the ContextSet
*/
@Nonnull
+ @Deprecated
Map toMap();
/**
@@ -225,6 +233,19 @@ public interface ContextSet {
*/
boolean has(@Nonnull String key, @Nonnull String value);
+ /**
+ * Check if thr set contains a given key mapped to a given value
+ *
+ * @param entry the entry to look for
+ * @return true if the set contains the KV pair
+ * @throws NullPointerException if the key or value is null
+ * @since 3.4
+ */
+ default boolean has(@Nonnull Map.Entry entry) {
+ Preconditions.checkNotNull(entry, "entry");
+ return has(entry.getKey(), entry.getValue());
+ }
+
/**
* Same as {@link #has(String, String)}, except ignores the case of the value.
*
@@ -235,6 +256,19 @@ public interface ContextSet {
*/
boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value);
+ /**
+ * Same as {@link #has(Map.Entry)}, except ignores the case of the value.
+ *
+ * @param entry the entry to look for
+ * @return true if the set contains the KV pair
+ * @throws NullPointerException if the key or value is null
+ * @since 3.4
+ */
+ default boolean hasIgnoreCase(@Nonnull Map.Entry entry) {
+ Preconditions.checkNotNull(entry, "entry");
+ return hasIgnoreCase(entry.getKey(), entry.getValue());
+ }
+
/**
* Checks to see if all entries in this context set are also included in another set.
*
@@ -243,6 +277,18 @@ public interface ContextSet {
* @since 3.1
*/
default boolean isSatisfiedBy(@Nonnull ContextSet other) {
+ return isSatisfiedBy(other, true);
+ }
+
+ /**
+ * Checks to see if all entries in this context set are also included in another set.
+ *
+ * @param other the other set to check
+ * @param caseSensitive if the lookup should be case sensitive. see {@link #has(Map.Entry)} and {@link #hasIgnoreCase(Map.Entry)}.
+ * @return true if all entries in this set are also in the other set
+ * @since 3.4
+ */
+ default boolean isSatisfiedBy(@Nonnull ContextSet other, boolean caseSensitive) {
Preconditions.checkNotNull(other, "other");
if (this.isEmpty()) {
// this is empty, so is therefore always satisfied.
@@ -256,8 +302,14 @@ public interface ContextSet {
} else {
// neither are empty, we need to compare the individual entries
for (Map.Entry pair : toSet()) {
- if (!other.has(pair.getKey(), pair.getValue())) {
- return false;
+ if (caseSensitive) {
+ if (!other.has(pair)) {
+ return false;
+ }
+ } else {
+ if (!other.hasIgnoreCase(pair)) {
+ return false;
+ }
}
}
diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java
index 2ce13b72..8344daf5 100644
--- a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java
+++ b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java
@@ -25,12 +25,10 @@
package me.lucko.luckperms.api.context;
-import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
-import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.Map;
@@ -38,9 +36,14 @@ import java.util.Set;
import javax.annotation.Nonnull;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* An immutable implementation of {@link ContextSet}.
*
+ * On construction, all keys/values are {@link String#intern()}ed, in order to increase
+ * comparison speed.
+ *
* @since 2.16
*/
public final class ImmutableContextSet implements ContextSet {
@@ -56,9 +59,10 @@ public final class ImmutableContextSet implements ContextSet {
*/
@Nonnull
public static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
- Preconditions.checkNotNull(key, "key");
- Preconditions.checkNotNull(value, "value");
- return new ImmutableContextSet(ImmutableSetMultimap.of(key.toLowerCase(), value));
+ return new ImmutableContextSet(ImmutableSetMultimap.of(
+ checkNotNull(key, "key").toLowerCase().intern(),
+ checkNotNull(value, "value").intern()
+ ));
}
/**
@@ -74,30 +78,12 @@ public final class ImmutableContextSet implements ContextSet {
*/
@Nonnull
public static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) {
- Preconditions.checkNotNull(key1, "key1");
- Preconditions.checkNotNull(value1, "value1");
- Preconditions.checkNotNull(key2, "key2");
- Preconditions.checkNotNull(value2, "value2");
- return new ImmutableContextSet(ImmutableSetMultimap.of(key1.toLowerCase(), value1, key2.toLowerCase(), value2));
- }
-
- /**
- * Creates an ImmutableContextSet from an existing map
- *
- * @param map the map to copy from
- * @return a new ImmutableContextSet representing the pairs from the map
- * @throws NullPointerException if the map is null
- */
- @Nonnull
- public static ImmutableContextSet fromMap(@Nonnull Map map) {
- Preconditions.checkNotNull(map, "map");
-
- ImmutableSetMultimap.Builder b = ImmutableSetMultimap.builder();
- for (Map.Entry e : map.entrySet()) {
- b.put(e.getKey().toLowerCase(), e.getValue());
- }
-
- return new ImmutableContextSet(b.build());
+ return new ImmutableContextSet(ImmutableSetMultimap.of(
+ checkNotNull(key1, "key1").toLowerCase().intern(),
+ checkNotNull(value1, "value1").intern(),
+ checkNotNull(key2, "key2").toLowerCase().intern(),
+ checkNotNull(value2, "value2").intern()
+ ));
}
/**
@@ -109,8 +95,26 @@ public final class ImmutableContextSet implements ContextSet {
*/
@Nonnull
public static ImmutableContextSet fromEntries(@Nonnull Iterable extends Map.Entry> iterable) {
- Preconditions.checkNotNull(iterable, "iterable");
- return MutableContextSet.fromEntries(iterable).makeImmutable();
+ checkNotNull(iterable, "iterable");
+
+ ImmutableSetMultimap.Builder b = ImmutableSetMultimap.builder();
+ for (Map.Entry e : iterable) {
+ b.put(checkNotNull(e.getKey()).toLowerCase().intern(), checkNotNull(e.getValue()).intern());
+ }
+
+ return new ImmutableContextSet(b.build());
+ }
+
+ /**
+ * Creates an ImmutableContextSet from an existing map
+ *
+ * @param map the map to copy from
+ * @return a new ImmutableContextSet representing the pairs from the map
+ * @throws NullPointerException if the map is null
+ */
+ @Nonnull
+ public static ImmutableContextSet fromMap(@Nonnull Map map) {
+ return fromEntries(checkNotNull(map, "map").entrySet());
}
/**
@@ -122,8 +126,7 @@ public final class ImmutableContextSet implements ContextSet {
*/
@Nonnull
public static ImmutableContextSet fromMultimap(@Nonnull Multimap multimap) {
- Preconditions.checkNotNull(multimap, "multimap");
- return MutableContextSet.fromMultimap(multimap).makeImmutable();
+ return fromEntries(checkNotNull(multimap, "multimap").entries());
}
/**
@@ -136,7 +139,7 @@ public final class ImmutableContextSet implements ContextSet {
*/
@Nonnull
public static ImmutableContextSet fromSet(@Nonnull ContextSet contextSet) {
- return Preconditions.checkNotNull(contextSet, "contextSet").makeImmutable();
+ return checkNotNull(contextSet, "contextSet").makeImmutable();
}
/**
@@ -149,10 +152,10 @@ public final class ImmutableContextSet implements ContextSet {
return EMPTY;
}
- private final SetMultimap map;
+ private final ImmutableSetMultimap map;
- ImmutableContextSet(Multimap contexts) {
- this.map = ImmutableSetMultimap.copyOf(contexts);
+ ImmutableContextSet(ImmutableSetMultimap contexts) {
+ this.map = contexts;
}
@Override
@@ -176,7 +179,7 @@ public final class ImmutableContextSet implements ContextSet {
@Override
@Nonnull
public Set> toSet() {
- return ImmutableSet.copyOf(map.entries());
+ return map.entries();
}
@Override
@@ -197,36 +200,35 @@ public final class ImmutableContextSet implements ContextSet {
}
@Override
+ @Nonnull
public boolean containsKey(@Nonnull String key) {
- Preconditions.checkNotNull(key, "key");
- return map.containsKey(key);
+ return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
}
@Override
+ @Nonnull
public Set getValues(@Nonnull String key) {
- Preconditions.checkNotNull(key, "key");
- Collection c = map.get(key);
- return c != null && !c.isEmpty() ? ImmutableSet.copyOf(c) : ImmutableSet.of();
+ Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern());
+ return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of();
}
@Override
+ @Nonnull
public boolean has(@Nonnull String key, @Nonnull String value) {
- Preconditions.checkNotNull(key, "key");
- Preconditions.checkNotNull(value, "value");
- return map.containsEntry(key, value);
+ return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
}
@Override
+ @Nonnull
public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
- Preconditions.checkNotNull(key, "key");
- Preconditions.checkNotNull(value, "value");
+ value = checkNotNull(value, "value").intern();
+ Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern());
- Collection c = map.get(key);
- if (c == null || c.isEmpty()) {
+ if (values == null || values.isEmpty()) {
return false;
}
- for (String val : c) {
+ for (String val : values) {
if (val.equalsIgnoreCase(value)) {
return true;
}
@@ -250,6 +252,11 @@ public final class ImmutableContextSet implements ContextSet {
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
+ // saves on copying the multimap
+ if (other instanceof MutableContextSet) {
+ return other.equals(this);
+ }
+
final Multimap thisContexts = this.toMultimap();
final Multimap otherContexts = other.toMultimap();
return thisContexts.equals(otherContexts);
@@ -257,7 +264,7 @@ public final class ImmutableContextSet implements ContextSet {
@Override
public int hashCode() {
- return 59 + (this.map == null ? 43 : this.map.hashCode());
+ return map.hashCode();
}
@Override
diff --git a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java
index 544f56bf..5d39ffae 100644
--- a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java
+++ b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java
@@ -40,9 +40,14 @@ import java.util.Set;
import javax.annotation.Nonnull;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* A mutable implementation of {@link ContextSet}.
*
+ * On construction, all keys/values are {@link String#intern()}ed, in order to increase
+ * comparison speed.
+ *
* @since 2.16
*/
public final class MutableContextSet implements ContextSet {
@@ -57,9 +62,9 @@ public final class MutableContextSet implements ContextSet {
*/
@Nonnull
public static MutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
- Preconditions.checkNotNull(key, "key");
- Preconditions.checkNotNull(value, "value");
- MutableContextSet set = new MutableContextSet();
+ checkNotNull(key, "key");
+ checkNotNull(value, "value");
+ MutableContextSet set = MutableContextSet.create();
set.add(key, value);
return set;
}
@@ -77,27 +82,13 @@ public final class MutableContextSet implements ContextSet {
*/
@Nonnull
public static MutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) {
- Preconditions.checkNotNull(key1, "key1");
- Preconditions.checkNotNull(value1, "value1");
- Preconditions.checkNotNull(key2, "key2");
- Preconditions.checkNotNull(value2, "value2");
- MutableContextSet ret = singleton(key1, value1);
- ret.add(key2, value2);
- return ret;
- }
-
- /**
- * Creates a MutableContextSet from an existing map
- *
- * @param map the map to copy from
- * @return a new MutableContextSet representing the pairs from the map
- * @throws NullPointerException if the map is null
- */
- @Nonnull
- public static MutableContextSet fromMap(@Nonnull Map map) {
- Preconditions.checkNotNull(map, "map");
- MutableContextSet set = new MutableContextSet();
- set.addAll(map);
+ checkNotNull(key1, "key1");
+ checkNotNull(value1, "value1");
+ checkNotNull(key2, "key2");
+ checkNotNull(value2, "value2");
+ MutableContextSet set = MutableContextSet.create();
+ set.add(key1, value1);
+ set.add(key2, value2);
return set;
}
@@ -110,12 +101,27 @@ public final class MutableContextSet implements ContextSet {
*/
@Nonnull
public static MutableContextSet fromEntries(@Nonnull Iterable extends Map.Entry> iterable) {
- Preconditions.checkNotNull(iterable, "iterable");
- MutableContextSet set = new MutableContextSet();
+ checkNotNull(iterable, "iterable");
+ MutableContextSet set = MutableContextSet.create();
set.addAll(iterable);
return set;
}
+ /**
+ * Creates a MutableContextSet from an existing map
+ *
+ * @param map the map to copy from
+ * @return a new MutableContextSet representing the pairs from the map
+ * @throws NullPointerException if the map is null
+ */
+ @Nonnull
+ public static MutableContextSet fromMap(@Nonnull Map map) {
+ checkNotNull(map, "map");
+ MutableContextSet set = MutableContextSet.create();
+ set.addAll(map);
+ return set;
+ }
+
/**
* Creates a MutableContextSet from an existing multimap
*
@@ -125,8 +131,10 @@ public final class MutableContextSet implements ContextSet {
*/
@Nonnull
public static MutableContextSet fromMultimap(@Nonnull Multimap multimap) {
- Preconditions.checkNotNull(multimap, "multimap");
- return fromEntries(multimap.entries());
+ checkNotNull(multimap, "multimap");
+ MutableContextSet set = MutableContextSet.create();
+ set.addAll(multimap);
+ return set;
}
/**
@@ -161,8 +169,8 @@ public final class MutableContextSet implements ContextSet {
this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create());
}
- private MutableContextSet(Multimap contexts) {
- this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(contexts));
+ private MutableContextSet(MutableContextSet other) {
+ this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other.map));
}
@Override
@@ -170,21 +178,25 @@ public final class MutableContextSet implements ContextSet {
return false;
}
+ @Nonnull
@Override
public ImmutableContextSet makeImmutable() {
- return new ImmutableContextSet(map);
+ return new ImmutableContextSet(ImmutableSetMultimap.copyOf(map));
}
+ @Nonnull
@Override
public MutableContextSet mutableCopy() {
- return new MutableContextSet(map);
+ return new MutableContextSet(this);
}
+ @Nonnull
@Override
public Set> toSet() {
return ImmutableSet.copyOf(map.entries());
}
+ @Nonnull
@Override
public Map toMap() {
ImmutableMap.Builder m = ImmutableMap.builder();
@@ -195,57 +207,42 @@ public final class MutableContextSet implements ContextSet {
return m.build();
}
+ @Nonnull
@Override
public Multimap toMultimap() {
return ImmutableSetMultimap.copyOf(map);
}
+ @Nonnull
@Override
- public boolean containsKey(String key) {
- if (key == null) {
- throw new NullPointerException("key");
- }
-
- return map.containsKey(key);
+ public boolean containsKey(@Nonnull String key) {
+ return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
}
+ @Nonnull
@Override
- public Set getValues(String key) {
- if (key == null) {
- throw new NullPointerException("key");
- }
-
- Collection c = map.get(key);
- return c != null && !c.isEmpty() ? ImmutableSet.copyOf(c) : ImmutableSet.of();
+ public Set getValues(@Nonnull String key) {
+ Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern());
+ return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of();
}
+ @Nonnull
@Override
- public boolean has(String key, String value) {
- if (key == null) {
- throw new NullPointerException("key");
- }
- if (value == null) {
- throw new NullPointerException("value");
- }
-
- return map.containsEntry(key, value);
+ public boolean has(@Nonnull String key, @Nonnull String value) {
+ return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
}
+ @Nonnull
@Override
- public boolean hasIgnoreCase(String key, String value) {
- if (key == null) {
- throw new NullPointerException("key");
- }
- if (value == null) {
- throw new NullPointerException("value");
- }
+ public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
+ value = checkNotNull(value, "value").intern();
+ Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern());
- Collection c = map.get(key);
- if (c == null || c.isEmpty()) {
+ if (values == null || values.isEmpty()) {
return false;
}
- for (String val : c) {
+ for (String val : values) {
if (val.equalsIgnoreCase(value)) {
return true;
}
@@ -270,15 +267,8 @@ public final class MutableContextSet implements ContextSet {
* @param value the value to add
* @throws NullPointerException if the key or value is null
*/
- public void add(String key, String value) {
- if (key == null) {
- throw new NullPointerException("key");
- }
- if (value == null) {
- throw new NullPointerException("value");
- }
-
- map.put(key.toLowerCase(), value);
+ public void add(@Nonnull String key, @Nonnull String value) {
+ map.put(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
}
/**
@@ -287,12 +277,9 @@ public final class MutableContextSet implements ContextSet {
* @param entry the entry to add
* @throws NullPointerException if the entry is null
*/
- public void add(Map.Entry entry) {
- if (entry == null) {
- throw new NullPointerException("context");
- }
-
- map.put(entry.getKey().toLowerCase(), entry.getValue());
+ public void add(@Nonnull Map.Entry entry) {
+ checkNotNull(entry, "entry");
+ add(entry.getKey(), entry.getValue());
}
/**
@@ -301,13 +288,9 @@ public final class MutableContextSet implements ContextSet {
* @param iterable an iterable of key value context pairs
* @throws NullPointerException if iterable is null
*/
- public void addAll(Iterable extends Map.Entry> iterable) {
- if (iterable == null) {
- throw new NullPointerException("iterable");
- }
-
- for (Map.Entry e : iterable) {
- this.map.put(e.getKey().toLowerCase(), e.getValue());
+ public void addAll(@Nonnull Iterable extends Map.Entry> iterable) {
+ for (Map.Entry e : checkNotNull(iterable, "iterable")) {
+ add(e);
}
}
@@ -317,11 +300,19 @@ public final class MutableContextSet implements ContextSet {
* @param map the map to add from
* @throws NullPointerException if the map is null
*/
- public void addAll(Map map) {
- if (map == null) {
- throw new NullPointerException("map");
- }
- addAll(map.entrySet());
+ public void addAll(@Nonnull Map map) {
+ addAll(checkNotNull(map, "map").entrySet());
+ }
+
+ /**
+ * Adds the entries of a multimap to the set
+ *
+ * @param multimap the multimap to add from
+ * @throws NullPointerException if the map is null
+ * @since 3.4
+ */
+ public void addAll(@Nonnull Multimap multimap) {
+ addAll(checkNotNull(multimap, "multimap").entries());
}
/**
@@ -330,12 +321,14 @@ public final class MutableContextSet implements ContextSet {
* @param contextSet the set to add from
* @throws NullPointerException if the contextSet is null
*/
- public void addAll(ContextSet contextSet) {
- if (contextSet == null) {
- throw new NullPointerException("contextSet");
+ public void addAll(@Nonnull ContextSet contextSet) {
+ checkNotNull(contextSet, "contextSet");
+ if (contextSet instanceof MutableContextSet) {
+ MutableContextSet other = ((MutableContextSet) contextSet);
+ this.map.putAll(other.map);
+ } else {
+ addAll(contextSet.toMultimap());
}
-
- this.map.putAll(contextSet.toMultimap());
}
/**
@@ -345,15 +338,12 @@ public final class MutableContextSet implements ContextSet {
* @param value the value to remove (case sensitive)
* @throws NullPointerException if the key or value is null
*/
- public void remove(String key, String value) {
- if (key == null) {
- throw new NullPointerException("key");
- }
- if (value == null) {
- throw new NullPointerException("value");
- }
+ public void remove(@Nonnull String key, @Nonnull String value) {
+ String k = checkNotNull(key, "key").toLowerCase().intern();
+ String v = checkNotNull(value, "value").intern();
- map.entries().removeIf(entry -> entry.getKey().equals(key) && entry.getValue().equals(value));
+ //noinspection StringEquality
+ map.entries().removeIf(entry -> entry.getKey() == k && entry.getValue() == v);
}
/**
@@ -363,15 +353,12 @@ public final class MutableContextSet implements ContextSet {
* @param value the value to remove
* @throws NullPointerException if the key or value is null
*/
- public void removeIgnoreCase(String key, String value) {
- if (key == null) {
- throw new NullPointerException("key");
- }
- if (value == null) {
- throw new NullPointerException("value");
- }
+ public void removeIgnoreCase(@Nonnull String key, @Nonnull String value) {
+ String k = checkNotNull(key, "key").toLowerCase().intern();
+ String v = checkNotNull(value, "value").intern();
- map.entries().removeIf(e -> e.getKey().equalsIgnoreCase(key) && e.getValue().equalsIgnoreCase(value));
+ //noinspection StringEquality
+ map.entries().removeIf(e -> e.getKey() == k && e.getValue().equalsIgnoreCase(v));
}
/**
@@ -380,12 +367,8 @@ public final class MutableContextSet implements ContextSet {
* @param key the key to remove
* @throws NullPointerException if the key is null
*/
- public void removeAll(String key) {
- if (key == null) {
- throw new NullPointerException("key");
- }
-
- map.removeAll(key.toLowerCase());
+ public void removeAll(@Nonnull String key) {
+ map.removeAll(checkNotNull(key, "key").toLowerCase());
}
/**
@@ -401,14 +384,20 @@ public final class MutableContextSet implements ContextSet {
if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o;
- final Multimap thisContexts = this.toMultimap();
- final Multimap otherContexts = other.toMultimap();
- return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts);
+ final Multimap otherContexts;
+
+ if (other instanceof MutableContextSet) {
+ otherContexts = ((MutableContextSet) other).map;
+ } else {
+ otherContexts = other.toMultimap();
+ }
+
+ return this.map.equals(otherContexts);
}
@Override
public int hashCode() {
- return 59 + (this.map == null ? 43 : this.map.hashCode());
+ return map.hashCode();
}
@Override
diff --git a/api/src/main/java/me/lucko/luckperms/api/event/user/UserLoginProcessEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/user/UserLoginProcessEvent.java
new file mode 100644
index 00000000..739e809e
--- /dev/null
+++ b/api/src/main/java/me/lucko/luckperms/api/event/user/UserLoginProcessEvent.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of LuckPerms, licensed under the MIT License.
+ *
+ * Copyright (c) lucko (Luck)
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.api.event.user;
+
+import me.lucko.luckperms.api.User;
+import me.lucko.luckperms.api.event.LuckPermsEvent;
+
+import java.util.UUID;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Called when LuckPerms has finished processing a certain Player's connection.
+ *
+ * This event will always execute during the platforms async login/auth event.
+ * All handlers will be called instantly.
+ *
+ * This, among other things, allows you to wait until permission data is loaded
+ * for a User during the BungeeCord 'LoginEvent', as event priorities are ignored
+ * by the current implementation.
+ *
+ * @since 3.4
+ */
+public interface UserLoginProcessEvent extends LuckPermsEvent {
+
+ /**
+ * Gets the UUID of the connection which was processed
+ *
+ * @return the uuid of the connection which was processed
+ */
+ @Nonnull
+ UUID getUuid();
+
+ /**
+ * Gets the username of the connection which was processed
+ *
+ * @return the username of the connection which was processed
+ */
+ @Nonnull
+ String getUsername();
+
+ /**
+ * Gets the resultant User instance which was loaded.
+ *
+ * @return the user instance
+ */
+ User getUser();
+
+}
diff --git a/api/src/main/java/me/lucko/luckperms/exceptions/MembershipException.java b/api/src/main/java/me/lucko/luckperms/exceptions/MembershipException.java
index 7989601c..06d94a45 100644
--- a/api/src/main/java/me/lucko/luckperms/exceptions/MembershipException.java
+++ b/api/src/main/java/me/lucko/luckperms/exceptions/MembershipException.java
@@ -26,8 +26,16 @@
package me.lucko.luckperms.exceptions;
/**
- * Thrown when a permission holding object doesn't / already has a permission or isn't / is already is a member of a
- * group
+ * Thrown when a certain membership state is / isn't met.
+ *
+ * For example, when:
+ *
+ *
+ * - a permission holding object doesn't have a permission
+ * - a permission holding object already has a permission
+ * - a permission holding object is already a member of a group
+ * - a permission holding object isn't already a member of a group
+ *
*
* @since 2.7
*/
diff --git a/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java
index 52c2c7f1..59b2a4dd 100644
--- a/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java
+++ b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java
@@ -26,8 +26,15 @@
package me.lucko.luckperms.exceptions;
/**
- * Thrown when a permission holding object already has a permission, is already a member of a group, or when a track
- * already contains a group.
+ * Thrown when an object already has something.
+ *
+ * For example, when:
+ *
+ *
+ * - a permission holding object already has a permission
+ * - a permission holding object is already a member of a group
+ * - a track already contains a group
+ *
*/
public class ObjectAlreadyHasException extends MembershipException {
}
diff --git a/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java
index f6bf0ad8..47d52e76 100644
--- a/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java
+++ b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java
@@ -26,8 +26,15 @@
package me.lucko.luckperms.exceptions;
/**
- * Thrown when a permission holding object does not already have a permission, is not already a member of a group,
- * or when a track doesn't contain a group.
+ * Thrown when an object lacks something.
+ *
+ * For example, when:
+ *
+ *
+ * - a permission holding object doesn't have a permission
+ * - a permission holding object isn't already a member of a group
+ * - a track doesn't contain a group
+ *
*/
public class ObjectLacksException extends MembershipException {
}
diff --git a/bukkit-legacy/pom.xml b/bukkit-legacy/pom.xml
index a825fc0f..63fbb71b 100644
--- a/bukkit-legacy/pom.xml
+++ b/bukkit-legacy/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
4.0.0
diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index 21450fb3..539be15d 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
4.0.0
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java
index 4daaf8fc..9150ba27 100644
--- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java
@@ -102,7 +102,8 @@ public class BukkitListener implements Listener {
- creating a user instance in the UserManager for this connection.
- setting up cached data. */
try {
- LoginHelper.loadUser(plugin, e.getUniqueId(), e.getName(), false);
+ User user = LoginHelper.loadUser(plugin, e.getUniqueId(), e.getName(), false);
+ plugin.getApiProvider().getEventFactory().handleUserLoginProcess(e.getUniqueId(), e.getName(), user);
} catch (Exception ex) {
ex.printStackTrace();
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java
index fc6c616f..6fbb4622 100644
--- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java
@@ -38,6 +38,7 @@ import me.lucko.luckperms.bukkit.processors.ChildProcessor;
import me.lucko.luckperms.bukkit.processors.DefaultsProcessor;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
+import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.processors.MapProcessor;
@@ -82,7 +83,11 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new DefaultsProcessor(contexts.isOp(), plugin.getDefaultsProvider()));
}
- return registerCalculator(new PermissionCalculator(plugin, user.getFriendlyName(), processors.build()));
+ return registerCalculator(new PermissionCalculator(
+ plugin,
+ PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()),
+ processors.build()
+ ));
}
@Override
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java
index 8b85eef4..50888151 100644
--- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java
@@ -35,6 +35,7 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.caching.UserCache;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -110,13 +111,13 @@ public class LPPermissible extends PermissibleBase {
@Override
public boolean isPermissionSet(@NonNull String permission) {
- Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission);
+ Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
return ts != Tristate.UNDEFINED || Permission.DEFAULT_PERMISSION.getValue(isOp());
}
@Override
public boolean isPermissionSet(@NonNull Permission permission) {
- Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName());
+ Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK);
if (ts != Tristate.UNDEFINED) {
return true;
}
@@ -130,13 +131,13 @@ public class LPPermissible extends PermissibleBase {
@Override
public boolean hasPermission(@NonNull String permission) {
- Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission);
+ Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK);
return ts != Tristate.UNDEFINED ? ts.asBoolean() : Permission.DEFAULT_PERMISSION.getValue(isOp());
}
@Override
public boolean hasPermission(@NonNull Permission permission) {
- Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName());
+ Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK);
if (ts != Tristate.UNDEFINED) {
return ts.asBoolean();
}
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java
index 7c9639de..c3821ead 100644
--- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java
@@ -331,7 +331,7 @@ public class VaultChatHook extends Chat {
perms.log("Getting meta: '" + node + "' for group " + group.getName() + " on world " + world + ", server " + perms.getServer());
for (Node n : group.getOwnNodes()) {
- if (!n.getValue()) continue;
+ if (!n.getValuePrimitive()) continue;
if (!n.isMeta()) continue;
if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) continue;
@@ -357,7 +357,7 @@ public class VaultChatHook extends Chat {
ExtractedContexts ec = ExtractedContexts.generate(Contexts.of(perms.createContextForWorldLookup(world).getContexts(), perms.isIncludeGlobal(), true, true, true, true, false));
for (Node n : group.getAllNodes(ec)) {
- if (!n.getValue()) continue;
+ if (!n.getValuePrimitive()) continue;
if (type.shouldIgnore(n)) continue;
if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) continue;
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java
index 0a5d3581..57f7368d 100644
--- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java
@@ -32,16 +32,17 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.DataMutateResult;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate;
-import me.lucko.luckperms.api.caching.PermissionData;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
+import me.lucko.luckperms.common.caching.PermissionCache;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.contexts.ExtractedContexts;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.NodeFactory;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import net.milkbowl.vault.permission.Permission;
@@ -147,7 +148,7 @@ public class VaultPermissionHook extends Permission {
}
// Effectively fallback to the standard Bukkit #hasPermission check.
- return user.getUserData().getPermissionData(createContextForWorldLookup(player, world)).getPermissionValue(permission).asBoolean();
+ return user.getUserData().getPermissionData(createContextForWorldLookup(player, world)).getPermissionValue(permission, CheckOrigin.INTERNAL).asBoolean();
}
@Override
@@ -396,7 +397,7 @@ public class VaultPermissionHook extends Permission {
// we need to do the complex PGO checking. (it's been enabled in the config.)
if (isPgoCheckInherited()) {
// we can just check the cached data
- PermissionData data = user.getUserData().getPermissionData(createContextForWorldLookup(plugin.getPlayer(user), world));
+ PermissionCache data = user.getUserData().getPermissionData(createContextForWorldLookup(plugin.getPlayer(user), world));
for (Map.Entry e : data.getImmutableBacking().entrySet()) {
if (!e.getValue()) continue;
if (!e.getKey().toLowerCase().startsWith("vault.primarygroup.")) continue;
@@ -409,7 +410,7 @@ public class VaultPermissionHook extends Permission {
}
if (isPgoCheckMemberOf()) {
- if (data.getPermissionValue("group." + group) != Tristate.TRUE) {
+ if (data.getPermissionValue("group." + group, CheckOrigin.INTERNAL) != Tristate.TRUE) {
continue;
}
}
@@ -419,7 +420,7 @@ public class VaultPermissionHook extends Permission {
} else {
// we need to check the users permissions only
for (Node node : user.getOwnNodes()) {
- if (!node.getValue()) continue;
+ if (!node.getValuePrimitive()) continue;
if (!node.getPermission().toLowerCase().startsWith("vault.primarygroup.")) continue;
if (!node.shouldApplyOnServer(getServer(), isIncludeGlobal(), false)) continue;
if (!node.shouldApplyOnWorld(world, true, false)) continue;
diff --git a/bungee/pom.xml b/bungee/pom.xml
index 36d7698d..4cd8d7e6 100644
--- a/bungee/pom.xml
+++ b/bungee/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
4.0.0
diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java
index 3e1ed165..a2126f56 100644
--- a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java
+++ b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java
@@ -35,6 +35,7 @@ import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.LoginHelper;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
@@ -100,7 +101,8 @@ public class BungeeListener implements Listener {
- creating a user instance in the UserManager for this connection.
- setting up cached data. */
try {
- LoginHelper.loadUser(plugin, c.getUniqueId(), c.getName(), true);
+ User user = LoginHelper.loadUser(plugin, c.getUniqueId(), c.getName(), true);
+ plugin.getApiProvider().getEventFactory().handleUserLoginProcess(c.getUniqueId(), c.getName(), user);
} catch (Exception ex) {
ex.printStackTrace();
@@ -159,7 +161,7 @@ public class BungeeListener implements Listener {
}
Contexts contexts = plugin.getContextManager().getApplicableContexts(player);
- Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission());
+ Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission(), CheckOrigin.PLATFORM_PERMISSION_CHECK);
if (result == Tristate.UNDEFINED && plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) {
return; // just use the result provided by the proxy when the event was created
}
@@ -182,7 +184,7 @@ public class BungeeListener implements Listener {
}
Contexts contexts = plugin.getContextManager().getApplicableContexts(player);
- Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission());
+ Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission(), CheckOrigin.PLATFORM_LOOKUP_CHECK);
if (result == Tristate.UNDEFINED && plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) {
return; // just use the result provided by the proxy when the event was created
}
diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/calculators/BungeeCalculatorFactory.java b/bungee/src/main/java/me/lucko/luckperms/bungee/calculators/BungeeCalculatorFactory.java
index 457042fc..b6786be5 100644
--- a/bungee/src/main/java/me/lucko/luckperms/bungee/calculators/BungeeCalculatorFactory.java
+++ b/bungee/src/main/java/me/lucko/luckperms/bungee/calculators/BungeeCalculatorFactory.java
@@ -33,6 +33,7 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.bungee.LPBungeePlugin;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
+import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.processors.MapProcessor;
@@ -60,7 +61,11 @@ public class BungeeCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new WildcardProcessor());
}
- return registerCalculator(new PermissionCalculator(plugin, user.getFriendlyName(), processors.build()));
+ return registerCalculator(new PermissionCalculator(
+ plugin,
+ PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()),
+ processors.build()
+ ));
}
@Override
diff --git a/common/pom.xml b/common/pom.xml
index 0d468663..fb4c6c7e 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
4.0.0
diff --git a/common/src/main/java/me/lucko/luckperms/common/api/ApiHandler.java b/common/src/main/java/me/lucko/luckperms/common/api/ApiHandler.java
index d769a083..ebaff00b 100644
--- a/common/src/main/java/me/lucko/luckperms/common/api/ApiHandler.java
+++ b/common/src/main/java/me/lucko/luckperms/common/api/ApiHandler.java
@@ -31,18 +31,23 @@ import me.lucko.luckperms.api.LuckPermsApi;
import java.lang.reflect.Method;
public class ApiHandler {
- private static Method REGISTER;
- private static Method UNREGISTER;
+ private static final Method REGISTER;
+ private static final Method UNREGISTER;
static {
+ Method register = null;
+ Method unregister = null;
try {
- REGISTER = LuckPerms.class.getDeclaredMethod("registerProvider", LuckPermsApi.class);
- REGISTER.setAccessible(true);
+ register = LuckPerms.class.getDeclaredMethod("registerProvider", LuckPermsApi.class);
+ register.setAccessible(true);
- UNREGISTER = LuckPerms.class.getDeclaredMethod("unregisterProvider");
- UNREGISTER.setAccessible(true);
+ unregister = LuckPerms.class.getDeclaredMethod("unregisterProvider");
+ unregister.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
+
+ REGISTER = register;
+ UNREGISTER = unregister;
}
public static void registerProvider(LuckPermsApi luckPermsApi) {
diff --git a/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java
index 2f194e4e..1b3044e6 100644
--- a/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java
+++ b/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java
@@ -88,7 +88,7 @@ public class ApiProvider implements LuckPermsApi {
@Override
public double getApiVersion() {
- return 3.3;
+ return 3.4;
}
@Override
diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/PermissionCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/PermissionCache.java
index 1b8a6cac..d91726b9 100644
--- a/common/src/main/java/me/lucko/luckperms/common/caching/PermissionCache.java
+++ b/common/src/main/java/me/lucko/luckperms/common/caching/PermissionCache.java
@@ -33,6 +33,7 @@ import me.lucko.luckperms.api.caching.PermissionData;
import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.model.User;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import java.util.Collections;
import java.util.Map;
@@ -93,6 +94,10 @@ public class PermissionCache implements PermissionData {
@Override
public Tristate getPermissionValue(@NonNull String permission) {
- return calculator.getPermissionValue(permission);
+ return calculator.getPermissionValue(permission, CheckOrigin.API);
+ }
+
+ public Tristate getPermissionValue(@NonNull String permission, CheckOrigin origin) {
+ return calculator.getPermissionValue(permission, origin);
}
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java
index b60b0437..23a359b3 100644
--- a/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java
+++ b/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java
@@ -36,7 +36,6 @@ import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.ChatMetaType;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.caching.MetaContexts;
-import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.caching.PermissionData;
import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.common.config.ConfigKeys;
@@ -68,17 +67,17 @@ public class UserCache implements UserData {
.build(new MetaCacheLoader());
@Override
- public PermissionData getPermissionData(@NonNull Contexts contexts) {
+ public PermissionCache getPermissionData(@NonNull Contexts contexts) {
return permission.get(contexts);
}
@Override
- public MetaData getMetaData(@NonNull MetaContexts contexts) {
+ public MetaCache getMetaData(@NonNull MetaContexts contexts) {
return meta.get(contexts);
}
@Override
- public MetaData getMetaData(@NonNull Contexts contexts) {
+ public MetaCache getMetaData(@NonNull Contexts contexts) {
// just create a MetaContexts instance using the values in the config
return getMetaData(makeFromMetaContextsConfig(contexts, user.getPlugin()));
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java
index e631bbef..c0cb3628 100644
--- a/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java
+++ b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java
@@ -33,6 +33,7 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.processors.PermissionProcessor;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import java.util.List;
import java.util.Map;
@@ -43,7 +44,7 @@ import java.util.Map;
@RequiredArgsConstructor
public class PermissionCalculator {
private final LuckPermsPlugin plugin;
- private final String objectName;
+ private final PermissionCalculatorMetadata metadata;
private final List processors;
// caches lookup calls.
@@ -54,7 +55,7 @@ public class PermissionCalculator {
lookupCache.invalidateAll();
}
- public Tristate getPermissionValue(String permission) {
+ public Tristate getPermissionValue(String permission, CheckOrigin origin) {
// convert the permission to lowercase, as all values in the backing map are also lowercase.
// this allows fast case insensitive lookups
@@ -64,7 +65,7 @@ public class PermissionCalculator {
Tristate result = lookupCache.get(permission);
// log this permission lookup to the verbose handler
- plugin.getVerboseHandler().offerCheckData(objectName, permission, result);
+ plugin.getVerboseHandler().offerCheckData(origin, metadata.getObjectName(), metadata.getContext(), permission, result);
// return the result
return result;
diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculatorMetadata.java b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculatorMetadata.java
new file mode 100644
index 00000000..585673a7
--- /dev/null
+++ b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculatorMetadata.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of LuckPerms, licensed under the MIT License.
+ *
+ * Copyright (c) lucko (Luck)
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.common.calculators;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import me.lucko.luckperms.api.context.ContextSet;
+
+@Getter
+@AllArgsConstructor(staticName = "of")
+public class PermissionCalculatorMetadata {
+
+ /**
+ * The name of the object which owns the permission calculator
+ */
+ private final String objectName;
+
+ /**
+ * The context the permission calculator works with
+ */
+ private final ContextSet context;
+
+}
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java
index eeab5a89..101a5e02 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java
@@ -173,7 +173,7 @@ public class PermissionInfo extends SharedSubCommand {
}
for (Node node : page) {
- String s = "&3> " + (node.getValue() ? "&a" : "&c") + node.getPermission() + (console ? " &7(" + node.getValue() + "&7)" : "") + Util.getAppendableNodeContextString(node) + "\n";
+ String s = "&3> " + (node.getValuePrimitive() ? "&a" : "&c") + node.getPermission() + (console ? " &7(" + node.getValuePrimitive() + "&7)" : "") + Util.getAppendableNodeContextString(node) + "\n";
if (temp) {
s += "&2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "\n";
}
@@ -186,7 +186,7 @@ public class PermissionInfo extends SharedSubCommand {
private static Consumer> makeFancy(PermissionHolder holder, String label, Node node) {
HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline(
- "¥3> " + (node.getValue() ? "¥a" : "¥c") + node.getPermission(),
+ "¥3> " + (node.getValuePrimitive() ? "¥a" : "¥c") + node.getPermission(),
" ",
"¥7Click to remove this node from " + holder.getFriendlyName()
), '¥'));
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java
index 1e7ae003..dd1dcaa4 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java
@@ -192,7 +192,7 @@ public class GroupListMembers extends SubCommand {
private static Consumer> makeFancy(String holderName, boolean group, String label, HeldPermission> perm) {
HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline(
- "&3> " + (perm.asNode().getValue() ? "&a" : "&c") + perm.asNode().getGroupName(),
+ "&3> " + (perm.asNode().getValuePrimitive() ? "&a" : "&c") + perm.asNode().getGroupName(),
" ",
"&7Click to remove this parent from " + holderName
), Constants.FORMAT_CHAR));
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/log/LogNotify.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/log/LogNotify.java
index 313d5218..2ce1f8e8 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/log/LogNotify.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/log/LogNotify.java
@@ -61,7 +61,7 @@ public class LogNotify extends SubCommand {
// if they don't have the perm, they're not ignoring
// if set to false, ignore it, return false
- return ret.map(Node::getValue).orElse(false);
+ return ret.map(Node::getValuePrimitive).orElse(false);
}
private static void setIgnoring(LuckPermsPlugin plugin, UUID uuid, boolean state) {
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java
index 5296ec23..4f4e48a2 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java
@@ -39,6 +39,7 @@ import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import java.util.List;
import java.util.UUID;
@@ -67,7 +68,7 @@ public class CheckCommand extends SingleCommand {
return CommandResult.STATE_ERROR;
}
- Tristate tristate = user.getUserData().getPermissionData(plugin.getContextForUser(user)).getPermissionValue(permission);
+ Tristate tristate = user.getUserData().getPermissionData(plugin.getContextForUser(user)).getPermissionValue(permission, CheckOrigin.INTERNAL);
Message.CHECK_RESULT.send(sender, user.getFriendlyName(), permission, Util.formatTristate(tristate));
return CommandResult.SUCCESS;
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java
index 4340dcd9..7910b917 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java
@@ -191,7 +191,7 @@ public class SearchCommand extends SingleCommand {
private static Consumer> makeFancy(String holderName, boolean group, String label, HeldPermission> perm) {
HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline(
- "&3> " + (perm.asNode().getValue() ? "&a" : "&c") + perm.asNode().getPermission(),
+ "&3> " + (perm.asNode().getValuePrimitive() ? "&a" : "&c") + perm.asNode().getPermission(),
" ",
"&7Click to remove this node from " + holderName
), Constants.FORMAT_CHAR));
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java
index 8957ed0a..dd27cbf0 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java
@@ -25,7 +25,7 @@
package me.lucko.luckperms.common.commands.impl.misc;
-import me.lucko.luckperms.api.caching.PermissionData;
+import me.lucko.luckperms.common.caching.PermissionCache;
import me.lucko.luckperms.common.commands.CommandException;
import me.lucko.luckperms.common.commands.CommandResult;
import me.lucko.luckperms.common.commands.abstraction.SingleCommand;
@@ -86,7 +86,7 @@ public class TreeCommand extends SingleCommand {
return CommandResult.STATE_ERROR;
}
- PermissionData permissionData = user.getUserData().getPermissionData(plugin.getContextForUser(user));
+ PermissionCache permissionData = user.getUserData().getPermissionData(plugin.getContextForUser(user));
TreeView view = TreeViewBuilder.newBuilder().rootPosition(selection).maxLevels(maxLevel).build(plugin.getPermissionVault());
if (!view.hasData()) {
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java
index 6a455c06..39d68178 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java
@@ -63,6 +63,7 @@ public class VerboseCommand extends SingleCommand {
return CommandResult.INVALID_ARGS;
}
+ boolean noTraces = args.remove("--notrace") || args.remove("--notraces") || args.remove("--slim") || args.remove("-s");
String mode = args.get(0).toLowerCase();
if (mode.equals("on") || mode.equals("true") || mode.equals("record")) {
@@ -108,7 +109,7 @@ public class VerboseCommand extends SingleCommand {
} else {
Message.VERBOSE_RECORDING_UPLOAD_START.send(sender);
- String url = listener.uploadPasteData();
+ String url = listener.uploadPasteData(!noTraces);
if (url == null) {
url = "null";
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java
index 619feeb0..4f31a6f1 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java
@@ -99,7 +99,7 @@ public class UserDemote extends SubCommand {
// Load applicable groups
Set nodes = user.getEnduringNodes().values().stream()
.filter(Node::isGroupNode)
- .filter(Node::getValue)
+ .filter(Node::getValuePrimitive)
.filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable()))
.collect(Collectors.toSet());
diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java
index f0482ead..ec55e6d4 100644
--- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java
+++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java
@@ -97,7 +97,7 @@ public class UserPromote extends SubCommand {
// Load applicable groups
Set nodes = user.getEnduringNodes().values().stream()
.filter(Node::isGroupNode)
- .filter(Node::getValue)
+ .filter(Node::getValuePrimitive)
.filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable()))
.collect(Collectors.toSet());
diff --git a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetComparator.java b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetComparator.java
index 1432c77f..4105f355 100644
--- a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetComparator.java
+++ b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetComparator.java
@@ -27,20 +27,13 @@ package me.lucko.luckperms.common.contexts;
import me.lucko.luckperms.api.context.ImmutableContextSet;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
-import java.util.TreeSet;
public class ContextSetComparator implements Comparator {
- private static final Comparator> STRING_ENTRY_COMPARATOR = (o1, o2) -> {
- int ret = o1.getKey().compareTo(o2.getKey());
- if (ret != 0) {
- return ret;
- }
-
- return o1.getValue().compareTo(o2.getValue());
- };
private static final ContextSetComparator INSTANCE = new ContextSetComparator();
public static Comparator get() {
@@ -75,16 +68,15 @@ public class ContextSetComparator implements Comparator {
return o1Size > o2Size ? 1 : -1;
}
- // we *have* to maintain transitivity in this comparator. this may be expensive, but it's necessary, as these
- // values are stored in a treemap.
+ // we *have* to maintain transitivity in this comparator. this may be expensive, but it's necessary, as this
+ // comparator is used in the PermissionHolder nodes treemap
// in order to have consistent ordering, we have to compare the content of the context sets by ordering the
// elements and then comparing which set is greater.
- TreeSet> o1Map = new TreeSet<>(STRING_ENTRY_COMPARATOR);
- TreeSet> o2Map = new TreeSet<>(STRING_ENTRY_COMPARATOR);
-
- o1Map.addAll(o1.toMultimap().entries());
- o2Map.addAll(o2.toMultimap().entries());
+ List> o1Map = new ArrayList<>(o1.toSet());
+ List> o2Map = new ArrayList<>(o2.toSet());
+ o1Map.sort(STRING_ENTRY_COMPARATOR);
+ o2Map.sort(STRING_ENTRY_COMPARATOR);
int o1MapSize = o1Map.size();
int o2MapSize = o2Map.size();
@@ -101,18 +93,33 @@ public class ContextSetComparator implements Comparator {
Map.Entry ent2 = it2.next();
// compare these values.
- if (ent1.getKey().equals(ent2.getKey()) && ent1.getValue().equals(ent2.getValue())) {
+ //noinspection StringEquality - strings are intern'd
+ if (ent1.getKey() == ent2.getKey() && ent1.getValue() == ent2.getValue()) {
// identical entries. just move on
continue;
}
- // these values are at the same position in the ordered sets.
+ // these entries are at the same position in the ordered sets.
// if ent1 is "greater" than ent2, then at this first position, o1 has a "greater" entry, and can therefore be considered
- // a greater set.
+ // a greater set, and vice versa
return STRING_ENTRY_COMPARATOR.compare(ent1, ent2);
}
- // shouldn't ever reach this point. ever.
+ // shouldn't ever reach this point.
return 0;
}
+
+ private static final Comparator FAST_STRING_COMPARATOR = (o1, o2) -> {
+ //noinspection StringEquality
+ return o1 == o2 ? 0 : o1.compareTo(o2);
+ };
+
+ private static final Comparator> STRING_ENTRY_COMPARATOR = (o1, o2) -> {
+ int ret = FAST_STRING_COMPARATOR.compare(o1.getKey(), o2.getKey());
+ if (ret != 0) {
+ return ret;
+ }
+
+ return FAST_STRING_COMPARATOR.compare(o1.getValue(), o2.getValue());
+ };
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java b/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java
index 04a9ab7f..4f14ebab 100644
--- a/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java
+++ b/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java
@@ -64,6 +64,7 @@ import me.lucko.luckperms.common.event.impl.EventUserDataRecalculate;
import me.lucko.luckperms.common.event.impl.EventUserDemote;
import me.lucko.luckperms.common.event.impl.EventUserFirstLogin;
import me.lucko.luckperms.common.event.impl.EventUserLoad;
+import me.lucko.luckperms.common.event.impl.EventUserLoginProcess;
import me.lucko.luckperms.common.event.impl.EventUserPromote;
import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.PermissionHolder;
@@ -79,158 +80,167 @@ import java.util.concurrent.atomic.AtomicBoolean;
public final class EventFactory {
private final LuckPermsEventBus eventBus;
- private void fireEvent(LuckPermsEvent event) {
+ private void fireEventAsync(LuckPermsEvent event) {
eventBus.fireEventAsync(event);
}
+ private void fireEvent(LuckPermsEvent event) {
+ eventBus.fireEvent(event);
+ }
+
public void handleGroupCreate(Group group, CreationCause cause) {
EventGroupCreate event = new EventGroupCreate(group.getDelegate(), cause);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleGroupDelete(Group group, DeletionCause cause) {
EventGroupDelete event = new EventGroupDelete(group.getName(), ImmutableSet.copyOf(group.getEnduringNodes().values()), cause);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleGroupLoadAll() {
EventGroupLoadAll event = new EventGroupLoadAll();
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleGroupLoad(Group group) {
EventGroupLoad event = new EventGroupLoad(group.getDelegate());
- fireEvent(event);
+ fireEventAsync(event);
}
public boolean handleLogBroadcast(boolean initialState, LogEntry entry, LogBroadcastEvent.Origin origin) {
AtomicBoolean cancel = new AtomicBoolean(initialState);
EventLogBroadcast event = new EventLogBroadcast(cancel, entry, origin);
- eventBus.fireEvent(event);
+ fireEvent(event);
return cancel.get();
}
public boolean handleLogPublish(boolean initialState, LogEntry entry) {
AtomicBoolean cancel = new AtomicBoolean(initialState);
EventLogPublish event = new EventLogPublish(cancel, entry);
- eventBus.fireEvent(event);
+ fireEvent(event);
return cancel.get();
}
public boolean handleLogNetworkPublish(boolean initialState, UUID id, LogEntry entry) {
AtomicBoolean cancel = new AtomicBoolean(initialState);
EventLogNetworkPublish event = new EventLogNetworkPublish(cancel, id, entry);
- eventBus.fireEvent(event);
+ fireEvent(event);
return cancel.get();
}
public void handleLogReceive(UUID id, LogEntry entry) {
EventLogReceive event = new EventLogReceive(id, entry);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleNodeAdd(Node node, PermissionHolder target, Collection before, Collection after) {
EventNodeAdd event = new EventNodeAdd(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleNodeClear(PermissionHolder target, Collection before, Collection after) {
EventNodeClear event = new EventNodeClear(target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleNodeRemove(Node node, PermissionHolder target, Collection before, Collection after) {
EventNodeRemove event = new EventNodeRemove(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleConfigReload() {
EventConfigReload event = new EventConfigReload();
- fireEvent(event);
+ fireEventAsync(event);
}
public void handlePostSync() {
EventPostSync event = new EventPostSync();
- fireEvent(event);
+ fireEventAsync(event);
}
public boolean handleNetworkPreSync(boolean initialState, UUID id) {
AtomicBoolean cancel = new AtomicBoolean(initialState);
EventPreNetworkSync event = new EventPreNetworkSync(cancel, id);
- eventBus.fireEvent(event);
+ fireEvent(event);
return cancel.get();
}
public boolean handlePreSync(boolean initialState) {
AtomicBoolean cancel = new AtomicBoolean(initialState);
EventPreSync event = new EventPreSync(cancel);
- eventBus.fireEvent(event);
+ fireEvent(event);
return cancel.get();
}
public void handleTrackCreate(Track track, CreationCause cause) {
EventTrackCreate event = new EventTrackCreate(track.getDelegate(), cause);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleTrackDelete(Track track, DeletionCause cause) {
EventTrackDelete event = new EventTrackDelete(track.getName(), ImmutableList.copyOf(track.getGroups()), cause);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleTrackLoadAll() {
EventTrackLoadAll event = new EventTrackLoadAll();
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleTrackLoad(Track track) {
EventTrackLoad event = new EventTrackLoad(track.getDelegate());
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleTrackAddGroup(Track track, String group, List before, List after) {
EventTrackAddGroup event = new EventTrackAddGroup(group, track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.copyOf(after));
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleTrackClear(Track track, List before) {
EventTrackClear event = new EventTrackClear(track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.of());
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleTrackRemoveGroup(Track track, String group, List before, List after) {
EventTrackRemoveGroup event = new EventTrackRemoveGroup(group, track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.copyOf(after));
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleUserCacheLoad(User user, UserData data) {
EventUserCacheLoad event = new EventUserCacheLoad(user.getDelegate(), data);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleUserDataRecalculate(User user, UserData data) {
EventUserDataRecalculate event = new EventUserDataRecalculate(user.getDelegate(), data);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleUserFirstLogin(UUID uuid, String username) {
EventUserFirstLogin event = new EventUserFirstLogin(uuid, username);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleUserLoad(User user) {
EventUserLoad event = new EventUserLoad(user.getDelegate());
+ fireEventAsync(event);
+ }
+
+ public void handleUserLoginProcess(UUID uuid, String username, User user) {
+ EventUserLoginProcess event = new EventUserLoginProcess(uuid, username, user.getDelegate());
fireEvent(event);
}
public void handleUserDemote(User user, Track track, String from, String to) {
EventUserDemote event = new EventUserDemote(track.getDelegate(), user.getDelegate(), from, to);
- fireEvent(event);
+ fireEventAsync(event);
}
public void handleUserPromote(User user, Track track, String from, String to) {
EventUserPromote event = new EventUserPromote(track.getDelegate(), user.getDelegate(), from, to);
- fireEvent(event);
+ fireEventAsync(event);
}
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/event/impl/EventUserLoginProcess.java b/common/src/main/java/me/lucko/luckperms/common/event/impl/EventUserLoginProcess.java
new file mode 100644
index 00000000..92cc2040
--- /dev/null
+++ b/common/src/main/java/me/lucko/luckperms/common/event/impl/EventUserLoginProcess.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of LuckPerms, licensed under the MIT License.
+ *
+ * Copyright (c) lucko (Luck)
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.common.event.impl;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+
+import me.lucko.luckperms.api.User;
+import me.lucko.luckperms.api.event.user.UserLoginProcessEvent;
+import me.lucko.luckperms.common.event.AbstractEvent;
+
+import java.util.UUID;
+
+@Getter
+@ToString
+@AllArgsConstructor
+public class EventUserLoginProcess extends AbstractEvent implements UserLoginProcessEvent {
+
+ private final UUID uuid;
+ private final String username;
+ private final User user;
+
+}
diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java b/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java
index dc87ed18..374b69ef 100644
--- a/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java
+++ b/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java
@@ -57,7 +57,8 @@ public enum CommandSpec {
VERBOSE("Manage verbose permission checking", "/%s verbose [filter]",
Arg.list(
Arg.create("on|record|off|paste", true, "whether to enable/disable logging, or to paste the logged output"),
- Arg.create("filter", false, "the filter to match entries against")
+ Arg.create("filter", false, "the filter to match entries against"),
+ Arg.create("--slim", false, "add \"--slim\" to exclude trace data from the pasted output")
)
),
TREE("Generate a tree view of permissions", "/%s tree [selection] [max level] [player]",
diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java
index ea8ae746..c4d3ee77 100644
--- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java
+++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java
@@ -57,7 +57,7 @@ public enum Message {
OP_DISABLED("&bThe vanilla OP system is disabled on this server.", false),
OP_DISABLED_SPONGE("&2Server Operator status has no effect when a permission plugin is installed. Please edit user data directly.", true),
LOG("&3LOG &3&l> {0}", true),
- VERBOSE_LOG("&3VERBOSE &3&l> {0}", true),
+ VERBOSE_LOG("&3VB &3&l> {0}", true),
EXPORT_LOG("&3EXPORT &3&l> &f{0}", true),
EXPORT_LOG_PROGRESS("&3EXPORT &3&l> &7{0}", true),
diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java b/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java
index b96fcf92..1e9b4f70 100644
--- a/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java
+++ b/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java
@@ -39,35 +39,8 @@ import java.util.UUID;
@RequiredArgsConstructor
public class GenericUserManager extends AbstractManager implements UserManager {
- public static boolean giveDefaultIfNeeded(User user, boolean save, LuckPermsPlugin plugin) {
- boolean hasGroup = false;
- if (user.getPrimaryGroup().getStoredValue() != null && !user.getPrimaryGroup().getStoredValue().isEmpty()) {
- for (Node node : user.getEnduringNodes().values()) {
- if (node.hasSpecificContext()) {
- continue;
- }
-
- if (node.isGroupNode()) {
- hasGroup = true;
- break;
- }
- }
- }
-
- if (hasGroup) {
- return false;
- }
-
- user.getPrimaryGroup().setStoredValue("default");
- user.setPermission(NodeFactory.make("group.default"));
-
- if (save) {
- plugin.getStorage().saveUser(user);
- }
-
- return true;
- }
+ private final LuckPermsPlugin plugin;
@Override
public User getOrMake(UserIdentifier id) {
@@ -78,39 +51,6 @@ public class GenericUserManager extends AbstractManager im
return ret;
}
- /**
- * Check whether the user's state indicates that they should be persisted to storage.
- *
- * @param user the user to check
- * @return true if the user should be saved
- */
- public static boolean shouldSave(User user) {
- if (user.getEnduringNodes().size() != 1) {
- return true;
- }
-
- for (Node node : user.getEnduringNodes().values()) {
- // There's only one.
- if (!node.isGroupNode()) {
- return true;
- }
-
- if (node.isTemporary() || node.isServerSpecific() || node.isWorldSpecific()) {
- return true;
- }
-
- if (!node.getGroupName().equalsIgnoreCase("default")) {
- // The user's only node is not the default group one.
- return true;
- }
- }
-
- // Not in the default primary group
- return !user.getPrimaryGroup().getStoredValue().equalsIgnoreCase("default");
- }
-
- private final LuckPermsPlugin plugin;
-
@Override
public User apply(UserIdentifier id) {
return !id.getUsername().isPresent() ?
@@ -140,9 +80,12 @@ public class GenericUserManager extends AbstractManager im
}
@Override
- public void cleanup(User user) {
+ public boolean cleanup(User user) {
if (!plugin.isPlayerOnline(plugin.getUuidCache().getExternalUUID(user.getUuid()))) {
unload(user);
+ return true;
+ } else {
+ return false;
}
}
@@ -177,4 +120,65 @@ public class GenericUserManager extends AbstractManager im
});
});
}
+
+ public static boolean giveDefaultIfNeeded(User user, boolean save, LuckPermsPlugin plugin) {
+ boolean hasGroup = false;
+
+ if (user.getPrimaryGroup().getStoredValue() != null && !user.getPrimaryGroup().getStoredValue().isEmpty()) {
+ for (Node node : user.getEnduringNodes().values()) {
+ if (node.hasSpecificContext()) {
+ continue;
+ }
+
+ if (node.isGroupNode()) {
+ hasGroup = true;
+ break;
+ }
+ }
+ }
+
+ if (hasGroup) {
+ return false;
+ }
+
+ user.getPrimaryGroup().setStoredValue("default");
+ user.setPermission(NodeFactory.make("group.default"));
+
+ if (save) {
+ plugin.getStorage().saveUser(user);
+ }
+
+ return true;
+ }
+
+ /**
+ * Check whether the user's state indicates that they should be persisted to storage.
+ *
+ * @param user the user to check
+ * @return true if the user should be saved
+ */
+ public static boolean shouldSave(User user) {
+ if (user.getEnduringNodes().size() != 1) {
+ return true;
+ }
+
+ for (Node node : user.getEnduringNodes().values()) {
+ // There's only one.
+ if (!node.isGroupNode()) {
+ return true;
+ }
+
+ if (node.isTemporary() || node.isServerSpecific() || node.isWorldSpecific()) {
+ return true;
+ }
+
+ if (!node.getGroupName().equalsIgnoreCase("default")) {
+ // The user's only node is not the default group one.
+ return true;
+ }
+ }
+
+ // Not in the default primary group
+ return !user.getPrimaryGroup().getStoredValue().equalsIgnoreCase("default");
+ }
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java b/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java
index 99f7756d..10ac993e 100644
--- a/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java
+++ b/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java
@@ -61,7 +61,7 @@ public interface UserManager extends Manager {
*
* @param user The user to be cleaned up
*/
- void cleanup(User user);
+ boolean cleanup(User user);
/**
* Schedules a task to cleanup a user after a certain period of time, if they're not on the server anymore.
diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java
index 113987f2..a5334072 100644
--- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java
+++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java
@@ -546,7 +546,7 @@ public abstract class PermissionHolder {
if (!n.isGroupNode()) continue;
String groupName = n.getGroupName();
- if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValue()) continue;
+ if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue;
if (!((contexts.isApplyGlobalGroups() || n.isServerSpecific()) && (contexts.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) {
continue;
@@ -624,7 +624,7 @@ public abstract class PermissionHolder {
if (!n.isGroupNode()) continue;
String groupName = n.getGroupName();
- if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValue()) continue;
+ if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue;
Group g = plugin.getGroupManager().getIfLoaded(groupName);
if (g != null) {
@@ -717,11 +717,11 @@ public abstract class PermissionHolder {
for (Node node : entries) {
String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission();
- if (perms.putIfAbsent(perm, node.getValue()) == null) {
+ if (perms.putIfAbsent(perm, node.getValuePrimitive()) == null) {
if (applyShorthand) {
List sh = node.resolveShorthand();
if (!sh.isEmpty()) {
- sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValue()));
+ sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValuePrimitive()));
}
}
}
@@ -738,11 +738,11 @@ public abstract class PermissionHolder {
for (Node node : entries) {
String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission();
- if (perms.putIfAbsent(perm, node.getValue()) == null) {
+ if (perms.putIfAbsent(perm, node.getValuePrimitive()) == null) {
if (applyShorthand) {
List sh = node.resolveShorthand();
if (!sh.isEmpty()) {
- sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValue()));
+ sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValuePrimitive()));
}
}
}
@@ -770,7 +770,7 @@ public abstract class PermissionHolder {
List nodes = filterNodes(context.getContextSet());
for (Node node : nodes) {
- if (!node.getValue()) continue;
+ if (!node.getValuePrimitive()) continue;
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
if (!((contexts.isIncludeGlobal() || node.isServerSpecific()) && (contexts.isIncludeGlobalWorld() || node.isWorldSpecific()))) {
@@ -793,7 +793,7 @@ public abstract class PermissionHolder {
if (!n.isGroupNode()) continue;
String groupName = n.getGroupName();
- if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValue()) continue;
+ if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue;
if (!((contexts.isApplyGlobalGroups() || n.isServerSpecific()) && (contexts.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) {
continue;
@@ -834,7 +834,7 @@ public abstract class PermissionHolder {
List nodes = getOwnNodes();
for (Node node : nodes) {
- if (!node.getValue()) continue;
+ if (!node.getValuePrimitive()) continue;
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, getObjectName()));
@@ -853,7 +853,7 @@ public abstract class PermissionHolder {
if (!n.isGroupNode()) continue;
String groupName = n.getGroupName();
- if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValue()) continue;
+ if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue;
Group g = plugin.getGroupManager().getIfLoaded(groupName);
if (g != null) {
diff --git a/common/src/main/java/me/lucko/luckperms/common/model/User.java b/common/src/main/java/me/lucko/luckperms/common/model/User.java
index fc664af4..05a1fe77 100644
--- a/common/src/main/java/me/lucko/luckperms/common/model/User.java
+++ b/common/src/main/java/me/lucko/luckperms/common/model/User.java
@@ -70,13 +70,7 @@ public class User extends PermissionHolder implements Identifiable refreshBuffer = new BufferedRequest(250L, 50L, r -> getPlugin().doAsync(r)) {
- @Override
- protected Void perform() {
- refreshPermissions();
- return null;
- }
- };
+ private BufferedRequest refreshBuffer = new UserRefreshBuffer(this);
@Getter
private final UserDelegate delegate = new UserDelegate(this);
@@ -214,4 +208,20 @@ public class User extends PermissionHolder implements Identifiable {
+ private final User user;
+
+ private UserRefreshBuffer(User user) {
+ super(250L, 50L, r -> user.getPlugin().doAsync(r));
+ this.user = user;
+ }
+
+ @Override
+ protected Void perform() {
+ user.refreshPermissions();
+ return null;
+ }
+ }
+
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java
index cf88a9fe..8c0242c7 100644
--- a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java
+++ b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java
@@ -28,7 +28,6 @@ package me.lucko.luckperms.common.node;
import lombok.Getter;
import lombok.ToString;
-import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
@@ -50,18 +49,31 @@ import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import static com.google.common.base.Preconditions.checkState;
+
/**
- * An immutable permission node
+ * An immutable implementation of {@link Node}.
*/
@ToString(of = {"permission", "value", "override", "server", "world", "expireAt", "contexts"})
public final class ImmutableNode implements Node {
private static final int NODE_SEPARATOR_CHAR = Character.getNumericValue('.');
+ private static final String[] PERMISSION_DELIMS = new String[]{"/", "-", "$", "(", ")", "=", ","};
+ private static final String[] SERVER_WORLD_DELIMS = new String[]{"/", "-"};
+ private static final Splitter META_SPLITTER = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2);
+
+
+ /*
+ * NODE STATE
+ *
+ * This are the actual node parameters, and are
+ * basically what this class wraps.
+ */
+
@Getter
private final String permission;
- @Getter
- private final Boolean value;
+ private final boolean value;
@Getter
private boolean override;
@@ -80,12 +92,28 @@ public final class ImmutableNode implements Node {
@Getter
private final ImmutableContextSet fullContexts;
- // Cached state
+ /*
+ * CACHED STATE
+ *
+ * These values are based upon the node state above, and are stored here
+ * to make node comparison and manipulation faster.
+ *
+ * This increases the memory footprint of this class by a bit, but it is
+ * worth it for the gain in speed.
+ *
+ * The methods on this class are called v. frequently.
+ */
- // these save on lots of instance creation when comparing nodes
+ // storing optionals as a field type is usually a bad idea, however, the
+ // #getServer and #getWorld methods are called frequently when comparing nodes.
+ // without caching these values, it creates quite a bit of object churn
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private final Optional optServer;
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private final Optional optWorld;
+ private final int hashCode;
+
private final boolean isGroup;
private String groupName;
@@ -103,7 +131,7 @@ public final class ImmutableNode implements Node {
private final List resolvedShorthand;
- private final String serializedNode;
+ private String serializedNode = null;
/**
* Make an immutable node instance
@@ -116,7 +144,7 @@ public final class ImmutableNode implements Node {
* @param contexts any additional contexts applying to this node
*/
@SuppressWarnings("deprecation")
- public ImmutableNode(String permission, boolean value, boolean override, long expireAt, String server, String world, ContextSet contexts) {
+ ImmutableNode(String permission, boolean value, boolean override, long expireAt, String server, String world, ContextSet contexts) {
if (permission == null || permission.equals("")) {
throw new IllegalArgumentException("Empty permission");
}
@@ -135,18 +163,20 @@ public final class ImmutableNode implements Node {
world = null;
}
- this.permission = NodeFactory.unescapeDelimiters(permission, "/", "-", "$", "(", ")", "=", ",");
+ this.permission = NodeFactory.unescapeDelimiters(permission, PERMISSION_DELIMS).intern();
this.value = value;
this.override = override;
this.expireAt = expireAt;
- this.server = NodeFactory.unescapeDelimiters(server, "/", "-");
- this.world = NodeFactory.unescapeDelimiters(world, "/", "-");
+ this.server = internString(NodeFactory.unescapeDelimiters(server, SERVER_WORLD_DELIMS));
+ this.world = internString(NodeFactory.unescapeDelimiters(world, SERVER_WORLD_DELIMS));
this.contexts = contexts == null ? ContextSet.empty() : contexts.makeImmutable();
+ String lowerCasePermission = this.permission.toLowerCase();
+
// Setup state
- isGroup = this.permission.toLowerCase().startsWith("group.");
+ isGroup = lowerCasePermission.startsWith("group.");
if (isGroup) {
- groupName = this.permission.substring("group.".length()).toLowerCase();
+ groupName = lowerCasePermission.substring("group.".length()).intern();
}
isWildcard = this.permission.endsWith(".*");
@@ -154,38 +184,48 @@ public final class ImmutableNode implements Node {
isMeta = NodeFactory.isMetaNode(this.permission);
if (isMeta) {
- List metaPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("meta.".length()));
- meta = Maps.immutableEntry(MetaUtils.unescapeCharacters(metaPart.get(0)), MetaUtils.unescapeCharacters(metaPart.get(1)));
+ List metaPart = META_SPLITTER.splitToList(this.permission.substring("meta.".length()));
+ meta = Maps.immutableEntry(MetaUtils.unescapeCharacters(metaPart.get(0)).intern(), MetaUtils.unescapeCharacters(metaPart.get(1)).intern());
}
isPrefix = NodeFactory.isPrefixNode(this.permission);
if (isPrefix) {
- List prefixPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("prefix.".length()));
+ List prefixPart = META_SPLITTER.splitToList(this.permission.substring("prefix.".length()));
Integer i = Integer.parseInt(prefixPart.get(0));
- prefix = Maps.immutableEntry(i, MetaUtils.unescapeCharacters(prefixPart.get(1)));
+ prefix = Maps.immutableEntry(i, MetaUtils.unescapeCharacters(prefixPart.get(1)).intern());
}
isSuffix = NodeFactory.isSuffixNode(this.permission);
if (isSuffix) {
- List suffixPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("suffix.".length()));
+ List suffixPart = META_SPLITTER.splitToList(this.permission.substring("suffix.".length()));
Integer i = Integer.parseInt(suffixPart.get(0));
- suffix = Maps.immutableEntry(i, MetaUtils.unescapeCharacters(suffixPart.get(1)));
+ suffix = Maps.immutableEntry(i, MetaUtils.unescapeCharacters(suffixPart.get(1)).intern());
}
resolvedShorthand = ImmutableList.copyOf(ShorthandParser.parseShorthand(getPermission()));
- serializedNode = calculateSerializedNode();
- MutableContextSet fullContexts = this.contexts.mutableCopy();
- if (isServerSpecific()) {
- fullContexts.add("server", this.server);
- }
- if (isWorldSpecific()) {
- fullContexts.add("world", this.world);
+ if (this.server != null || this.world != null) {
+ MutableContextSet fullContexts = this.contexts.mutableCopy();
+ if (this.server != null) {
+ fullContexts.add("server", this.server);
+ }
+ if (this.world != null) {
+ fullContexts.add("world", this.world);
+ }
+
+ this.fullContexts = fullContexts.makeImmutable();
+ } else {
+ this.fullContexts = this.contexts;
}
- this.fullContexts = fullContexts.makeImmutable();
this.optServer = Optional.ofNullable(this.server);
this.optWorld = Optional.ofNullable(this.world);
+ this.hashCode = calculateHashCode();
+ }
+
+ @Override
+ public boolean getValuePrimitive() {
+ return value;
}
@Override
@@ -225,19 +265,19 @@ public final class ImmutableNode implements Node {
@Override
public long getExpiryUnixTime() {
- Preconditions.checkState(isTemporary(), "Node does not have an expiry time.");
+ checkState(isTemporary(), "Node does not have an expiry time.");
return expireAt;
}
@Override
public Date getExpiry() {
- Preconditions.checkState(isTemporary(), "Node does not have an expiry time.");
+ checkState(isTemporary(), "Node does not have an expiry time.");
return new Date(expireAt * 1000L);
}
@Override
public long getSecondsTilExpiry() {
- Preconditions.checkState(isTemporary(), "Node does not have an expiry time.");
+ checkState(isTemporary(), "Node does not have an expiry time.");
return expireAt - DateUtil.unixSecondsNow();
}
@@ -253,7 +293,7 @@ public final class ImmutableNode implements Node {
@Override
public String getGroupName() {
- Preconditions.checkState(isGroupNode(), "Node is not a group node");
+ checkState(isGroupNode(), "Node is not a group node");
return groupName;
}
@@ -274,7 +314,7 @@ public final class ImmutableNode implements Node {
@Override
public Map.Entry getMeta() {
- Preconditions.checkState(isMeta(), "Node is not a meta node");
+ checkState(isMeta(), "Node is not a meta node");
return meta;
}
@@ -285,7 +325,7 @@ public final class ImmutableNode implements Node {
@Override
public Map.Entry getPrefix() {
- Preconditions.checkState(isPrefix(), "Node is not a prefix node");
+ checkState(isPrefix(), "Node is not a prefix node");
return prefix;
}
@@ -296,7 +336,7 @@ public final class ImmutableNode implements Node {
@Override
public Map.Entry getSuffix() {
- Preconditions.checkState(isSuffix(), "Node is not a suffix node");
+ checkState(isSuffix(), "Node is not a suffix node");
return suffix;
}
@@ -395,7 +435,10 @@ public final class ImmutableNode implements Node {
}
@Override
- public String toSerializedNode() {
+ public synchronized String toSerializedNode() {
+ if (serializedNode == null) {
+ serializedNode = calculateSerializedNode();
+ }
return serializedNode;
}
@@ -403,16 +446,11 @@ public final class ImmutableNode implements Node {
StringBuilder builder = new StringBuilder();
if (server != null) {
- builder.append(NodeFactory.escapeDelimiters(server, "/", "-"));
-
- if (world != null) {
- builder.append("-").append(NodeFactory.escapeDelimiters(world, "/", "-"));
- }
+ builder.append(NodeFactory.escapeDelimiters(server, SERVER_WORLD_DELIMS));
+ if (world != null) builder.append("-").append(NodeFactory.escapeDelimiters(world, SERVER_WORLD_DELIMS));
builder.append("/");
} else {
- if (world != null) {
- builder.append("global-").append(NodeFactory.escapeDelimiters(world, "/", "-")).append("/");
- }
+ if (world != null) builder.append("global-").append(NodeFactory.escapeDelimiters(world, SERVER_WORLD_DELIMS)).append("/");
}
if (!contexts.isEmpty()) {
@@ -420,162 +458,110 @@ public final class ImmutableNode implements Node {
for (Map.Entry entry : contexts.toSet()) {
builder.append(NodeFactory.escapeDelimiters(entry.getKey(), "=", "(", ")", ",")).append("=").append(NodeFactory.escapeDelimiters(entry.getValue(), "=", "(", ")", ",")).append(",");
}
-
builder.deleteCharAt(builder.length() - 1);
builder.append(")");
}
builder.append(NodeFactory.escapeDelimiters(permission, "/", "-", "$", "(", ")", "=", ","));
-
- if (expireAt != 0L) {
- builder.append("$").append(expireAt);
- }
-
+ if (expireAt != 0L) builder.append("$").append(expireAt);
return builder.toString();
}
+ @SuppressWarnings("StringEquality")
+ @Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Node)) return false;
final Node other = (Node) o;
- if (!this.permission.equals(other.getPermission())) return false;
- if (!this.getValue().equals(other.getValue())) return false;
+ if (this.permission != other.getPermission()) return false;
+ if (this.value != other.getValuePrimitive()) return false;
if (this.override != other.isOverride()) return false;
- final String thisServer = this.getServer().orElse(null);
+ final String thisServer = this.server;
final String otherServer = other.getServer().orElse(null);
if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false;
- final String thisWorld = this.getWorld().orElse(null);
+ final String thisWorld = this.world;
final String otherWorld = other.getWorld().orElse(null);
if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) return false;
- final long thisExpireAt = this.isTemporary() ? this.getExpiryUnixTime() : 0L;
final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L;
-
- return thisExpireAt == otherExpireAt && this.getContexts().equals(other.getContexts());
+ return this.expireAt == otherExpireAt && this.getContexts().equals(other.getContexts());
}
+ @Override
public int hashCode() {
+ return this.hashCode;
+ }
+
+ private int calculateHashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.permission.hashCode();
- result = result * PRIME + Boolean.hashCode(this.value);
+ result = result * PRIME + (this.value ? 79 : 97);
result = result * PRIME + (this.override ? 79 : 97);
-
- final String server = this.getServer().orElse(null);
- result = result * PRIME + (server == null ? 43 : server.hashCode());
-
- final String world = this.getWorld().orElse(null);
- result = result * PRIME + (world == null ? 43 : world.hashCode());
-
+ result = result * PRIME + (this.server == null ? 43 : this.server.hashCode());
+ result = result * PRIME + (this.world == null ? 43 : this.world.hashCode());
result = result * PRIME + (int) (this.expireAt >>> 32 ^ this.expireAt);
result = result * PRIME + this.contexts.hashCode();
return result;
}
+ @SuppressWarnings("StringEquality")
@Override
public boolean equalsIgnoringValue(Node other) {
- if (!other.getPermission().equalsIgnoreCase(this.getPermission())) {
- return false;
- }
+ if (this.permission != other.getPermission()) return false;
+ if (this.override != other.isOverride()) return false;
- if (other.isTemporary() != this.isTemporary()) {
- return false;
- }
+ final String thisServer = this.server;
+ final String otherServer = other.getServer().orElse(null);
+ if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false;
- if (this.isTemporary()) {
- if (other.getExpiryUnixTime() != this.getExpiryUnixTime()) {
- return false;
- }
- }
+ final String thisWorld = this.world;
+ final String otherWorld = other.getWorld().orElse(null);
+ if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) 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;
- }
-
- return other.getContexts().equals(this.getContexts());
+ final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L;
+ return this.expireAt == otherExpireAt && this.getContexts().equals(other.getContexts());
}
+ @SuppressWarnings("StringEquality")
@Override
public boolean almostEquals(Node other) {
- if (!other.getPermission().equalsIgnoreCase(this.getPermission())) {
- return false;
- }
+ if (this.permission != other.getPermission()) return false;
+ if (this.override != other.isOverride()) return false;
- if (other.isTemporary() != this.isTemporary()) {
+ final String thisServer = this.server;
+ final String otherServer = other.getServer().orElse(null);
+ if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer))
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;
- }
+ final String thisWorld = this.world;
+ final String otherWorld = other.getWorld().orElse(null);
+ return (thisWorld == null ? otherWorld == null : thisWorld.equals(otherWorld)) &&
+ this.isTemporary() == other.isTemporary() &&
+ this.getContexts().equals(other.getContexts());
- if (other.getWorld().isPresent() == this.getWorld().isPresent()) {
- if (other.getWorld().isPresent()) {
- if (!other.getWorld().get().equalsIgnoreCase(this.getWorld().get())) {
- return false;
- }
- }
- } else {
- return false;
- }
-
- return other.getContexts().equals(this.getContexts());
}
+ @SuppressWarnings("StringEquality")
@Override
public boolean equalsIgnoringValueOrTemp(Node other) {
- if (!other.getPermission().equalsIgnoreCase(this.getPermission())) {
- return false;
- }
+ if (this.permission != other.getPermission()) return false;
+ if (this.override != other.isOverride()) return false;
- if (other.getServer().isPresent() == this.getServer().isPresent()) {
- if (other.getServer().isPresent()) {
- if (!other.getServer().get().equalsIgnoreCase(this.getServer().get())) {
- return false;
- }
- }
- } else {
+ final String thisServer = this.server;
+ final String otherServer = other.getServer().orElse(null);
+ if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer))
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;
- }
-
- return other.getContexts().equals(this.getContexts());
+ final String thisWorld = this.world;
+ final String otherWorld = other.getWorld().orElse(null);
+ return (thisWorld == null ? otherWorld == null : thisWorld.equals(otherWorld)) &&
+ this.getContexts().equals(other.getContexts());
}
@Override
@@ -603,39 +589,33 @@ public final class ImmutableNode implements Node {
}
for (String s : expandedThisStr) {
- if (p.matcher(s).matches()) {
- return true;
- }
+ if (p.matcher(s).matches()) return true;
}
return false;
}
if (thisStr.toLowerCase().startsWith("r=") && applyRegex) {
Pattern p = PatternCache.compile(thisStr.substring(2));
- if (p == null) {
- return false;
- }
+ if (p == null) return false;
for (String s : expandedStr) {
- if (p.matcher(s).matches()) {
- return true;
- }
+ if (p.matcher(s).matches()) return true;
}
return false;
}
- if (expandedStr.size() <= 1 && expandedThisStr.size() <= 1) {
- return false;
- }
+ if (expandedStr.size() <= 1 && expandedThisStr.size() <= 1) return false;
for (String t : expandedThisStr) {
for (String s : expandedStr) {
- if (t.equalsIgnoreCase(s)) {
- return true;
- }
+ if (t.equalsIgnoreCase(s)) return true;
}
}
return false;
}
+ private static String internString(String s) {
+ return s == null ? null : s.intern();
+ }
+
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeBuilder.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeBuilder.java
index 683edf99..fc2359d3 100644
--- a/common/src/main/java/me/lucko/luckperms/common/node/NodeBuilder.java
+++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeBuilder.java
@@ -82,7 +82,7 @@ class NodeBuilder implements Node.Builder {
NodeBuilder(Node other) {
this.permission = other.getPermission();
- this.value = other.getValue();
+ this.value = other.getValuePrimitive();
this.override = other.isOverride();
this.server = other.getServer().orElse(null);
this.world = other.getWorld().orElse(null);
diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java
index f8c8797f..f1e3a3b7 100644
--- a/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java
+++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java
@@ -151,7 +151,7 @@ public class NodeFactory {
return appendContextToCommand(sb, node).toString();
}
- if (node.getValue() && (node.isPrefix() || node.isSuffix())) {
+ if (node.getValuePrimitive() && (node.isPrefix() || node.isSuffix())) {
ChatMetaType type = node.isPrefix() ? ChatMetaType.PREFIX : ChatMetaType.SUFFIX;
String typeName = type.name().toLowerCase();
@@ -171,7 +171,7 @@ public class NodeFactory {
return appendContextToCommand(sb, node).toString();
}
- if (node.getValue() && node.isMeta()) {
+ if (node.getValuePrimitive() && node.isMeta()) {
sb.append(node.isTemporary() ? (set ? "meta settemp " : "meta unsettemp ") : (set ? "meta set " : "meta unset "));
if (node.getMeta().getKey().contains(" ")) {
@@ -204,7 +204,7 @@ public class NodeFactory {
sb.append(node.getPermission());
}
if (set) {
- sb.append(" ").append(node.getValue());
+ sb.append(" ").append(node.getValuePrimitive());
if (node.isTemporary()) {
sb.append(" ").append(node.getExpiryUnixTime());
diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeHeldPermission.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeHeldPermission.java
index 09ad2831..5e81f2cd 100644
--- a/common/src/main/java/me/lucko/luckperms/common/node/NodeHeldPermission.java
+++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeHeldPermission.java
@@ -33,6 +33,7 @@ import com.google.common.collect.Multimap;
import me.lucko.luckperms.api.HeldPermission;
import me.lucko.luckperms.api.Node;
+import me.lucko.luckperms.api.context.ContextSet;
import java.util.Optional;
import java.util.OptionalLong;
@@ -55,7 +56,7 @@ public final class NodeHeldPermission implements HeldPermission {
@Override
public boolean getValue() {
- return node.getValue();
+ return node.getValuePrimitive();
}
@Override
@@ -78,6 +79,11 @@ public final class NodeHeldPermission implements HeldPermission {
return node.getContexts().toMultimap();
}
+ @Override
+ public ContextSet getContexts() {
+ return node.getContexts();
+ }
+
@Override
public Node asNode() {
return node;
diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java
index 19644f52..c2293923 100644
--- a/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java
+++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java
@@ -64,7 +64,7 @@ public final class NodeModel {
public static NodeModel fromNode(Node node) {
return NodeModel.of(
node.getPermission(),
- node.getValue(),
+ node.getValuePrimitive(),
node.getServer().orElse("global"),
node.getWorld().orElse("global"),
node.isTemporary() ? node.getExpiryUnixTime() : 0L,
diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java
index 4e1fedac..c63bf45b 100644
--- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java
+++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java
@@ -220,12 +220,14 @@ public interface LuckPermsPlugin {
/**
* Gets the name or "brand" of the running platform
+ *
* @return the server brand
*/
String getServerName();
/**
* Gets the version of the running platform
+ *
* @return the server version
*/
String getServerVersion();
diff --git a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java
index 7215786f..3e95ee93 100644
--- a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java
+++ b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java
@@ -57,7 +57,7 @@ public class ParentsByWeightHolder extends StoredHolder {
List groups = new ArrayList<>();
for (Node node : user.filterNodes(contextSet)) {
- if (!node.getValue() || !node.isGroupNode()) {
+ if (!node.getValuePrimitive() || !node.isGroupNode()) {
continue;
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java
index df3a5a68..a9ace918 100644
--- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java
+++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java
@@ -755,7 +755,7 @@ public class MongoDBBacking extends AbstractBacking {
public static Map exportToLegacy(Iterable nodes) {
Map m = new HashMap<>();
for (Node node : nodes) {
- m.put(node.toSerializedNode(), node.getValue());
+ m.put(node.toSerializedNode(), node.getValuePrimitive());
}
return m;
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java
index 123d425b..76f0a30d 100644
--- a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java
+++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java
@@ -31,8 +31,9 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import me.lucko.luckperms.api.Tristate;
-import me.lucko.luckperms.api.caching.PermissionData;
+import me.lucko.luckperms.common.caching.PermissionCache;
import me.lucko.luckperms.common.utils.PasteUtils;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -194,7 +195,7 @@ public class TreeView {
* @return the url, or null
* @see PasteUtils#paste(String, List)
*/
- public String uploadPasteData(String version, String username, PermissionData checker) {
+ public String uploadPasteData(String version, String username, PermissionCache checker) {
// only paste if there is actually data here
if (!hasData()) {
throw new IllegalStateException();
@@ -213,7 +214,7 @@ public class TreeView {
for (Map.Entry e : ret) {
// lookup a permission value for the node
- Tristate tristate = checker.getPermissionValue(e.getValue());
+ Tristate tristate = checker.getPermissionValue(e.getValue(), CheckOrigin.INTERNAL);
// append the data to the paste
builder.add(getTristateDiffPrefix(tristate) + e.getKey() + e.getValue());
diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java b/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java
index 8fb0acfb..70ff8345 100644
--- a/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java
+++ b/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java
@@ -41,7 +41,7 @@ import java.util.concurrent.CompletableFuture;
@UtilityClass
public class LoginHelper {
- public static void loadUser(LuckPermsPlugin plugin, UUID u, String username, boolean joinUuidSave) {
+ public static User loadUser(LuckPermsPlugin plugin, UUID u, String username, boolean joinUuidSave) {
final long startTime = System.currentTimeMillis();
final UuidCache cache = plugin.getUuidCache();
@@ -97,6 +97,8 @@ public class LoginHelper {
if (time >= 1000) {
plugin.getLog().warn("Processing login for " + username + " took " + time + "ms.");
}
+
+ return user;
}
public static void refreshPlayer(LuckPermsPlugin plugin, UUID uuid) {
diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/TextUtils.java b/common/src/main/java/me/lucko/luckperms/common/utils/TextUtils.java
index 876c56e6..c99831a5 100644
--- a/common/src/main/java/me/lucko/luckperms/common/utils/TextUtils.java
+++ b/common/src/main/java/me/lucko/luckperms/common/utils/TextUtils.java
@@ -33,13 +33,18 @@ import net.kyori.text.serializer.ComponentSerializers;
import java.util.Arrays;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
@SuppressWarnings("deprecation")
@UtilityClass
public class TextUtils {
public String joinNewline(String... strings) {
- return Arrays.stream(strings).collect(Collectors.joining("\n"));
+ return joinNewline(Arrays.stream(strings));
+ }
+
+ public String joinNewline(Stream strings) {
+ return strings.collect(Collectors.joining("\n"));
}
public TextComponent fromLegacy(String input, char character) {
diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java
index e0705ddd..e394ad78 100644
--- a/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java
+++ b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java
@@ -29,6 +29,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import me.lucko.luckperms.api.Tristate;
+import me.lucko.luckperms.api.context.ImmutableContextSet;
/**
* Holds the data from a permission check
@@ -37,11 +38,26 @@ import me.lucko.luckperms.api.Tristate;
@AllArgsConstructor
public class CheckData {
+ /**
+ * The origin of the check
+ */
+ private final CheckOrigin checkOrigin;
+
/**
* The name of the entity which was checked
*/
private final String checkTarget;
+ /**
+ * The contexts where the check took place
+ */
+ private final ImmutableContextSet checkContext;
+
+ /**
+ * The stack trace when the check took place
+ */
+ private final StackTraceElement[] checkTrace;
+
/**
* The permission which was checked for
*/
diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/CheckOrigin.java b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckOrigin.java
new file mode 100644
index 00000000..6108fdfa
--- /dev/null
+++ b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckOrigin.java
@@ -0,0 +1,60 @@
+/*
+ * This file is part of LuckPerms, licensed under the MIT License.
+ *
+ * Copyright (c) lucko (Luck)
+ * Copyright (c) contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.lucko.luckperms.common.verbose;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Represents the origin of a permission check
+ */
+@Getter
+@AllArgsConstructor
+public enum CheckOrigin {
+
+ /**
+ * Indicates the check was caused by a 'hasPermission' check on the platform
+ */
+ PLATFORM_PERMISSION_CHECK('C'),
+
+ /**
+ * Indicates the check was caused by a 'hasPermissionSet' type check on the platform
+ */
+ PLATFORM_LOOKUP_CHECK('L'),
+
+ /**
+ * Indicates the check was caused by an API call
+ */
+ API('A'),
+
+ /**
+ * Indicates the check was caused by a LuckPerms internal
+ */
+ INTERNAL('I');
+
+ private final char code;
+
+}
diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java
index f0848839..6cfb1065 100644
--- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java
+++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java
@@ -28,6 +28,7 @@ package me.lucko.luckperms.common.verbose;
import lombok.Setter;
import me.lucko.luckperms.api.Tristate;
+import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.commands.sender.Sender;
import java.util.Map;
@@ -70,18 +71,23 @@ public class VerboseHandler implements Runnable {
* The check data is added to a queue to be processed later, to avoid blocking
* the main thread each time a permission check is made.
*
+ * @param checkOrigin the origin of the check
* @param checkTarget the target of the permission check
+ * @param checkContext the contexts where the check occurred
* @param permission the permission which was checked for
* @param result the result of the permission check
*/
- public void offerCheckData(String checkTarget, String permission, Tristate result) {
+ public void offerCheckData(CheckOrigin checkOrigin, String checkTarget, ContextSet checkContext, String permission, Tristate result) {
// don't bother even processing the check if there are no listeners registered
if (!listening) {
return;
}
+ //noinspection ThrowableNotThrown
+ StackTraceElement[] trace = new Exception().getStackTrace();
+
// add the check data to a queue to be processed later.
- queue.offer(new CheckData(checkTarget, permission, result));
+ queue.offer(new CheckData(checkOrigin, checkTarget, checkContext.makeImmutable(), trace, permission, result));
}
/**
diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java
index b6e33367..b0e76c09 100644
--- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java
+++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java
@@ -33,9 +33,15 @@ import com.google.common.collect.Maps;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.commands.sender.Sender;
+import me.lucko.luckperms.common.commands.utils.Util;
+import me.lucko.luckperms.common.constants.Constants;
import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.utils.DateUtil;
import me.lucko.luckperms.common.utils.PasteUtils;
+import me.lucko.luckperms.common.utils.TextUtils;
+
+import net.kyori.text.TextComponent;
+import net.kyori.text.event.HoverEvent;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -55,6 +61,9 @@ public class VerboseListener {
// how much data should we store before stopping.
private static final int DATA_TRUNCATION = 10000;
+ // how many traces should we add
+ private static final int TRACE_DATA_TRUNCATION = 250;
+
// the time when the listener was first registered
private final long startTime = System.currentTimeMillis();
@@ -98,7 +107,64 @@ public class VerboseListener {
}
if (notify) {
- Message.VERBOSE_LOG.send(notifiedSender, "&a" + data.getCheckTarget() + "&7 -- &a" + data.getPermission() + "&7 -- " + getTristateColor(data.getResult()) + data.getResult().name().toLowerCase() + "");
+ StringBuilder msgContent = new StringBuilder();
+ msgContent.append("&a")
+ .append(data.getCheckTarget());
+
+ if (notifiedSender.isConsole()) {
+ msgContent.append("&7 - &8[&2")
+ .append(data.getCheckOrigin().getCode())
+ .append("&8]");
+ }
+
+ msgContent.append("&7 - &a")
+ .append(data.getPermission())
+ .append("&7 - ")
+ .append(getTristateColor(data.getResult()))
+ .append(data.getResult().name().toLowerCase());
+
+ if (notifiedSender.isConsole()) {
+ msgContent.append("&7 - ").append(Util.contextSetToString(data.getCheckContext()));
+ }
+
+ if (notifiedSender.isConsole()) {
+ Message.VERBOSE_LOG.send(notifiedSender, msgContent.toString());
+ } else {
+ TextComponent textComponent = TextUtils.fromLegacy(Message.VERBOSE_LOG.asString(notifiedSender.getPlatform().getLocaleManager(), msgContent.toString()));
+ List hover = new ArrayList<>();
+ hover.add("&bOrigin: &2" + data.getCheckOrigin().name());
+ hover.add("&bContext: &r" + Util.contextSetToString(data.getCheckContext()));
+ hover.add("&bTrace: &r");
+
+ StackTraceElement[] checkTrace = data.getCheckTrace();
+
+ // how many lines have been printed
+ int count = 0;
+ // if we're printing elements yet
+ boolean printing = false;
+
+ for (StackTraceElement e : checkTrace) {
+ // start printing when we escape LP internals code
+ if (!printing && !e.getClassName().startsWith("me.lucko.luckperms.")) {
+ printing = true;
+ }
+
+ if (!printing) continue;
+ if (count >= 15) break;
+
+ hover.add("&7" + e.getClassName() + "." + e.getMethodName() + (e.getLineNumber() >= 0 ? ":" + e.getLineNumber() : ""));
+ count++;
+ }
+
+ if (checkTrace.length > 15) {
+ int remain = checkTrace.length - 15;
+ hover.add("&f... and " + remain + " more");
+ }
+
+ HoverEvent e = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline(hover.stream()), Constants.FORMAT_CHAR));
+ TextComponent msg = textComponent.toBuilder().applyDeep(comp -> comp.hoverEvent(e)).build();
+ notifiedSender.sendMessage(msg);
+ }
}
}
@@ -108,7 +174,7 @@ public class VerboseListener {
* @return the url
* @see PasteUtils#paste(String, List)
*/
- public String uploadPasteData() {
+ public String uploadPasteData(boolean showTraces) {
long now = System.currentTimeMillis();
String startDate = DATE_FORMAT.format(new Date(startTime));
String endDate = DATE_FORMAT.format(new Date(now));
@@ -132,9 +198,10 @@ public class VerboseListener {
.add("| Start Time | " + startDate + " |")
.add("| End Time | " + endDate + " |")
.add("| Duration | " + duration +" |")
- .add("| Count | **" + matchedCounter.get() + "** / " + counter + " |")
+ .add("| Count | **" + matchedCounter.get() + "** / " + counter.get() + " |")
.add("| User | " + notifiedSender.getName() + " |")
.add("| Filter | " + filter + " |")
+ .add("| Include traces | " + showTraces + " |")
.add("");
if (matchedCounter.get() > results.size()) {
@@ -142,8 +209,14 @@ public class VerboseListener {
prettyOutput.add("");
}
+ if (showTraces && results.size() > TRACE_DATA_TRUNCATION) {
+ prettyOutput.add("**WARN:** Result set exceeded size of " + TRACE_DATA_TRUNCATION + ". The traced output below was truncated to " + TRACE_DATA_TRUNCATION + " entries. ");
+ prettyOutput.add("Either refine the query using a more specific filter, or disable tracing by adding '--slim' to the end of the paste command.");
+ prettyOutput.add("");
+ }
+
prettyOutput.add("### Output")
- .add("Format: `` `` ``")
+ .add("Format: `` `` `` ``")
.add("")
.add("___")
.add("");
@@ -151,8 +224,49 @@ public class VerboseListener {
ImmutableList.Builder csvOutput = ImmutableList.builder()
.add("User,Permission,Result");
+ AtomicInteger printedCount = new AtomicInteger(0);
+
results.forEach(c -> {
- prettyOutput.add("`" + c.getCheckTarget() + "` - " + c.getPermission() + " - **" + c.getResult().toString() + "** ");
+ if (!showTraces) {
+ prettyOutput.add("`" + c.getCheckTarget() + "` - " + c.getPermission() + " - " + getTristateSymbol(c.getResult()) + " ");
+ } else if (printedCount.incrementAndGet() > TRACE_DATA_TRUNCATION) {
+ prettyOutput.add("
" + c.getCheckTarget() + "
- " + c.getPermission() + " - " + getTristateSymbol(c.getResult()));
+ } else {
+ prettyOutput.add("" + c.getCheckTarget() + "
- " + c.getPermission() + " - " + getTristateSymbol(c.getResult()) + "
");
+
+ prettyOutput.add("
Origin: " + c.getCheckOrigin().name() + "
");
+ prettyOutput.add("
Context: " + Util.stripColor(Util.contextSetToString(c.getCheckContext())) + "
");
+ prettyOutput.add("
Trace:
");
+
+ // add trace
+ StackTraceElement[] checkTrace = c.getCheckTrace();
+
+ // how many lines have been printed
+ int count = 0;
+ // if we're printing elements yet
+ boolean printing = false;
+
+ for (StackTraceElement e : checkTrace) {
+ // start printing when we escape LP internals code
+ if (!printing && !e.getClassName().startsWith("me.lucko.luckperms.")) {
+ printing = true;
+ }
+
+ if (!printing) continue;
+ if (count >= 30) break;
+
+ prettyOutput.add(e.getClassName() + "." + e.getMethodName() + (e.getLineNumber() >= 0 ? ":" + e.getLineNumber() : ""));
+ count++;
+ }
+
+ if (checkTrace.length > 30) {
+ int remain = checkTrace.length - 30;
+ prettyOutput.add("... and " + remain + " more");
+ }
+
+ prettyOutput.add("
");
+ }
+
csvOutput.add(escapeCommas(c.getCheckTarget()) + "," + escapeCommas(c.getPermission()) + "," + c.getResult().name().toLowerCase());
});
results.clear();
@@ -180,4 +294,15 @@ public class VerboseListener {
}
}
+ private static String getTristateSymbol(Tristate tristate) {
+ switch (tristate) {
+ case TRUE:
+ return "✔️";
+ case FALSE:
+ return "❌";
+ default:
+ return "❔";
+ }
+ }
+
}
diff --git a/pom.xml b/pom.xml
index a2cea2a7..8a39b366 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
me.lucko.luckperms
luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
api
@@ -47,7 +47,7 @@
true
- 3.3
+ 3.4
${git.closest.tag.commit.count}
diff --git a/sponge/pom.xml b/sponge/pom.xml
index 6ab38f93..e933f41d 100644
--- a/sponge/pom.xml
+++ b/sponge/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
4.0.0
diff --git a/sponge/sponge-service-api6/pom.xml b/sponge/sponge-service-api6/pom.xml
index 0273ebc0..215a3155 100644
--- a/sponge/sponge-service-api6/pom.xml
+++ b/sponge/sponge-service-api6/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
../../pom.xml
4.0.0
diff --git a/sponge/sponge-service-api7/pom.xml b/sponge/sponge-service-api7/pom.xml
index db343c8e..d3b4749a 100644
--- a/sponge/sponge-service-api7/pom.xml
+++ b/sponge/sponge-service-api7/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
../../pom.xml
4.0.0
@@ -54,7 +54,7 @@
org.spongepowered
spongeapi
- 7.0.0-20170716.103113-1
+ 7.0.0-SNAPSHOT
provided
diff --git a/sponge/sponge-service/pom.xml b/sponge/sponge-service/pom.xml
index 0bcede21..bf035404 100644
--- a/sponge/sponge-service/pom.xml
+++ b/sponge/sponge-service/pom.xml
@@ -5,7 +5,7 @@
luckperms
me.lucko.luckperms
- 3.3-SNAPSHOT
+ 3.4-SNAPSHOT
../../pom.xml
4.0.0
@@ -33,7 +33,7 @@
org.spongepowered
spongeapi
- 7.0.0-20170716.103113-1
+ 7.0.0-SNAPSHOT
provided
true
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java
index 68b086a4..3bc1de85 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java
@@ -72,6 +72,7 @@ public class SpongeListener {
Listening on AFTER_PRE priority to allow plugins to modify username / UUID data here. (auth plugins) */
final GameProfile p = e.getProfile();
+ final String username = p.getName().orElseThrow(() -> new RuntimeException("No username present for user " + p.getUniqueId()));
if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
plugin.getLog().info("Processing auth event for " + p.getUniqueId() + " - " + p.getName());
@@ -107,7 +108,8 @@ public class SpongeListener {
- creating a user instance in the UserManager for this connection.
- setting up cached data. */
try {
- LoginHelper.loadUser(plugin, p.getUniqueId(), p.getName().orElseThrow(() -> new RuntimeException("No username present for user " + p.getUniqueId())), false);
+ User user = LoginHelper.loadUser(plugin, p.getUniqueId(), username, false);
+ plugin.getApiProvider().getEventFactory().handleUserLoginProcess(p.getUniqueId(), username, user);
} catch (Exception ex) {
ex.printStackTrace();
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeCalculatorFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeCalculatorFactory.java
index 5a113e54..2e09ad31 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeCalculatorFactory.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeCalculatorFactory.java
@@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
+import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.processors.MapProcessor;
@@ -70,7 +71,11 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new DefaultsProcessor(plugin.getService(), contexts.getContexts().makeImmutable()));
}
- return registerCalculator(new PermissionCalculator(plugin, user.getFriendlyName(), processors.build()));
+ return registerCalculator(new PermissionCalculator(
+ plugin,
+ PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()),
+ processors.build()
+ ));
}
@Override
@@ -79,7 +84,7 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
ret.add("Map");
if (plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_IMPLICIT_WILDCARDS)) ret.add("SpongeWildcard");
if (plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) ret.add("Regex");
- if (plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) ret.add("Wildcards");
+ if (plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) ret.add("Wildcard");
if (plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_DEFAULT_SUBJECTS)) ret.add("Defaults");
return ret.build();
}
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java
index 0ef0088a..d9fc9a9e 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java
@@ -196,8 +196,9 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
}
@Override
- public void cleanup(User user) {
+ public boolean cleanup(User user) {
// Do nothing - this instance uses other means in order to cleanup
+ return false;
}
@Override
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java
index 38c77022..5b3f7c1d 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java
@@ -97,7 +97,7 @@ public class SpongeGroup extends Group {
// TODO move this away from NodeTree
Map permissions = getParent().getAllNodes(ExtractedContexts.generate(getPlugin().getService().calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode)
- .collect(Collectors.toMap(Node::getPermission, Node::getValue));
+ .collect(Collectors.toMap(Node::getPermission, Node::getValuePrimitive));
return NodeTree.of(permissions);
});
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java
index bb6144d7..3338a708 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java
@@ -34,6 +34,7 @@ import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.model.User;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.sponge.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.LuckPermsSubjectData;
@@ -129,7 +130,7 @@ public class SpongeUser extends User {
@Override
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PERMISSION_VALUE)) {
- return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission);
+ return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK);
}
}
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java
index dc7dee63..1f7b9190 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java
@@ -81,7 +81,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
for (Map.Entry> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) {
ImmutableMap.Builder results = ImmutableMap.builder();
for (Node n : e.getValue()) {
- results.put(n.getPermission(), n.getValue());
+ results.put(n.getPermission(), n.getValuePrimitive());
}
perms.put(e.getKey(), results);
}
@@ -322,7 +322,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
Map minSuffixPriority = new HashMap<>();
for (Node n : enduring ? holder.getEnduringNodes().values() : holder.getTransientNodes().values()) {
- if (!n.getValue()) continue;
+ if (!n.getValuePrimitive()) continue;
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue;
ImmutableContextSet immutableContexts = n.getFullContexts().makeImmutable();
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java
index 4ab02667..d42d1177 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java
@@ -39,8 +39,10 @@ import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.calculators.PermissionCalculator;
+import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
import me.lucko.luckperms.common.processors.MapProcessor;
import me.lucko.luckperms.common.processors.PermissionProcessor;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.sponge.processors.SpongeWildcardProcessor;
import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject;
@@ -84,7 +86,7 @@ public class CalculatedSubjectData implements LPSubjectData {
processors.add(new MapProcessor());
processors.add(new SpongeWildcardProcessor());
- CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(service.getPlugin(), calculatorDisplayName, processors.build()));
+ CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(service.getPlugin(), PermissionCalculatorMetadata.of(calculatorDisplayName, contexts), processors.build()));
holder.setPermissions(flattenMap(contexts, permissions));
return holder;
@@ -100,7 +102,7 @@ public class CalculatedSubjectData implements LPSubjectData {
}
public Tristate getPermissionValue(ContextSet contexts, String permission) {
- return permissionCache.get(contexts).getCalculator().getPermissionValue(permission);
+ return permissionCache.get(contexts).getCalculator().getPermissionValue(permission, CheckOrigin.INTERNAL);
}
public void replacePermissions(Map> map) {
diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java
index 60484511..5bc7ec1e 100644
--- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java
+++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java
@@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.buffers.BufferedRequest;
+import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.ProxyFactory;
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
@@ -247,7 +248,7 @@ public class PersistedSubject implements LPSubject {
public Tristate getPermissionValue(@NonNull ImmutableContextSet contexts, @NonNull String node) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) {
Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts));
- service.getPlugin().getVerboseHandler().offerCheckData("local:" + getParentCollection().getIdentifier() + "/" + identifier, node, t);
+ service.getPlugin().getVerboseHandler().offerCheckData(CheckOrigin.INTERNAL, "local:" + getParentCollection().getIdentifier() + "/" + identifier, contexts, node, t);
return t;
}
}