Add tracing to /lp verbose, API updates/cleanup, add login process event, and utilise string interning for faster context/node comparisons

This commit is contained in:
Luck 2017-09-10 21:23:54 +01:00
parent 368700156c
commit 096885d91f
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
89 changed files with 1389 additions and 728 deletions

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -32,49 +32,59 @@ import java.util.Optional;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* Singleton for the {@link LuckPermsApi}. * Provides static access to the {@link LuckPermsApi}.
* *
* <p>Ideally, the ServiceManager for the platform should be used to obtain and cache an instance, however, this can be * <p>Ideally, the ServiceManager for the platform should be used to obtain an instance,
* used if you need static access.</p> * however, this provider can be used if you need static access.</p>
*/ */
public final class LuckPerms { public final class LuckPerms {
private static LuckPermsApi api = null; private static LuckPermsApi instance = null;
/** /**
* Gets an instance of {@link LuckPermsApi}, throwing {@link IllegalStateException} if the API is not loaded. * Gets an instance of the {@link LuckPermsApi},
* throwing {@link IllegalStateException} if an instance is not yet loaded.
*
* <p>Will never return null.</p>
* *
* @return an api instance * @return an api instance
* @throws IllegalStateException if the api is not loaded * @throws IllegalStateException if the api is not loaded
*/ */
@Nonnull @Nonnull
public static LuckPermsApi getApi() { public static LuckPermsApi getApi() {
if (api == null) { if (instance == null) {
throw new IllegalStateException("API is not loaded."); throw new IllegalStateException("API is not loaded.");
} }
return api; return instance;
} }
/** /**
* Gets an instance of {@link LuckPermsApi}, if it is loaded. * Gets an instance of {@link LuckPermsApi}, if it is loaded.
* *
* <p> Unlike {@link LuckPerms#getApi}, this method will not throw an {@link IllegalStateException} if the API is * <p>Unlike {@link LuckPerms#getApi}, this method will not throw an
* not loaded, rather return an empty {@link Optional}. * {@link IllegalStateException} if an instance is not yet loaded, rather return
* an empty {@link Optional}.
* *
* @return an optional api instance * @return an optional api instance
*/ */
@Nonnull @Nonnull
public static Optional<LuckPermsApi> getApiSafe() { public static Optional<LuckPermsApi> getApiSafe() {
return Optional.ofNullable(api); return Optional.ofNullable(instance);
} }
/* method used by the implementation to set the singleton instance */ /**
static void registerProvider(LuckPermsApi luckPermsApi) { * Registers an instance of the {@link LuckPermsApi} with this provider.
api = luckPermsApi; *
* @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() { static void unregisterProvider() {
api = null; LuckPerms.instance = null;
} }
private LuckPerms() { private LuckPerms() {

View File

@ -28,6 +28,7 @@ package me.lucko.luckperms.api;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -104,4 +105,22 @@ public enum ChatMetaType {
@Nonnull @Nonnull
public abstract Map.Entry<Integer, String> getEntry(@Nonnull Node node); public abstract Map.Entry<Integer, String> getEntry(@Nonnull Node node);
/**
* Parses a ChatMetaType from the given node.
*
* @param node the node
* @return the parsed chat meta type
* @since 3.4
*/
@Nonnull
public static Optional<ChatMetaType> ofNode(@Nonnull Node node) {
if (node.isPrefix()) {
return Optional.of(PREFIX);
} else if (node.isSuffix()) {
return Optional.of(SUFFIX);
} else {
return Optional.empty();
}
}
} }

View File

@ -30,11 +30,29 @@ import me.lucko.luckperms.exceptions.ObjectLacksException;
import java.util.function.Supplier; import java.util.function.Supplier;
/**
* Represents the result of a mutation call.
*/
public enum DataMutateResult { public enum DataMutateResult {
/**
* Indicates the mutation was a success
*/
SUCCESS(true, null), SUCCESS(true, null),
/**
* Indicates the mutation failed because the subject already has something
*/
ALREADY_HAS(false, ObjectAlreadyHasException::new), ALREADY_HAS(false, ObjectAlreadyHasException::new),
/**
* Indicates the mutation failed because the subject lacks something
*/
LACKS(false, ObjectLacksException::new), LACKS(false, ObjectLacksException::new),
/**
* Indicates the mutation failed
*/
FAIL(false, RuntimeException::new); FAIL(false, RuntimeException::new);
private boolean value; private boolean value;
@ -51,10 +69,35 @@ public enum DataMutateResult {
} }
} }
/**
* Gets a boolean representation of the result.
*
* @return a boolean representation
*/
public boolean asBoolean() { public boolean asBoolean() {
return value; 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 // allows us to throw checked exceptions without declaring it, as #throwException throws a number of
// exception types. // exception types.
private static void sneakyThrow(Throwable t) { private static void sneakyThrow(Throwable t) {

View File

@ -35,7 +35,7 @@ import java.util.OptionalInt;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* A group which holds permission data. * An inheritable holder of permission data.
*/ */
public interface Group extends PermissionHolder { public interface Group extends PermissionHolder {

View File

@ -27,13 +27,15 @@ package me.lucko.luckperms.api;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* A relationship between a Holder and a permission * A relationship between a PermissionHolder and a permission
* *
* @param <T> the identifier type of the holder * @param <T> the identifier type of the holder
* @since 2.17 * @since 2.17
@ -90,10 +92,19 @@ public interface HeldPermission<T> {
* Gets the context for the permission. * Gets the context for the permission.
* *
* @return the context * @return the context
* @deprecated in favour of {@link #getContexts()}.
*/ */
@Nonnull @Nonnull
@Deprecated
Multimap<String, String> getContext(); Multimap<String, String> getContext();
/**
* Gets the extra context for the permission.
*
* @return the extra context
*/
ContextSet getContexts();
/** /**
* Converts this permission into a Node * Converts this permission into a Node
* *

View File

@ -32,104 +32,119 @@ import java.util.Map;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* Read-only access to the LuckPerms configuration settings * Wrapper around parts of the LuckPerms configuration file
*/ */
public interface LPConfiguration { public interface LPConfiguration {
/** /**
* Returns the name of this server * Gets the name of this server
*
* @return the name of this server * @return the name of this server
*/ */
@Nonnull @Nonnull
String getServer(); 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 * @return how often a sync task will run in minutes
*/ */
int getSyncTime(); 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 * @return if the users on this server will have their global permissions applied
*/ */
boolean getIncludeGlobalPerms(); 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 * @return if the users on this server will have their global world permissions applied
* @since 2.9 * @since 2.9
*/ */
boolean getIncludeGlobalWorldPerms(); 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 * @return true if the platform is applying global groups
* @since 2.9 * @since 2.9
*/ */
boolean getApplyGlobalGroups(); 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 * @return true if the platform is applying global world groups
* @since 2.9 * @since 2.9
*/ */
boolean getApplyGlobalWorldGroups(); boolean getApplyGlobalWorldGroups();
/** /**
* Returns the online mode setting * Gets the online mode setting
*
* @return the online mode setting * @return the online mode setting
*/ */
boolean getOnlineMode(); boolean getOnlineMode();
/** /**
* Returns if LuckPerms is applying wildcard permissions * Gets if LuckPerms is applying wildcard permissions
*
* @return if LuckPerms is applying wildcard permissions * @return if LuckPerms is applying wildcard permissions
*/ */
boolean getApplyWildcards(); boolean getApplyWildcards();
/** /**
* Returns if LuckPerms is resolving and applying regex permissions * Returns if LuckPerms is resolving and applying regex permissions
*
* @return if LuckPerms is resolving and applying regex permissions * @return if LuckPerms is resolving and applying regex permissions
*/ */
boolean getApplyRegex(); boolean getApplyRegex();
/** /**
* Returns if LuckPerms is expanding shorthand permissions * Gets if LuckPerms is expanding shorthand permissions
*
* @return if LuckPerms is expanding shorthand permissions * @return if LuckPerms is expanding shorthand permissions
*/ */
boolean getApplyShorthand(); 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 * @return if LuckPerms will send notifications to users when permissions are modified
* @since 2.7 * @since 2.7
*/ */
boolean getLogNotify(); 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 * @return true if the vanilla op system is enabled
* @since 2.8 * @since 2.8
*/ */
boolean getEnableOps(); 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 * @return true if opped players are allowed to use LuckPerms commands
* @since 2.8 * @since 2.8
*/ */
boolean getCommandsAllowOp(); boolean getCommandsAllowOp();
/** /**
* Returns true if auto op is enabled * Gets if auto op is enabled
*
* @return true if auto op is enabled * @return true if auto op is enabled
* @since 2.9 * @since 2.9
*/ */
boolean getAutoOp(); 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 * @return the name of the server used within Vault operations
* @since 2.7 * @since 2.7
*/ */
@ -137,35 +152,40 @@ public interface LPConfiguration {
String getVaultServer(); 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 * @return true if global permissions should be considered when retrieving meta or player groups
* @since 2.7 * @since 2.7
*/ */
boolean getVaultIncludeGlobal(); 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 * @return the values set for data storage in the configuration
*/ */
@Nonnull @Nonnull
DatastoreConfiguration getDatastoreConfig(); 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 * @return the storage method string from the configuration
*/ */
@Nonnull @Nonnull
String getStorageMethod(); String getStorageMethod();
/** /**
* Returns true if split storage is enabled * Gets true if split storage is enabled
*
* @return true if split storage is enabled * @return true if split storage is enabled
* @since 2.7 * @since 2.7
*/ */
boolean getSplitStorage(); 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 * @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 * method. For example: key = user, value = json
* @since 2.7 * @since 2.7

View File

@ -25,6 +25,7 @@
package me.lucko.luckperms.api; package me.lucko.luckperms.api;
import me.lucko.luckperms.LuckPerms;
import me.lucko.luckperms.api.context.ContextCalculator; import me.lucko.luckperms.api.context.ContextCalculator;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.event.EventBus; import me.lucko.luckperms.api.event.EventBus;
@ -38,7 +39,13 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* The root API interface for LuckPerms * The LuckPerms API.
*
* <p>This interface is the base of the entire API package. All API functions
* are accessed via this interface.</p>
*
* <p>An instance can be obtained via {@link LuckPerms#getApi()}, or the platforms
* Services Manager.</p>
*/ */
public interface LuckPermsApi { public interface LuckPermsApi {

View File

@ -41,6 +41,7 @@ import java.util.Set;
*/ */
@Deprecated @Deprecated
public class MetaUtils { public class MetaUtils {
private static final String[] DELIMS = new String[]{".", "/", "-", "$"};
private static String escapeDelimiters(String s, String... delims) { private static String escapeDelimiters(String s, String... delims) {
for (String delim : delims) { for (String delim : delims) {
@ -68,7 +69,7 @@ public class MetaUtils {
throw new NullPointerException(); throw new NullPointerException();
} }
return escapeDelimiters(s, ".", "/", "-", "$"); return escapeDelimiters(s, DELIMS);
} }
/** /**
@ -86,7 +87,7 @@ public class MetaUtils {
s = s.replace("{SEP}", "."); s = s.replace("{SEP}", ".");
s = s.replace("{FSEP}", "/"); s = s.replace("{FSEP}", "/");
s = s.replace("{DSEP}", "$"); s = s.replace("{DSEP}", "$");
s = unescapeDelimiters(s, ".", "/", "-", "$"); s = unescapeDelimiters(s, DELIMS);
return s; return s;
} }
@ -161,7 +162,7 @@ public class MetaUtils {
node = escapeCharacters(node); node = escapeCharacters(node);
for (Node n : holder.getPermissions()) { 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.shouldApplyOnServer(server, includeGlobal, false)) continue;
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) continue; if (!n.shouldApplyOnWorld(world, includeGlobal, false)) continue;
@ -233,7 +234,7 @@ public class MetaUtils {
int priority = Integer.MIN_VALUE; int priority = Integer.MIN_VALUE;
String meta = null; String meta = null;
for (Node n : holder.getAllNodes(Contexts.allowAll())) { for (Node n : holder.getAllNodes(Contexts.allowAll())) {
if (!n.getValue()) continue; if (!n.getValuePrimitive()) continue;
if (!n.shouldApplyOnServer(server, includeGlobal, false)) continue; if (!n.shouldApplyOnServer(server, includeGlobal, false)) continue;
if (!n.shouldApplyOnWorld(world, includeGlobal, false)) continue; if (!n.shouldApplyOnWorld(world, includeGlobal, false)) continue;

View File

@ -37,16 +37,18 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* An immutable permission node * Represents a permission node.
* *
* <p>Use {@link LuckPermsApi#buildNode(String)} to get an instance.</p> * <p>All implementations of this interface must be immutable.</p>
*
* <p>Use the {@link NodeFactory} to obtain and construct instances.</p>
* *
* @since 2.6 * @since 2.6
*/ */
public interface Node extends Map.Entry<String, Boolean> { public interface Node extends Map.Entry<String, Boolean> {
/** /**
* Returns the actual permission string * Gets the permission string
* *
* @return the actual permission node * @return the actual permission node
*/ */
@ -54,36 +56,50 @@ public interface Node extends Map.Entry<String, Boolean> {
String getPermission(); String getPermission();
/** /**
* Gets what value the permission is set to. A negated node would return <code>false</code>. * Gets the value.
*
* <p>A negated node would return a value of <code>false</code>.</p>
* *
* @return the permission's value * @return the permission's value
*/ */
@Override @Override
@Nonnull @Nonnull
Boolean getValue(); default Boolean getValue() {
return getValuePrimitive();
}
/** /**
* Returns the value of this node as a tristate * Gets the value.
*
* <p>A negated node would return a value of <code>false</code>.</p>
*
* @return the permission's value
*/
boolean getValuePrimitive();
/**
* Gets the value of this node as a {@link Tristate}
* *
* @return the value of this node as a Tristate * @return the value of this node as a Tristate
*/ */
@Nonnull @Nonnull
default Tristate getTristate() { 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 * @return true if the node is negated
*/ */
default boolean isNegated() { default boolean isNegated() {
return !getValue(); return !getValuePrimitive();
} }
/** /**
* If this node is set to override explicitly. * Gets if this node is set to override explicitly.
* This value does not persist across saves, and is therefore only useful for transient nodes *
* <p>This value does not persist across saves, and is therefore only useful for transient nodes</p>
* *
* @return true if this node is set to override explicitly * @return true if this node is set to override explicitly
*/ */
@ -106,21 +122,21 @@ public interface Node extends Map.Entry<String, Boolean> {
Optional<String> getWorld(); Optional<String> getWorld();
/** /**
* Returns if this node is server specific * Gets if this node is server specific
* *
* @return true if this node is server specific * @return true if this node is server specific
*/ */
boolean isServerSpecific(); boolean isServerSpecific();
/** /**
* Returns if this node is server specific * Gets if this node is server specific
* *
* @return true if this node is server specific * @return true if this node is server specific
*/ */
boolean isWorldSpecific(); 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 * @return true if this node applies globally, and has no specific context
* @since 3.1 * @since 3.1
@ -128,7 +144,7 @@ public interface Node extends Map.Entry<String, Boolean> {
boolean appliesGlobally(); 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 * @return true if this node has specific context
* @since 3.1 * @since 3.1
@ -136,7 +152,7 @@ public interface Node extends Map.Entry<String, Boolean> {
boolean hasSpecificContext(); 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 includeGlobal if global server values should apply
* @param includeGlobalWorld if global world values should apply * @param includeGlobalWorld if global world values should apply
@ -150,7 +166,7 @@ public interface Node extends Map.Entry<String, Boolean> {
boolean shouldApply(boolean includeGlobal, boolean includeGlobalWorld, @Nullable String server, @Nullable String world, @Nullable ContextSet context, boolean applyRegex); 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 server the name of the server
* @param includeGlobal if global permissions should apply * @param includeGlobal if global permissions should apply
@ -160,7 +176,7 @@ public interface Node extends Map.Entry<String, Boolean> {
boolean shouldApplyOnServer(@Nullable String server, boolean includeGlobal, boolean applyRegex); 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 world the name of the world
* @param includeGlobal if global permissions should apply * @param includeGlobal if global permissions should apply
@ -170,7 +186,7 @@ public interface Node extends Map.Entry<String, Boolean> {
boolean shouldApplyOnWorld(@Nullable String world, boolean includeGlobal, boolean applyRegex); 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 context the context key value pairs
* @param worldAndServer if world and server contexts should be checked * @param worldAndServer if world and server contexts should be checked
@ -180,7 +196,7 @@ public interface Node extends Map.Entry<String, Boolean> {
boolean shouldApplyWithContext(@Nonnull ContextSet context, boolean worldAndServer); 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 * @param context the context key value pairs
* @return true if the node should apply * @return true if the node should apply
@ -211,8 +227,10 @@ public interface Node extends Map.Entry<String, Boolean> {
* *
* @param possibleNodes a list of possible permission nodes * @param possibleNodes a list of possible permission nodes
* @return a list of permissions that match this wildcard * @return a list of permissions that match this wildcard
* @deprecated as this is no longer used internally to resolve wildcards
*/ */
@Nonnull @Nonnull
@Deprecated
List<String> resolveWildcard(@Nonnull List<String> possibleNodes); List<String> resolveWildcard(@Nonnull List<String> possibleNodes);
/** /**
@ -224,14 +242,14 @@ public interface Node extends Map.Entry<String, Boolean> {
List<String> resolveShorthand(); List<String> 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 * @return true if this node will expire in the future
*/ */
boolean isTemporary(); boolean isTemporary();
/** /**
* Returns if this node will not expire * Gets if this node will not expire
* *
* @return true if this node will not expire * @return true if this node will not expire
*/ */
@ -240,7 +258,7 @@ public interface Node extends Map.Entry<String, Boolean> {
} }
/** /**
* 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 * @return the time in Unix time when this node will expire
* @throws IllegalStateException if the node is not temporary * @throws IllegalStateException if the node is not temporary
@ -248,7 +266,7 @@ public interface Node extends Map.Entry<String, Boolean> {
long getExpiryUnixTime() throws IllegalStateException; 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 * @return the {@link Date} when this node will expire
* @throws IllegalStateException if the node is not temporary * @throws IllegalStateException if the node is not temporary
@ -257,7 +275,7 @@ public interface Node extends Map.Entry<String, Boolean> {
Date getExpiry() throws IllegalStateException; 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 * @return the number of seconds until this permission will expire
* @throws IllegalStateException if the node is not temporary * @throws IllegalStateException if the node is not temporary
@ -265,8 +283,9 @@ public interface Node extends Map.Entry<String, Boolean> {
long getSecondsTilExpiry() throws IllegalStateException; long getSecondsTilExpiry() throws IllegalStateException;
/** /**
* Return true if the node has expired. * Gets if the node has expired.
* This also returns false if the node is not temporary *
* <p>This also returns false if the node is not temporary.</p>
* *
* @return true if this node has expired * @return true if this node has expired
*/ */
@ -301,14 +320,14 @@ public interface Node extends Map.Entry<String, Boolean> {
String toSerializedNode(); String toSerializedNode();
/** /**
* Returns if this is a group node * Gets if this is a group node
* *
* @return true if this is a group node * @return true if this is a group node
*/ */
boolean isGroupNode(); 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 * @return the name of the group
* @throws IllegalStateException if this is not a group node. See {@link #isGroupNode()} * @throws IllegalStateException if this is not a group node. See {@link #isGroupNode()}
@ -317,7 +336,7 @@ public interface Node extends Map.Entry<String, Boolean> {
String getGroupName() throws IllegalStateException; 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 * @return true if this node is a wildcard node
*/ */
@ -332,7 +351,7 @@ public interface Node extends Map.Entry<String, Boolean> {
int getWildcardLevel() throws IllegalStateException; 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 * @return true if this node is a meta node
*/ */
@ -348,7 +367,7 @@ public interface Node extends Map.Entry<String, Boolean> {
Map.Entry<String, String> getMeta() throws IllegalStateException; Map.Entry<String, String> 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 * @return true if this node is a prefix node
*/ */
@ -364,7 +383,7 @@ public interface Node extends Map.Entry<String, Boolean> {
Map.Entry<Integer, String> getPrefix() throws IllegalStateException; Map.Entry<Integer, String> 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 * @return true if this node is a suffix node
*/ */

View File

@ -28,27 +28,12 @@ package me.lucko.luckperms.api;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* Builds {@link Node} instances * Assists with constructing {@link Node} instances.
* *
* @since 2.17 * @since 2.17
*/ */
public interface NodeFactory { 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 * Creates a new node builder from a given base permission string
* *
@ -69,20 +54,6 @@ public interface NodeFactory {
@Nonnull @Nonnull
Node.Builder newBuilderFromExisting(@Nonnull Node other); 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 * Creates a node builder from a group
@ -141,4 +112,35 @@ public interface NodeFactory {
@Nonnull @Nonnull
Node.Builder makeSuffixNode(int priority, @Nonnull String suffix); Node.Builder makeSuffixNode(int priority, @Nonnull String suffix);
/**
* Creates a node from a serialised node string
*
* <p>This format is what was previously used in YAML/JSON storage files.</p>
*
* @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);
} }

View File

@ -43,14 +43,15 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* An object capable of holding permissions * An object which holds permissions.
* *
* <p>Any changes made will be lost unless the instance is saved back to the {@link Storage}.</p> * <p>Any changes made to permission holding objects will be lost unless the
* instance is saved back to the {@link Storage}.</p>
*/ */
public interface PermissionHolder { public interface PermissionHolder {
/** /**
* Gets the objects name * Gets the objects name.
* *
* <p>{@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} should normally be used instead of * <p>{@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} should normally be used instead of
* this method.</p> * this method.</p>
@ -123,7 +124,7 @@ public interface PermissionHolder {
SortedSet<? extends Node> getPermissions(); SortedSet<? extends Node> getPermissions();
/** /**
* Similar to {@link #getPermissions()}, except without transient permissions * Similar to {@link #getPermissions()}, except without transient permissions.
* *
* <p>Unlike transient permissions, enduring permissions will be saved to storage, and exist after the session.</p> * <p>Unlike transient permissions, enduring permissions will be saved to storage, and exist after the session.</p>
* *
@ -134,7 +135,7 @@ public interface PermissionHolder {
Set<? extends Node> getEnduringPermissions(); Set<? extends Node> getEnduringPermissions();
/** /**
* Similar to {@link #getPermissions()}, except without enduring permissions * Similar to {@link #getPermissions()}, except without enduring permissions.
* *
* <p>Transient permissions only exist for the duration of the session.</p> * <p>Transient permissions only exist for the duration of the session.</p>
* *

View File

@ -28,7 +28,7 @@ package me.lucko.luckperms.api;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* The platforms which LuckPerms can run on * Represents a type of platform which LuckPerms can run on.
* *
* @since 2.7 * @since 2.7
*/ */
@ -44,6 +44,11 @@ public enum PlatformType {
this.friendlyName = friendlyName; this.friendlyName = friendlyName;
} }
/**
* Gets a readable name for the platform type.
*
* @return a readable name
*/
@Nonnull @Nonnull
public String getFriendlyName() { public String getFriendlyName() {
return friendlyName; return friendlyName;

View File

@ -36,14 +36,14 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* A means of loading and saving data to/from the Storage provider. * A means of loading and saving permission data to/from the backend.
* *
* <p>All methods return {@link CompletableFuture}s, which will be populated with the result once the data has been * <p>All blocking 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 * loaded/saved asynchronously. Care should be taken when using such methods to ensure that the main server thread is not
* blocked.</p> * blocked.</p>
* *
* <p>Methods such as {@link CompletableFuture#get()} and equivalent should <strong>not</strong> be called on the main * <p>Methods such as {@link CompletableFuture#get()} and equivalent should <strong>not</strong> be called on the main
* server thread. If you need to use the result of these operations on the main server thread, please register a * server thread. If you need to use the result of these operations on the main server thread, register a
* callback using {@link CompletableFuture#thenAcceptAsync(Consumer, Executor)} and {@link #getSyncExecutor()}.</p> * callback using {@link CompletableFuture#thenAcceptAsync(Consumer, Executor)} and {@link #getSyncExecutor()}.</p>
* *
* @since 2.14 * @since 2.14
@ -59,7 +59,7 @@ public interface Storage {
String getName(); 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 * @return true if logins are enabled
*/ */
@ -144,7 +144,8 @@ public interface Storage {
/** /**
* Gets a set all "unique" user UUIDs. * Gets a set all "unique" user UUIDs.
* "Unique" meaning the user isn't just a member of the "default" group. *
* <p>"Unique" meaning the user isn't just a member of the "default" group.</p>
* *
* @return a set of uuids, or null if the operation failed. * @return a set of uuids, or null if the operation failed.
*/ */

View File

@ -34,21 +34,24 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* An ordered collection of groups for easy promotions and demotions * An ordered chain of {@link Group}s.
*/ */
public interface Track { public interface Track {
/** /**
* Gets the name of this track * Gets the name of this track
*
* @return the name of this track * @return the name of this track
*/ */
@Nonnull @Nonnull
String getName(); String getName();
/** /**
* Gets an ordered list of the groups on this track * Gets a list of the groups on this track
* *
* <p>Index 0 is the first/lowest group in (or start of) the track</p> * <p>Index 0 is the first/lowest group in (or start of) the track.</p>
*
* <p>The returned collection is immutable, and cannot be modified.</p>
* *
* @return an ordered {@link List} of the groups on this track * @return an ordered {@link List} of the groups on this track
*/ */

View File

@ -28,37 +28,42 @@ package me.lucko.luckperms.api;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* Represents a permission setting. * Represents three different states of a setting.
* *
* <p>Consider a value of {@link #TRUE} to be a positive setting, {@link #FALSE} to be a "negated" setting, * <p>Possible values:</p>
* and a value of {@link #UNDEFINED} to be a non-existent value.</p> * <p></p>
* <ul>
* <li>{@link #TRUE} - a positive setting</li>
* <li>{@link #FALSE} - a negative (negated) setting</li>
* <li>{@link #UNDEFINED} - a non-existent setting</li>
* </ul>
*/ */
public enum Tristate { public enum Tristate {
/** /**
* A value indicating a holder has a permission set. * A value indicating a positive setting
*/ */
TRUE(true), TRUE(true),
/** /**
* A value indicating a holder has a negated value for a permission. * A value indicating a negative (negated) setting
*/ */
FALSE(false), 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); 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. * @return {@link #TRUE} or {@link #FALSE}, depending on the value of the boolean.
*/ */
@Nonnull @Nonnull
public static Tristate fromBoolean(boolean b) { public static Tristate fromBoolean(boolean val) {
return b ? TRUE : FALSE; return val ? TRUE : FALSE;
} }
private final boolean booleanValue; private final boolean booleanValue;

View File

@ -38,7 +38,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* A player holding permission data * A player which holds permission data.
*/ */
public interface User extends PermissionHolder { public interface User extends PermissionHolder {
@ -51,7 +51,9 @@ public interface User extends PermissionHolder {
UUID getUuid(); UUID getUuid();
/** /**
* Gets the users username, or null if no username is associated with this user * Gets the users username
*
* <p>Returns null if no username is associated with this user.</p>
* *
* @return the users username * @return the users username
*/ */
@ -96,6 +98,16 @@ public interface User extends PermissionHolder {
@Nonnull @Nonnull
UserData getCachedData(); UserData getCachedData();
/**
* Pre-calculates some values in the user's data cache.
*
* <p>Is it <b>not</b> necessary to call this method before
* using {@link #getCachedData()}.</p>
*
* @since 2.17
*/
void setupDataCache();
/** /**
* Check to see if the user is a direct member of a group * Check to see if the user is a direct member of a group
* *
@ -128,16 +140,6 @@ public interface User extends PermissionHolder {
@Nonnull @Nonnull
Optional<UserData> getUserDataCache(); Optional<UserData> getUserDataCache();
/**
* Pre-calculates some values in the user's data cache.
*
* <p>Is it <b>not</b> necessary to call this method before
* using {@link #getCachedData()}.</p>
*
* @since 2.17
*/
void setupDataCache();
/** /**
* Check to see if a user is a member of a group on a specific server * Check to see if a user is a member of a group on a specific server
* *

View File

@ -32,34 +32,34 @@ import javax.annotation.Nonnull;
/** /**
* A UUID cache for online users, between external Mojang UUIDs, and internal LuckPerms UUIDs. * A UUID cache for online users, between external Mojang UUIDs, and internal LuckPerms UUIDs.
* *
* <p>This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode * <p>A user's internal LuckPerms UUID is always the same as their Mojang one,
* servers or mixed offline mode and online mode servers. Platforms running in offline mode generate a UUID for a * unless <code>use-server-uuids</code> is disabled.</p>
* user when they first join the server, but this UUID will then not be consistent across the network. LuckPerms will
* instead check the datastore cache, to get a UUID for a user that is consistent across an entire network.</p>
* *
* <p>If you want to get a user object from the Storage using the api on a server in offline mode, you will need to use * <p>When this setting is disabled, this cache becomes active, and allows you to convert
* this cache, OR use Storage#getUUID, for users that are not online.</p> * between 'internal' and 'server provided' uuids.</p>
* *
* <p><strong>This is only effective for online players. Use {@link Storage#getUUID(String)} for offline players.</strong></p> * <p><strong>This is only effective for online players. Use {@link Storage#getUUID(String)} for offline players.</strong></p>
*/ */
public interface UuidCache { public interface UuidCache {
/** /**
* Gets a users internal "LuckPerms" UUID, from the one given by the server. * Gets a users "internal" LuckPerms UUID, from the one given by the server.
* *
* @param external the UUID assigned by the server, through Player#getUniqueId or ProxiedPlayer#getUniqueId * <p>When <code>use-server-uuids</code> is true, this returns the same UUID instance.</p>
*
* @param mojangUuid the UUID assigned by the server, through <code>Player#getUniqueId</code> or <code>ProxiedPlayer#getUniqueId</code>
* @return the corresponding internal UUID * @return the corresponding internal UUID
*/ */
@Nonnull @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 <code>User#getUuid</code>
* @return the corresponding external UUID * @return the corresponding external UUID
*/ */
@Nonnull @Nonnull
UUID getExternalUUID(@Nonnull UUID internal); UUID getExternalUUID(@Nonnull UUID internalUuid);
} }

View File

@ -33,8 +33,10 @@ import me.lucko.luckperms.api.metastacking.MetaStackDefinition;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* Represents the context for a meta lookup, consisting of a standard {@link Contexts} element, plus options to define how * Represents the context for a meta lookup.
* the meta stack should be constructed. *
* <p>Consisting of a standard {@link Contexts} element, plus options to define how
* the meta stack should be constructed.</p>
* *
* @since 3.2 * @since 3.2
*/ */

View File

@ -35,7 +35,13 @@ import java.util.Set;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
/** /**
* Holder of contexts. * A set of context pairs.
*
* <p>You can think of ContextSets as a wrapped <code>Multimap&lt;String, String&gt;</code>.
* Each key can be mapped to multiple values.</p>
*
* <p>Keys are automatically converted to lowercase when added, and are therefore
* case-insensitive. Values however are not.</p>
* *
* <p>Implementations may be either mutable or immutable.</p> * <p>Implementations may be either mutable or immutable.</p>
* *
@ -72,18 +78,6 @@ public interface ContextSet {
return ImmutableContextSet.of(key1, value1, key2, value2); 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<String, String> map) {
return ImmutableContextSet.fromMap(map);
}
/** /**
* Creates an ImmutableContextSet from an existing iterable of Map Entries * Creates an ImmutableContextSet from an existing iterable of Map Entries
* *
@ -96,6 +90,18 @@ public interface ContextSet {
return ImmutableContextSet.fromEntries(iterable); 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<String, String> map) {
return ImmutableContextSet.fromMap(map);
}
/** /**
* Creates an ImmutableContextSet from an existing multimap * 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 * @return true if the set is immutable
*/ */
@ -167,12 +173,14 @@ public interface ContextSet {
/** /**
* Converts this ContextSet to an immutable {@link Map} * Converts this ContextSet to an immutable {@link Map}
* *
* <b>NOTE: Use of this method may result in data being lost. ContextSets can contain lots of different values for * <b>IMPORTANT: Use of this method may result in data being lost. ContextSets can contain lots of different values for
* one key.</b> * one key.</b>
* *
* @return an immutable map * @return an immutable map
* @deprecated because the resultant map may not contain all data in the ContextSet
*/ */
@Nonnull @Nonnull
@Deprecated
Map<String, String> toMap(); Map<String, String> toMap();
/** /**
@ -225,6 +233,19 @@ public interface ContextSet {
*/ */
boolean has(@Nonnull String key, @Nonnull String value); boolean has(@Nonnull String key, @Nonnull String value);
/**
* Check if thr set contains a given key mapped to a given value
*
* @param entry the entry to look for
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
* @since 3.4
*/
default boolean has(@Nonnull Map.Entry<String, String> entry) {
Preconditions.checkNotNull(entry, "entry");
return has(entry.getKey(), entry.getValue());
}
/** /**
* Same as {@link #has(String, String)}, except ignores the case of the value. * 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); boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value);
/**
* Same as {@link #has(Map.Entry)}, except ignores the case of the value.
*
* @param entry the entry to look for
* @return true if the set contains the KV pair
* @throws NullPointerException if the key or value is null
* @since 3.4
*/
default boolean hasIgnoreCase(@Nonnull Map.Entry<String, String> entry) {
Preconditions.checkNotNull(entry, "entry");
return hasIgnoreCase(entry.getKey(), entry.getValue());
}
/** /**
* Checks to see if all entries in this context set are also included in another set. * 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 * @since 3.1
*/ */
default boolean isSatisfiedBy(@Nonnull ContextSet other) { 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"); Preconditions.checkNotNull(other, "other");
if (this.isEmpty()) { if (this.isEmpty()) {
// this is empty, so is therefore always satisfied. // this is empty, so is therefore always satisfied.
@ -256,9 +302,15 @@ public interface ContextSet {
} else { } else {
// neither are empty, we need to compare the individual entries // neither are empty, we need to compare the individual entries
for (Map.Entry<String, String> pair : toSet()) { for (Map.Entry<String, String> pair : toSet()) {
if (!other.has(pair.getKey(), pair.getValue())) { if (caseSensitive) {
if (!other.has(pair)) {
return false; return false;
} }
} else {
if (!other.hasIgnoreCase(pair)) {
return false;
}
}
} }
return true; return true;

View File

@ -25,12 +25,10 @@
package me.lucko.luckperms.api.context; package me.lucko.luckperms.api.context;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.SetMultimap;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@ -38,9 +36,14 @@ import java.util.Set;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* An immutable implementation of {@link ContextSet}. * An immutable implementation of {@link ContextSet}.
* *
* <p>On construction, all keys/values are {@link String#intern()}ed, in order to increase
* comparison speed.</p>
*
* @since 2.16 * @since 2.16
*/ */
public final class ImmutableContextSet implements ContextSet { public final class ImmutableContextSet implements ContextSet {
@ -56,9 +59,10 @@ public final class ImmutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) { public static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
Preconditions.checkNotNull(key, "key"); return new ImmutableContextSet(ImmutableSetMultimap.of(
Preconditions.checkNotNull(value, "value"); checkNotNull(key, "key").toLowerCase().intern(),
return new ImmutableContextSet(ImmutableSetMultimap.of(key.toLowerCase(), value)); checkNotNull(value, "value").intern()
));
} }
/** /**
@ -74,30 +78,12 @@ public final class ImmutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) { public static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) {
Preconditions.checkNotNull(key1, "key1"); return new ImmutableContextSet(ImmutableSetMultimap.of(
Preconditions.checkNotNull(value1, "value1"); checkNotNull(key1, "key1").toLowerCase().intern(),
Preconditions.checkNotNull(key2, "key2"); checkNotNull(value1, "value1").intern(),
Preconditions.checkNotNull(value2, "value2"); checkNotNull(key2, "key2").toLowerCase().intern(),
return new ImmutableContextSet(ImmutableSetMultimap.of(key1.toLowerCase(), value1, key2.toLowerCase(), value2)); checkNotNull(value2, "value2").intern()
} ));
/**
* 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<String, String> map) {
Preconditions.checkNotNull(map, "map");
ImmutableSetMultimap.Builder<String, String> b = ImmutableSetMultimap.builder();
for (Map.Entry<String, String> e : map.entrySet()) {
b.put(e.getKey().toLowerCase(), e.getValue());
}
return new ImmutableContextSet(b.build());
} }
/** /**
@ -109,8 +95,26 @@ public final class ImmutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static ImmutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) { public static ImmutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
Preconditions.checkNotNull(iterable, "iterable"); checkNotNull(iterable, "iterable");
return MutableContextSet.fromEntries(iterable).makeImmutable();
ImmutableSetMultimap.Builder<String, String> b = ImmutableSetMultimap.builder();
for (Map.Entry<String, String> e : iterable) {
b.put(checkNotNull(e.getKey()).toLowerCase().intern(), checkNotNull(e.getValue()).intern());
}
return new ImmutableContextSet(b.build());
}
/**
* Creates 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<String, String> map) {
return fromEntries(checkNotNull(map, "map").entrySet());
} }
/** /**
@ -122,8 +126,7 @@ public final class ImmutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static ImmutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) { public static ImmutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) {
Preconditions.checkNotNull(multimap, "multimap"); return fromEntries(checkNotNull(multimap, "multimap").entries());
return MutableContextSet.fromMultimap(multimap).makeImmutable();
} }
/** /**
@ -136,7 +139,7 @@ public final class ImmutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static ImmutableContextSet fromSet(@Nonnull ContextSet contextSet) { 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; return EMPTY;
} }
private final SetMultimap<String, String> map; private final ImmutableSetMultimap<String, String> map;
ImmutableContextSet(Multimap<String, String> contexts) { ImmutableContextSet(ImmutableSetMultimap<String, String> contexts) {
this.map = ImmutableSetMultimap.copyOf(contexts); this.map = contexts;
} }
@Override @Override
@ -176,7 +179,7 @@ public final class ImmutableContextSet implements ContextSet {
@Override @Override
@Nonnull @Nonnull
public Set<Map.Entry<String, String>> toSet() { public Set<Map.Entry<String, String>> toSet() {
return ImmutableSet.copyOf(map.entries()); return map.entries();
} }
@Override @Override
@ -197,36 +200,35 @@ public final class ImmutableContextSet implements ContextSet {
} }
@Override @Override
@Nonnull
public boolean containsKey(@Nonnull String key) { public boolean containsKey(@Nonnull String key) {
Preconditions.checkNotNull(key, "key"); return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
return map.containsKey(key);
} }
@Override @Override
@Nonnull
public Set<String> getValues(@Nonnull String key) { public Set<String> getValues(@Nonnull String key) {
Preconditions.checkNotNull(key, "key"); Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
Collection<String> c = map.get(key); return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of();
return c != null && !c.isEmpty() ? ImmutableSet.copyOf(c) : ImmutableSet.of();
} }
@Override @Override
@Nonnull
public boolean has(@Nonnull String key, @Nonnull String value) { public boolean has(@Nonnull String key, @Nonnull String value) {
Preconditions.checkNotNull(key, "key"); return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
Preconditions.checkNotNull(value, "value");
return map.containsEntry(key, value);
} }
@Override @Override
@Nonnull
public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) { public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
Preconditions.checkNotNull(key, "key"); value = checkNotNull(value, "value").intern();
Preconditions.checkNotNull(value, "value"); Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
Collection<String> c = map.get(key); if (values == null || values.isEmpty()) {
if (c == null || c.isEmpty()) {
return false; return false;
} }
for (String val : c) { for (String val : values) {
if (val.equalsIgnoreCase(value)) { if (val.equalsIgnoreCase(value)) {
return true; return true;
} }
@ -250,6 +252,11 @@ public final class ImmutableContextSet implements ContextSet {
if (!(o instanceof ContextSet)) return false; if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o; final ContextSet other = (ContextSet) o;
// saves on copying the multimap
if (other instanceof MutableContextSet) {
return other.equals(this);
}
final Multimap<String, String> thisContexts = this.toMultimap(); final Multimap<String, String> thisContexts = this.toMultimap();
final Multimap<String, String> otherContexts = other.toMultimap(); final Multimap<String, String> otherContexts = other.toMultimap();
return thisContexts.equals(otherContexts); return thisContexts.equals(otherContexts);
@ -257,7 +264,7 @@ public final class ImmutableContextSet implements ContextSet {
@Override @Override
public int hashCode() { public int hashCode() {
return 59 + (this.map == null ? 43 : this.map.hashCode()); return map.hashCode();
} }
@Override @Override

View File

@ -40,9 +40,14 @@ import java.util.Set;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* A mutable implementation of {@link ContextSet}. * A mutable implementation of {@link ContextSet}.
* *
* <p>On construction, all keys/values are {@link String#intern()}ed, in order to increase
* comparison speed.</p>
*
* @since 2.16 * @since 2.16
*/ */
public final class MutableContextSet implements ContextSet { public final class MutableContextSet implements ContextSet {
@ -57,9 +62,9 @@ public final class MutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static MutableContextSet singleton(@Nonnull String key, @Nonnull String value) { public static MutableContextSet singleton(@Nonnull String key, @Nonnull String value) {
Preconditions.checkNotNull(key, "key"); checkNotNull(key, "key");
Preconditions.checkNotNull(value, "value"); checkNotNull(value, "value");
MutableContextSet set = new MutableContextSet(); MutableContextSet set = MutableContextSet.create();
set.add(key, value); set.add(key, value);
return set; return set;
} }
@ -77,27 +82,13 @@ public final class MutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static MutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) { public static MutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) {
Preconditions.checkNotNull(key1, "key1"); checkNotNull(key1, "key1");
Preconditions.checkNotNull(value1, "value1"); checkNotNull(value1, "value1");
Preconditions.checkNotNull(key2, "key2"); checkNotNull(key2, "key2");
Preconditions.checkNotNull(value2, "value2"); checkNotNull(value2, "value2");
MutableContextSet ret = singleton(key1, value1); MutableContextSet set = MutableContextSet.create();
ret.add(key2, value2); set.add(key1, value1);
return ret; set.add(key2, value2);
}
/**
* Creates a MutableContextSet from an existing map
*
* @param map the map to copy from
* @return a new MutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
@Nonnull
public static MutableContextSet fromMap(@Nonnull Map<String, String> map) {
Preconditions.checkNotNull(map, "map");
MutableContextSet set = new MutableContextSet();
set.addAll(map);
return set; return set;
} }
@ -110,12 +101,27 @@ public final class MutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static MutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) { public static MutableContextSet fromEntries(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
Preconditions.checkNotNull(iterable, "iterable"); checkNotNull(iterable, "iterable");
MutableContextSet set = new MutableContextSet(); MutableContextSet set = MutableContextSet.create();
set.addAll(iterable); set.addAll(iterable);
return set; return set;
} }
/**
* Creates a MutableContextSet from an existing map
*
* @param map the map to copy from
* @return a new MutableContextSet representing the pairs from the map
* @throws NullPointerException if the map is null
*/
@Nonnull
public static MutableContextSet fromMap(@Nonnull Map<String, String> map) {
checkNotNull(map, "map");
MutableContextSet set = MutableContextSet.create();
set.addAll(map);
return set;
}
/** /**
* Creates a MutableContextSet from an existing multimap * Creates a MutableContextSet from an existing multimap
* *
@ -125,8 +131,10 @@ public final class MutableContextSet implements ContextSet {
*/ */
@Nonnull @Nonnull
public static MutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) { public static MutableContextSet fromMultimap(@Nonnull Multimap<String, String> multimap) {
Preconditions.checkNotNull(multimap, "multimap"); checkNotNull(multimap, "multimap");
return fromEntries(multimap.entries()); 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()); this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create());
} }
private MutableContextSet(Multimap<String, String> contexts) { private MutableContextSet(MutableContextSet other) {
this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(contexts)); this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other.map));
} }
@Override @Override
@ -170,21 +178,25 @@ public final class MutableContextSet implements ContextSet {
return false; return false;
} }
@Nonnull
@Override @Override
public ImmutableContextSet makeImmutable() { public ImmutableContextSet makeImmutable() {
return new ImmutableContextSet(map); return new ImmutableContextSet(ImmutableSetMultimap.copyOf(map));
} }
@Nonnull
@Override @Override
public MutableContextSet mutableCopy() { public MutableContextSet mutableCopy() {
return new MutableContextSet(map); return new MutableContextSet(this);
} }
@Nonnull
@Override @Override
public Set<Map.Entry<String, String>> toSet() { public Set<Map.Entry<String, String>> toSet() {
return ImmutableSet.copyOf(map.entries()); return ImmutableSet.copyOf(map.entries());
} }
@Nonnull
@Override @Override
public Map<String, String> toMap() { public Map<String, String> toMap() {
ImmutableMap.Builder<String, String> m = ImmutableMap.builder(); ImmutableMap.Builder<String, String> m = ImmutableMap.builder();
@ -195,57 +207,42 @@ public final class MutableContextSet implements ContextSet {
return m.build(); return m.build();
} }
@Nonnull
@Override @Override
public Multimap<String, String> toMultimap() { public Multimap<String, String> toMultimap() {
return ImmutableSetMultimap.copyOf(map); return ImmutableSetMultimap.copyOf(map);
} }
@Nonnull
@Override @Override
public boolean containsKey(String key) { public boolean containsKey(@Nonnull String key) {
if (key == null) { return map.containsKey(checkNotNull(key, "key").toLowerCase().intern());
throw new NullPointerException("key");
}
return map.containsKey(key);
} }
@Nonnull
@Override @Override
public Set<String> getValues(String key) { public Set<String> getValues(@Nonnull String key) {
if (key == null) { Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
throw new NullPointerException("key"); return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of();
}
Collection<String> c = map.get(key);
return c != null && !c.isEmpty() ? ImmutableSet.copyOf(c) : ImmutableSet.of();
} }
@Nonnull
@Override @Override
public boolean has(String key, String value) { public boolean has(@Nonnull String key, @Nonnull String value) {
if (key == null) { return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
return map.containsEntry(key, value);
} }
@Nonnull
@Override @Override
public boolean hasIgnoreCase(String key, String value) { public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) {
if (key == null) { value = checkNotNull(value, "value").intern();
throw new NullPointerException("key"); Collection<String> values = map.get(checkNotNull(key, "key").toLowerCase().intern());
}
if (value == null) {
throw new NullPointerException("value");
}
Collection<String> c = map.get(key); if (values == null || values.isEmpty()) {
if (c == null || c.isEmpty()) {
return false; return false;
} }
for (String val : c) { for (String val : values) {
if (val.equalsIgnoreCase(value)) { if (val.equalsIgnoreCase(value)) {
return true; return true;
} }
@ -270,15 +267,8 @@ public final class MutableContextSet implements ContextSet {
* @param value the value to add * @param value the value to add
* @throws NullPointerException if the key or value is null * @throws NullPointerException if the key or value is null
*/ */
public void add(String key, String value) { public void add(@Nonnull String key, @Nonnull String value) {
if (key == null) { map.put(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern());
throw new NullPointerException("key");
}
if (value == null) {
throw new NullPointerException("value");
}
map.put(key.toLowerCase(), value);
} }
/** /**
@ -287,12 +277,9 @@ public final class MutableContextSet implements ContextSet {
* @param entry the entry to add * @param entry the entry to add
* @throws NullPointerException if the entry is null * @throws NullPointerException if the entry is null
*/ */
public void add(Map.Entry<String, String> entry) { public void add(@Nonnull Map.Entry<String, String> entry) {
if (entry == null) { checkNotNull(entry, "entry");
throw new NullPointerException("context"); add(entry.getKey(), entry.getValue());
}
map.put(entry.getKey().toLowerCase(), entry.getValue());
} }
/** /**
@ -301,13 +288,9 @@ public final class MutableContextSet implements ContextSet {
* @param iterable an iterable of key value context pairs * @param iterable an iterable of key value context pairs
* @throws NullPointerException if iterable is null * @throws NullPointerException if iterable is null
*/ */
public void addAll(Iterable<? extends Map.Entry<String, String>> iterable) { public void addAll(@Nonnull Iterable<? extends Map.Entry<String, String>> iterable) {
if (iterable == null) { for (Map.Entry<String, String> e : checkNotNull(iterable, "iterable")) {
throw new NullPointerException("iterable"); add(e);
}
for (Map.Entry<String, String> e : iterable) {
this.map.put(e.getKey().toLowerCase(), e.getValue());
} }
} }
@ -317,11 +300,19 @@ public final class MutableContextSet implements ContextSet {
* @param map the map to add from * @param map the map to add from
* @throws NullPointerException if the map is null * @throws NullPointerException if the map is null
*/ */
public void addAll(Map<String, String> map) { public void addAll(@Nonnull Map<String, String> map) {
if (map == null) { addAll(checkNotNull(map, "map").entrySet());
throw new NullPointerException("map");
} }
addAll(map.entrySet());
/**
* Adds the entries of a multimap to the set
*
* @param multimap the multimap to add from
* @throws NullPointerException if the map is null
* @since 3.4
*/
public void addAll(@Nonnull Multimap<String, String> multimap) {
addAll(checkNotNull(multimap, "multimap").entries());
} }
/** /**
@ -330,12 +321,14 @@ public final class MutableContextSet implements ContextSet {
* @param contextSet the set to add from * @param contextSet the set to add from
* @throws NullPointerException if the contextSet is null * @throws NullPointerException if the contextSet is null
*/ */
public void addAll(ContextSet contextSet) { public void addAll(@Nonnull ContextSet contextSet) {
if (contextSet == null) { checkNotNull(contextSet, "contextSet");
throw new NullPointerException("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) * @param value the value to remove (case sensitive)
* @throws NullPointerException if the key or value is null * @throws NullPointerException if the key or value is null
*/ */
public void remove(String key, String value) { public void remove(@Nonnull String key, @Nonnull String value) {
if (key == null) { String k = checkNotNull(key, "key").toLowerCase().intern();
throw new NullPointerException("key"); String v = checkNotNull(value, "value").intern();
}
if (value == null) {
throw new NullPointerException("value");
}
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 * @param value the value to remove
* @throws NullPointerException if the key or value is null * @throws NullPointerException if the key or value is null
*/ */
public void removeIgnoreCase(String key, String value) { public void removeIgnoreCase(@Nonnull String key, @Nonnull String value) {
if (key == null) { String k = checkNotNull(key, "key").toLowerCase().intern();
throw new NullPointerException("key"); String v = checkNotNull(value, "value").intern();
}
if (value == null) {
throw new NullPointerException("value");
}
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 * @param key the key to remove
* @throws NullPointerException if the key is null * @throws NullPointerException if the key is null
*/ */
public void removeAll(String key) { public void removeAll(@Nonnull String key) {
if (key == null) { map.removeAll(checkNotNull(key, "key").toLowerCase());
throw new NullPointerException("key");
}
map.removeAll(key.toLowerCase());
} }
/** /**
@ -401,14 +384,20 @@ public final class MutableContextSet implements ContextSet {
if (!(o instanceof ContextSet)) return false; if (!(o instanceof ContextSet)) return false;
final ContextSet other = (ContextSet) o; final ContextSet other = (ContextSet) o;
final Multimap<String, String> thisContexts = this.toMultimap(); final Multimap<String, String> otherContexts;
final Multimap<String, String> otherContexts = other.toMultimap();
return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts); if (other instanceof MutableContextSet) {
otherContexts = ((MutableContextSet) other).map;
} else {
otherContexts = other.toMultimap();
}
return this.map.equals(otherContexts);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return 59 + (this.map == null ? 43 : this.map.hashCode()); return map.hashCode();
} }
@Override @Override

View File

@ -0,0 +1,72 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.event.user;
import me.lucko.luckperms.api.User;
import me.lucko.luckperms.api.event.LuckPermsEvent;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Called when LuckPerms has finished processing a certain Player's connection.
*
* <p>This event will always execute during the platforms async login/auth event.
* All handlers will be called instantly.</p>
*
* <p>This, among other things, allows you to wait until permission data is loaded
* for a User during the BungeeCord 'LoginEvent', as event priorities are ignored
* by the current implementation.</p>
*
* @since 3.4
*/
public interface UserLoginProcessEvent extends LuckPermsEvent {
/**
* Gets the UUID of the connection which was processed
*
* @return the uuid of the connection which was processed
*/
@Nonnull
UUID getUuid();
/**
* Gets the username of the connection which was processed
*
* @return the username of the connection which was processed
*/
@Nonnull
String getUsername();
/**
* Gets the resultant User instance which was loaded.
*
* @return the user instance
*/
User getUser();
}

View File

@ -26,8 +26,16 @@
package me.lucko.luckperms.exceptions; 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 * Thrown when a certain membership state is / isn't met.
* group *
* For example, when:
* <p></p>
* <ul>
* <li>a permission holding object doesn't have a permission</li>
* <li>a permission holding object already has a permission</li>
* <li>a permission holding object is already a member of a group</li>
* <li>a permission holding object isn't already a member of a group</li>
* </ul>
* *
* @since 2.7 * @since 2.7
*/ */

View File

@ -26,8 +26,15 @@
package me.lucko.luckperms.exceptions; 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 * Thrown when an object already has something.
* already contains a group. *
* <p>For example, when:</p>
* <p></p>
* <ul>
* <li>a permission holding object already has a permission</li>
* <li>a permission holding object is already a member of a group</li>
* <li>a track already contains a group</li>
* </ul>
*/ */
public class ObjectAlreadyHasException extends MembershipException { public class ObjectAlreadyHasException extends MembershipException {
} }

View File

@ -26,8 +26,15 @@
package me.lucko.luckperms.exceptions; 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, * Thrown when an object lacks something.
* or when a track doesn't contain a group. *
* <p>For example, when:</p>
* <p></p>
* <ul>
* <li>a permission holding object doesn't have a permission</li>
* <li>a permission holding object isn't already a member of a group</li>
* <li>a track doesn't contain a group</li>
* </ul>
*/ */
public class ObjectLacksException extends MembershipException { public class ObjectLacksException extends MembershipException {
} }

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -102,7 +102,8 @@ public class BukkitListener implements Listener {
- creating a user instance in the UserManager for this connection. - creating a user instance in the UserManager for this connection.
- setting up cached data. */ - setting up cached data. */
try { 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) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();

View File

@ -38,6 +38,7 @@ import me.lucko.luckperms.bukkit.processors.ChildProcessor;
import me.lucko.luckperms.bukkit.processors.DefaultsProcessor; import me.lucko.luckperms.bukkit.processors.DefaultsProcessor;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator; 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.config.ConfigKeys;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.processors.MapProcessor; import me.lucko.luckperms.common.processors.MapProcessor;
@ -82,7 +83,11 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new DefaultsProcessor(contexts.isOp(), plugin.getDefaultsProvider())); 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 @Override

View File

@ -35,6 +35,7 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.caching.UserCache; import me.lucko.luckperms.common.caching.UserCache;
import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -110,13 +111,13 @@ public class LPPermissible extends PermissibleBase {
@Override @Override
public boolean isPermissionSet(@NonNull String permission) { 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()); return ts != Tristate.UNDEFINED || Permission.DEFAULT_PERMISSION.getValue(isOp());
} }
@Override @Override
public boolean isPermissionSet(@NonNull Permission permission) { 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) { if (ts != Tristate.UNDEFINED) {
return true; return true;
} }
@ -130,13 +131,13 @@ public class LPPermissible extends PermissibleBase {
@Override @Override
public boolean hasPermission(@NonNull String permission) { 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()); return ts != Tristate.UNDEFINED ? ts.asBoolean() : Permission.DEFAULT_PERMISSION.getValue(isOp());
} }
@Override @Override
public boolean hasPermission(@NonNull Permission permission) { 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) { if (ts != Tristate.UNDEFINED) {
return ts.asBoolean(); return ts.asBoolean();
} }

View File

@ -331,7 +331,7 @@ public class VaultChatHook extends Chat {
perms.log("Getting meta: '" + node + "' for group " + group.getName() + " on world " + world + ", server " + perms.getServer()); perms.log("Getting meta: '" + node + "' for group " + group.getName() + " on world " + world + ", server " + perms.getServer());
for (Node n : group.getOwnNodes()) { for (Node n : group.getOwnNodes()) {
if (!n.getValue()) continue; if (!n.getValuePrimitive()) continue;
if (!n.isMeta()) continue; if (!n.isMeta()) continue;
if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) 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)); ExtractedContexts ec = ExtractedContexts.generate(Contexts.of(perms.createContextForWorldLookup(world).getContexts(), perms.isIncludeGlobal(), true, true, true, true, false));
for (Node n : group.getAllNodes(ec)) { for (Node n : group.getAllNodes(ec)) {
if (!n.getValue()) continue; if (!n.getValuePrimitive()) continue;
if (type.shouldIgnore(n)) continue; if (type.shouldIgnore(n)) continue;
if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) continue; if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) continue;

View File

@ -32,16 +32,17 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.DataMutateResult; import me.lucko.luckperms.api.DataMutateResult;
import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.Tristate; 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.ImmutableContextSet;
import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.api.context.MutableContextSet;
import me.lucko.luckperms.bukkit.LPBukkitPlugin; 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.config.ConfigKeys;
import me.lucko.luckperms.common.contexts.ExtractedContexts; import me.lucko.luckperms.common.contexts.ExtractedContexts;
import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.PermissionHolder;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import net.milkbowl.vault.permission.Permission; import net.milkbowl.vault.permission.Permission;
@ -147,7 +148,7 @@ public class VaultPermissionHook extends Permission {
} }
// Effectively fallback to the standard Bukkit #hasPermission check. // 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 @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.) // we need to do the complex PGO checking. (it's been enabled in the config.)
if (isPgoCheckInherited()) { if (isPgoCheckInherited()) {
// we can just check the cached data // 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<String, Boolean> e : data.getImmutableBacking().entrySet()) { for (Map.Entry<String, Boolean> e : data.getImmutableBacking().entrySet()) {
if (!e.getValue()) continue; if (!e.getValue()) continue;
if (!e.getKey().toLowerCase().startsWith("vault.primarygroup.")) continue; if (!e.getKey().toLowerCase().startsWith("vault.primarygroup.")) continue;
@ -409,7 +410,7 @@ public class VaultPermissionHook extends Permission {
} }
if (isPgoCheckMemberOf()) { if (isPgoCheckMemberOf()) {
if (data.getPermissionValue("group." + group) != Tristate.TRUE) { if (data.getPermissionValue("group." + group, CheckOrigin.INTERNAL) != Tristate.TRUE) {
continue; continue;
} }
} }
@ -419,7 +420,7 @@ public class VaultPermissionHook extends Permission {
} else { } else {
// we need to check the users permissions only // we need to check the users permissions only
for (Node node : user.getOwnNodes()) { for (Node node : user.getOwnNodes()) {
if (!node.getValue()) continue; if (!node.getValuePrimitive()) continue;
if (!node.getPermission().toLowerCase().startsWith("vault.primarygroup.")) continue; if (!node.getPermission().toLowerCase().startsWith("vault.primarygroup.")) continue;
if (!node.shouldApplyOnServer(getServer(), isIncludeGlobal(), false)) continue; if (!node.shouldApplyOnServer(getServer(), isIncludeGlobal(), false)) continue;
if (!node.shouldApplyOnWorld(world, true, false)) continue; if (!node.shouldApplyOnWorld(world, true, false)) continue;

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -35,6 +35,7 @@ import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.LoginHelper; 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.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection; 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. - creating a user instance in the UserManager for this connection.
- setting up cached data. */ - setting up cached data. */
try { 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) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
@ -159,7 +161,7 @@ public class BungeeListener implements Listener {
} }
Contexts contexts = plugin.getContextManager().getApplicableContexts(player); 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)) { 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 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); 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)) { 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 return; // just use the result provided by the proxy when the event was created
} }

View File

@ -33,6 +33,7 @@ import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.bungee.LPBungeePlugin; import me.lucko.luckperms.bungee.LPBungeePlugin;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator; 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.config.ConfigKeys;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.processors.MapProcessor; import me.lucko.luckperms.common.processors.MapProcessor;
@ -60,7 +61,11 @@ public class BungeeCalculatorFactory extends AbstractCalculatorFactory {
processors.add(new WildcardProcessor()); 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 @Override

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -31,18 +31,23 @@ import me.lucko.luckperms.api.LuckPermsApi;
import java.lang.reflect.Method; import java.lang.reflect.Method;
public class ApiHandler { public class ApiHandler {
private static Method REGISTER; private static final Method REGISTER;
private static Method UNREGISTER; private static final Method UNREGISTER;
static { static {
Method register = null;
Method unregister = null;
try { try {
REGISTER = LuckPerms.class.getDeclaredMethod("registerProvider", LuckPermsApi.class); register = LuckPerms.class.getDeclaredMethod("registerProvider", LuckPermsApi.class);
REGISTER.setAccessible(true); register.setAccessible(true);
UNREGISTER = LuckPerms.class.getDeclaredMethod("unregisterProvider"); unregister = LuckPerms.class.getDeclaredMethod("unregisterProvider");
UNREGISTER.setAccessible(true); unregister.setAccessible(true);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
REGISTER = register;
UNREGISTER = unregister;
} }
public static void registerProvider(LuckPermsApi luckPermsApi) { public static void registerProvider(LuckPermsApi luckPermsApi) {

View File

@ -88,7 +88,7 @@ public class ApiProvider implements LuckPermsApi {
@Override @Override
public double getApiVersion() { public double getApiVersion() {
return 3.3; return 3.4;
} }
@Override @Override

View File

@ -33,6 +33,7 @@ import me.lucko.luckperms.api.caching.PermissionData;
import me.lucko.luckperms.common.calculators.CalculatorFactory; import me.lucko.luckperms.common.calculators.CalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator; import me.lucko.luckperms.common.calculators.PermissionCalculator;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
@ -93,6 +94,10 @@ public class PermissionCache implements PermissionData {
@Override @Override
public Tristate getPermissionValue(@NonNull String permission) { 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);
} }
} }

View File

@ -36,7 +36,6 @@ import com.google.common.collect.ImmutableSet;
import me.lucko.luckperms.api.ChatMetaType; import me.lucko.luckperms.api.ChatMetaType;
import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.api.caching.MetaContexts; 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.PermissionData;
import me.lucko.luckperms.api.caching.UserData; import me.lucko.luckperms.api.caching.UserData;
import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.ConfigKeys;
@ -68,17 +67,17 @@ public class UserCache implements UserData {
.build(new MetaCacheLoader()); .build(new MetaCacheLoader());
@Override @Override
public PermissionData getPermissionData(@NonNull Contexts contexts) { public PermissionCache getPermissionData(@NonNull Contexts contexts) {
return permission.get(contexts); return permission.get(contexts);
} }
@Override @Override
public MetaData getMetaData(@NonNull MetaContexts contexts) { public MetaCache getMetaData(@NonNull MetaContexts contexts) {
return meta.get(contexts); return meta.get(contexts);
} }
@Override @Override
public MetaData getMetaData(@NonNull Contexts contexts) { public MetaCache getMetaData(@NonNull Contexts contexts) {
// just create a MetaContexts instance using the values in the config // just create a MetaContexts instance using the values in the config
return getMetaData(makeFromMetaContextsConfig(contexts, user.getPlugin())); return getMetaData(makeFromMetaContextsConfig(contexts, user.getPlugin()));
} }

View File

@ -33,6 +33,7 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.processors.PermissionProcessor; import me.lucko.luckperms.common.processors.PermissionProcessor;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -43,7 +44,7 @@ import java.util.Map;
@RequiredArgsConstructor @RequiredArgsConstructor
public class PermissionCalculator { public class PermissionCalculator {
private final LuckPermsPlugin plugin; private final LuckPermsPlugin plugin;
private final String objectName; private final PermissionCalculatorMetadata metadata;
private final List<PermissionProcessor> processors; private final List<PermissionProcessor> processors;
// caches lookup calls. // caches lookup calls.
@ -54,7 +55,7 @@ public class PermissionCalculator {
lookupCache.invalidateAll(); 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. // convert the permission to lowercase, as all values in the backing map are also lowercase.
// this allows fast case insensitive lookups // this allows fast case insensitive lookups
@ -64,7 +65,7 @@ public class PermissionCalculator {
Tristate result = lookupCache.get(permission); Tristate result = lookupCache.get(permission);
// log this permission lookup to the verbose handler // 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 the result
return result; return result;

View File

@ -0,0 +1,47 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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;
}

View File

@ -173,7 +173,7 @@ public class PermissionInfo extends SharedSubCommand {
} }
for (Node node : page) { 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) { if (temp) {
s += "&2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "\n"; s += "&2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "\n";
} }
@ -186,7 +186,7 @@ public class PermissionInfo extends SharedSubCommand {
private static Consumer<BuildableComponent.Builder<?, ?>> makeFancy(PermissionHolder holder, String label, Node node) { private static Consumer<BuildableComponent.Builder<?, ?>> makeFancy(PermissionHolder holder, String label, Node node) {
HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( 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() "¥7Click to remove this node from " + holder.getFriendlyName()
), '¥')); ), '¥'));

View File

@ -192,7 +192,7 @@ public class GroupListMembers extends SubCommand<Group> {
private static Consumer<BuildableComponent.Builder<? ,?>> makeFancy(String holderName, boolean group, String label, HeldPermission<?> perm) { private static Consumer<BuildableComponent.Builder<? ,?>> makeFancy(String holderName, boolean group, String label, HeldPermission<?> perm) {
HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( 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 "&7Click to remove this parent from " + holderName
), Constants.FORMAT_CHAR)); ), Constants.FORMAT_CHAR));

View File

@ -61,7 +61,7 @@ public class LogNotify extends SubCommand<Log> {
// if they don't have the perm, they're not ignoring // if they don't have the perm, they're not ignoring
// if set to false, ignore it, return false // 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) { private static void setIgnoring(LuckPermsPlugin plugin, UUID uuid, boolean state) {

View File

@ -39,6 +39,7 @@ import me.lucko.luckperms.common.locale.Message;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.common.utils.Predicates;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -67,7 +68,7 @@ public class CheckCommand extends SingleCommand {
return CommandResult.STATE_ERROR; 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)); Message.CHECK_RESULT.send(sender, user.getFriendlyName(), permission, Util.formatTristate(tristate));
return CommandResult.SUCCESS; return CommandResult.SUCCESS;
} }

View File

@ -191,7 +191,7 @@ public class SearchCommand extends SingleCommand {
private static Consumer<BuildableComponent.Builder<?, ?>> makeFancy(String holderName, boolean group, String label, HeldPermission<?> perm) { private static Consumer<BuildableComponent.Builder<?, ?>> makeFancy(String holderName, boolean group, String label, HeldPermission<?> perm) {
HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( 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 "&7Click to remove this node from " + holderName
), Constants.FORMAT_CHAR)); ), Constants.FORMAT_CHAR));

View File

@ -25,7 +25,7 @@
package me.lucko.luckperms.common.commands.impl.misc; 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.CommandException;
import me.lucko.luckperms.common.commands.CommandResult; import me.lucko.luckperms.common.commands.CommandResult;
import me.lucko.luckperms.common.commands.abstraction.SingleCommand; import me.lucko.luckperms.common.commands.abstraction.SingleCommand;
@ -86,7 +86,7 @@ public class TreeCommand extends SingleCommand {
return CommandResult.STATE_ERROR; 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()); TreeView view = TreeViewBuilder.newBuilder().rootPosition(selection).maxLevels(maxLevel).build(plugin.getPermissionVault());
if (!view.hasData()) { if (!view.hasData()) {

View File

@ -63,6 +63,7 @@ public class VerboseCommand extends SingleCommand {
return CommandResult.INVALID_ARGS; return CommandResult.INVALID_ARGS;
} }
boolean noTraces = args.remove("--notrace") || args.remove("--notraces") || args.remove("--slim") || args.remove("-s");
String mode = args.get(0).toLowerCase(); String mode = args.get(0).toLowerCase();
if (mode.equals("on") || mode.equals("true") || mode.equals("record")) { if (mode.equals("on") || mode.equals("true") || mode.equals("record")) {
@ -108,7 +109,7 @@ public class VerboseCommand extends SingleCommand {
} else { } else {
Message.VERBOSE_RECORDING_UPLOAD_START.send(sender); Message.VERBOSE_RECORDING_UPLOAD_START.send(sender);
String url = listener.uploadPasteData(); String url = listener.uploadPasteData(!noTraces);
if (url == null) { if (url == null) {
url = "null"; url = "null";
} }

View File

@ -99,7 +99,7 @@ public class UserDemote extends SubCommand<User> {
// Load applicable groups // Load applicable groups
Set<Node> nodes = user.getEnduringNodes().values().stream() Set<Node> nodes = user.getEnduringNodes().values().stream()
.filter(Node::isGroupNode) .filter(Node::isGroupNode)
.filter(Node::getValue) .filter(Node::getValuePrimitive)
.filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable())) .filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable()))
.collect(Collectors.toSet()); .collect(Collectors.toSet());

View File

@ -97,7 +97,7 @@ public class UserPromote extends SubCommand<User> {
// Load applicable groups // Load applicable groups
Set<Node> nodes = user.getEnduringNodes().values().stream() Set<Node> nodes = user.getEnduringNodes().values().stream()
.filter(Node::isGroupNode) .filter(Node::isGroupNode)
.filter(Node::getValue) .filter(Node::getValuePrimitive)
.filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable())) .filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable()))
.collect(Collectors.toSet()); .collect(Collectors.toSet());

View File

@ -27,20 +27,13 @@ package me.lucko.luckperms.common.contexts;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeSet;
public class ContextSetComparator implements Comparator<ImmutableContextSet> { public class ContextSetComparator implements Comparator<ImmutableContextSet> {
private static final Comparator<Map.Entry<String, String>> 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(); private static final ContextSetComparator INSTANCE = new ContextSetComparator();
public static Comparator<ImmutableContextSet> get() { public static Comparator<ImmutableContextSet> get() {
@ -75,16 +68,15 @@ public class ContextSetComparator implements Comparator<ImmutableContextSet> {
return o1Size > o2Size ? 1 : -1; return o1Size > o2Size ? 1 : -1;
} }
// we *have* to maintain transitivity in this comparator. this may be expensive, but it's necessary, as these // we *have* to maintain transitivity in this comparator. this may be expensive, but it's necessary, as this
// values are stored in a treemap. // 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 // 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. // elements and then comparing which set is greater.
TreeSet<Map.Entry<String, String>> o1Map = new TreeSet<>(STRING_ENTRY_COMPARATOR); List<Map.Entry<String, String>> o1Map = new ArrayList<>(o1.toSet());
TreeSet<Map.Entry<String, String>> o2Map = new TreeSet<>(STRING_ENTRY_COMPARATOR); List<Map.Entry<String, String>> o2Map = new ArrayList<>(o2.toSet());
o1Map.sort(STRING_ENTRY_COMPARATOR);
o1Map.addAll(o1.toMultimap().entries()); o2Map.sort(STRING_ENTRY_COMPARATOR);
o2Map.addAll(o2.toMultimap().entries());
int o1MapSize = o1Map.size(); int o1MapSize = o1Map.size();
int o2MapSize = o2Map.size(); int o2MapSize = o2Map.size();
@ -101,18 +93,33 @@ public class ContextSetComparator implements Comparator<ImmutableContextSet> {
Map.Entry<String, String> ent2 = it2.next(); Map.Entry<String, String> ent2 = it2.next();
// compare these values. // 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 // identical entries. just move on
continue; 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 // 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); return STRING_ENTRY_COMPARATOR.compare(ent1, ent2);
} }
// shouldn't ever reach this point. ever. // shouldn't ever reach this point.
return 0; return 0;
} }
private static final Comparator<String> FAST_STRING_COMPARATOR = (o1, o2) -> {
//noinspection StringEquality
return o1 == o2 ? 0 : o1.compareTo(o2);
};
private static final Comparator<Map.Entry<String, String>> 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());
};
} }

View File

@ -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.EventUserDemote;
import me.lucko.luckperms.common.event.impl.EventUserFirstLogin; import me.lucko.luckperms.common.event.impl.EventUserFirstLogin;
import me.lucko.luckperms.common.event.impl.EventUserLoad; 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.event.impl.EventUserPromote;
import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.Group;
import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.PermissionHolder;
@ -79,158 +80,167 @@ import java.util.concurrent.atomic.AtomicBoolean;
public final class EventFactory { public final class EventFactory {
private final LuckPermsEventBus eventBus; private final LuckPermsEventBus eventBus;
private void fireEvent(LuckPermsEvent event) { private void fireEventAsync(LuckPermsEvent event) {
eventBus.fireEventAsync(event); eventBus.fireEventAsync(event);
} }
private void fireEvent(LuckPermsEvent event) {
eventBus.fireEvent(event);
}
public void handleGroupCreate(Group group, CreationCause cause) { public void handleGroupCreate(Group group, CreationCause cause) {
EventGroupCreate event = new EventGroupCreate(group.getDelegate(), cause); EventGroupCreate event = new EventGroupCreate(group.getDelegate(), cause);
fireEvent(event); fireEventAsync(event);
} }
public void handleGroupDelete(Group group, DeletionCause cause) { public void handleGroupDelete(Group group, DeletionCause cause) {
EventGroupDelete event = new EventGroupDelete(group.getName(), ImmutableSet.copyOf(group.getEnduringNodes().values()), cause); EventGroupDelete event = new EventGroupDelete(group.getName(), ImmutableSet.copyOf(group.getEnduringNodes().values()), cause);
fireEvent(event); fireEventAsync(event);
} }
public void handleGroupLoadAll() { public void handleGroupLoadAll() {
EventGroupLoadAll event = new EventGroupLoadAll(); EventGroupLoadAll event = new EventGroupLoadAll();
fireEvent(event); fireEventAsync(event);
} }
public void handleGroupLoad(Group group) { public void handleGroupLoad(Group group) {
EventGroupLoad event = new EventGroupLoad(group.getDelegate()); EventGroupLoad event = new EventGroupLoad(group.getDelegate());
fireEvent(event); fireEventAsync(event);
} }
public boolean handleLogBroadcast(boolean initialState, LogEntry entry, LogBroadcastEvent.Origin origin) { public boolean handleLogBroadcast(boolean initialState, LogEntry entry, LogBroadcastEvent.Origin origin) {
AtomicBoolean cancel = new AtomicBoolean(initialState); AtomicBoolean cancel = new AtomicBoolean(initialState);
EventLogBroadcast event = new EventLogBroadcast(cancel, entry, origin); EventLogBroadcast event = new EventLogBroadcast(cancel, entry, origin);
eventBus.fireEvent(event); fireEvent(event);
return cancel.get(); return cancel.get();
} }
public boolean handleLogPublish(boolean initialState, LogEntry entry) { public boolean handleLogPublish(boolean initialState, LogEntry entry) {
AtomicBoolean cancel = new AtomicBoolean(initialState); AtomicBoolean cancel = new AtomicBoolean(initialState);
EventLogPublish event = new EventLogPublish(cancel, entry); EventLogPublish event = new EventLogPublish(cancel, entry);
eventBus.fireEvent(event); fireEvent(event);
return cancel.get(); return cancel.get();
} }
public boolean handleLogNetworkPublish(boolean initialState, UUID id, LogEntry entry) { public boolean handleLogNetworkPublish(boolean initialState, UUID id, LogEntry entry) {
AtomicBoolean cancel = new AtomicBoolean(initialState); AtomicBoolean cancel = new AtomicBoolean(initialState);
EventLogNetworkPublish event = new EventLogNetworkPublish(cancel, id, entry); EventLogNetworkPublish event = new EventLogNetworkPublish(cancel, id, entry);
eventBus.fireEvent(event); fireEvent(event);
return cancel.get(); return cancel.get();
} }
public void handleLogReceive(UUID id, LogEntry entry) { public void handleLogReceive(UUID id, LogEntry entry) {
EventLogReceive event = new EventLogReceive(id, entry); EventLogReceive event = new EventLogReceive(id, entry);
fireEvent(event); fireEventAsync(event);
} }
public void handleNodeAdd(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) { public void handleNodeAdd(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) {
EventNodeAdd event = new EventNodeAdd(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); EventNodeAdd event = new EventNodeAdd(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEvent(event); fireEventAsync(event);
} }
public void handleNodeClear(PermissionHolder target, Collection<Node> before, Collection<Node> after) { public void handleNodeClear(PermissionHolder target, Collection<Node> before, Collection<Node> after) {
EventNodeClear event = new EventNodeClear(target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(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<Node> before, Collection<Node> after) { public void handleNodeRemove(Node node, PermissionHolder target, Collection<Node> before, Collection<Node> after) {
EventNodeRemove event = new EventNodeRemove(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); EventNodeRemove event = new EventNodeRemove(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after));
fireEvent(event); fireEventAsync(event);
} }
public void handleConfigReload() { public void handleConfigReload() {
EventConfigReload event = new EventConfigReload(); EventConfigReload event = new EventConfigReload();
fireEvent(event); fireEventAsync(event);
} }
public void handlePostSync() { public void handlePostSync() {
EventPostSync event = new EventPostSync(); EventPostSync event = new EventPostSync();
fireEvent(event); fireEventAsync(event);
} }
public boolean handleNetworkPreSync(boolean initialState, UUID id) { public boolean handleNetworkPreSync(boolean initialState, UUID id) {
AtomicBoolean cancel = new AtomicBoolean(initialState); AtomicBoolean cancel = new AtomicBoolean(initialState);
EventPreNetworkSync event = new EventPreNetworkSync(cancel, id); EventPreNetworkSync event = new EventPreNetworkSync(cancel, id);
eventBus.fireEvent(event); fireEvent(event);
return cancel.get(); return cancel.get();
} }
public boolean handlePreSync(boolean initialState) { public boolean handlePreSync(boolean initialState) {
AtomicBoolean cancel = new AtomicBoolean(initialState); AtomicBoolean cancel = new AtomicBoolean(initialState);
EventPreSync event = new EventPreSync(cancel); EventPreSync event = new EventPreSync(cancel);
eventBus.fireEvent(event); fireEvent(event);
return cancel.get(); return cancel.get();
} }
public void handleTrackCreate(Track track, CreationCause cause) { public void handleTrackCreate(Track track, CreationCause cause) {
EventTrackCreate event = new EventTrackCreate(track.getDelegate(), cause); EventTrackCreate event = new EventTrackCreate(track.getDelegate(), cause);
fireEvent(event); fireEventAsync(event);
} }
public void handleTrackDelete(Track track, DeletionCause cause) { public void handleTrackDelete(Track track, DeletionCause cause) {
EventTrackDelete event = new EventTrackDelete(track.getName(), ImmutableList.copyOf(track.getGroups()), cause); EventTrackDelete event = new EventTrackDelete(track.getName(), ImmutableList.copyOf(track.getGroups()), cause);
fireEvent(event); fireEventAsync(event);
} }
public void handleTrackLoadAll() { public void handleTrackLoadAll() {
EventTrackLoadAll event = new EventTrackLoadAll(); EventTrackLoadAll event = new EventTrackLoadAll();
fireEvent(event); fireEventAsync(event);
} }
public void handleTrackLoad(Track track) { public void handleTrackLoad(Track track) {
EventTrackLoad event = new EventTrackLoad(track.getDelegate()); EventTrackLoad event = new EventTrackLoad(track.getDelegate());
fireEvent(event); fireEventAsync(event);
} }
public void handleTrackAddGroup(Track track, String group, List<String> before, List<String> after) { public void handleTrackAddGroup(Track track, String group, List<String> before, List<String> after) {
EventTrackAddGroup event = new EventTrackAddGroup(group, track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.copyOf(after)); EventTrackAddGroup event = new EventTrackAddGroup(group, track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.copyOf(after));
fireEvent(event); fireEventAsync(event);
} }
public void handleTrackClear(Track track, List<String> before) { public void handleTrackClear(Track track, List<String> before) {
EventTrackClear event = new EventTrackClear(track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.of()); EventTrackClear event = new EventTrackClear(track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.of());
fireEvent(event); fireEventAsync(event);
} }
public void handleTrackRemoveGroup(Track track, String group, List<String> before, List<String> after) { public void handleTrackRemoveGroup(Track track, String group, List<String> before, List<String> after) {
EventTrackRemoveGroup event = new EventTrackRemoveGroup(group, track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.copyOf(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) { public void handleUserCacheLoad(User user, UserData data) {
EventUserCacheLoad event = new EventUserCacheLoad(user.getDelegate(), data); EventUserCacheLoad event = new EventUserCacheLoad(user.getDelegate(), data);
fireEvent(event); fireEventAsync(event);
} }
public void handleUserDataRecalculate(User user, UserData data) { public void handleUserDataRecalculate(User user, UserData data) {
EventUserDataRecalculate event = new EventUserDataRecalculate(user.getDelegate(), data); EventUserDataRecalculate event = new EventUserDataRecalculate(user.getDelegate(), data);
fireEvent(event); fireEventAsync(event);
} }
public void handleUserFirstLogin(UUID uuid, String username) { public void handleUserFirstLogin(UUID uuid, String username) {
EventUserFirstLogin event = new EventUserFirstLogin(uuid, username); EventUserFirstLogin event = new EventUserFirstLogin(uuid, username);
fireEvent(event); fireEventAsync(event);
} }
public void handleUserLoad(User user) { public void handleUserLoad(User user) {
EventUserLoad event = new EventUserLoad(user.getDelegate()); 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); fireEvent(event);
} }
public void handleUserDemote(User user, Track track, String from, String to) { public void handleUserDemote(User user, Track track, String from, String to) {
EventUserDemote event = new EventUserDemote(track.getDelegate(), user.getDelegate(), from, 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) { public void handleUserPromote(User user, Track track, String from, String to) {
EventUserPromote event = new EventUserPromote(track.getDelegate(), user.getDelegate(), from, to); EventUserPromote event = new EventUserPromote(track.getDelegate(), user.getDelegate(), from, to);
fireEvent(event); fireEventAsync(event);
} }
} }

View File

@ -0,0 +1,47 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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;
}

View File

@ -57,7 +57,8 @@ public enum CommandSpec {
VERBOSE("Manage verbose permission checking", "/%s verbose <true|false> [filter]", VERBOSE("Manage verbose permission checking", "/%s verbose <true|false> [filter]",
Arg.list( Arg.list(
Arg.create("on|record|off|paste", true, "whether to enable/disable logging, or to paste the logged output"), 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]", TREE("Generate a tree view of permissions", "/%s tree [selection] [max level] [player]",

View File

@ -57,7 +57,7 @@ public enum Message {
OP_DISABLED("&bThe vanilla OP system is disabled on this server.", false), 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), 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), 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("&3EXPORT &3&l> &f{0}", true),
EXPORT_LOG_PROGRESS("&3EXPORT &3&l> &7{0}", true), EXPORT_LOG_PROGRESS("&3EXPORT &3&l> &7{0}", true),

View File

@ -39,35 +39,8 @@ import java.util.UUID;
@RequiredArgsConstructor @RequiredArgsConstructor
public class GenericUserManager extends AbstractManager<UserIdentifier, User> implements UserManager { public class GenericUserManager extends AbstractManager<UserIdentifier, User> implements UserManager {
public static boolean giveDefaultIfNeeded(User user, boolean save, LuckPermsPlugin plugin) {
boolean hasGroup = false;
if (user.getPrimaryGroup().getStoredValue() != null && !user.getPrimaryGroup().getStoredValue().isEmpty()) { private final LuckPermsPlugin plugin;
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;
}
@Override @Override
public User getOrMake(UserIdentifier id) { public User getOrMake(UserIdentifier id) {
@ -78,39 +51,6 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
return ret; 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 @Override
public User apply(UserIdentifier id) { public User apply(UserIdentifier id) {
return !id.getUsername().isPresent() ? return !id.getUsername().isPresent() ?
@ -140,9 +80,12 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> im
} }
@Override @Override
public void cleanup(User user) { public boolean cleanup(User user) {
if (!plugin.isPlayerOnline(plugin.getUuidCache().getExternalUUID(user.getUuid()))) { if (!plugin.isPlayerOnline(plugin.getUuidCache().getExternalUUID(user.getUuid()))) {
unload(user); unload(user);
return true;
} else {
return false;
} }
} }
@ -177,4 +120,65 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> 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");
}
} }

View File

@ -61,7 +61,7 @@ public interface UserManager extends Manager<UserIdentifier, User> {
* *
* @param user The user to be cleaned up * @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. * Schedules a task to cleanup a user after a certain period of time, if they're not on the server anymore.

View File

@ -546,7 +546,7 @@ public abstract class PermissionHolder {
if (!n.isGroupNode()) continue; if (!n.isGroupNode()) continue;
String groupName = n.getGroupName(); 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()))) { if (!((contexts.isApplyGlobalGroups() || n.isServerSpecific()) && (contexts.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) {
continue; continue;
@ -624,7 +624,7 @@ public abstract class PermissionHolder {
if (!n.isGroupNode()) continue; if (!n.isGroupNode()) continue;
String groupName = n.getGroupName(); 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); Group g = plugin.getGroupManager().getIfLoaded(groupName);
if (g != null) { if (g != null) {
@ -717,11 +717,11 @@ public abstract class PermissionHolder {
for (Node node : entries) { for (Node node : entries) {
String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission(); String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission();
if (perms.putIfAbsent(perm, node.getValue()) == null) { if (perms.putIfAbsent(perm, node.getValuePrimitive()) == null) {
if (applyShorthand) { if (applyShorthand) {
List<String> sh = node.resolveShorthand(); List<String> sh = node.resolveShorthand();
if (!sh.isEmpty()) { 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) { for (Node node : entries) {
String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission(); String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission();
if (perms.putIfAbsent(perm, node.getValue()) == null) { if (perms.putIfAbsent(perm, node.getValuePrimitive()) == null) {
if (applyShorthand) { if (applyShorthand) {
List<String> sh = node.resolveShorthand(); List<String> sh = node.resolveShorthand();
if (!sh.isEmpty()) { 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<Node> nodes = filterNodes(context.getContextSet()); List<Node> nodes = filterNodes(context.getContextSet());
for (Node node : nodes) { for (Node node : nodes) {
if (!node.getValue()) continue; if (!node.getValuePrimitive()) continue;
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue; if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
if (!((contexts.isIncludeGlobal() || node.isServerSpecific()) && (contexts.isIncludeGlobalWorld() || node.isWorldSpecific()))) { if (!((contexts.isIncludeGlobal() || node.isServerSpecific()) && (contexts.isIncludeGlobalWorld() || node.isWorldSpecific()))) {
@ -793,7 +793,7 @@ public abstract class PermissionHolder {
if (!n.isGroupNode()) continue; if (!n.isGroupNode()) continue;
String groupName = n.getGroupName(); 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()))) { if (!((contexts.isApplyGlobalGroups() || n.isServerSpecific()) && (contexts.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) {
continue; continue;
@ -834,7 +834,7 @@ public abstract class PermissionHolder {
List<Node> nodes = getOwnNodes(); List<Node> nodes = getOwnNodes();
for (Node node : nodes) { for (Node node : nodes) {
if (!node.getValue()) continue; if (!node.getValuePrimitive()) continue;
if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue; if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue;
accumulator.accumulateNode(ImmutableLocalizedNode.of(node, getObjectName())); accumulator.accumulateNode(ImmutableLocalizedNode.of(node, getObjectName()));
@ -853,7 +853,7 @@ public abstract class PermissionHolder {
if (!n.isGroupNode()) continue; if (!n.isGroupNode()) continue;
String groupName = n.getGroupName(); 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); Group g = plugin.getGroupManager().getIfLoaded(groupName);
if (g != null) { if (g != null) {

View File

@ -70,13 +70,7 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
private final UserCache userData = new UserCache(this); private final UserCache userData = new UserCache(this);
@Getter @Getter
private BufferedRequest<Void> refreshBuffer = new BufferedRequest<Void>(250L, 50L, r -> getPlugin().doAsync(r)) { private BufferedRequest<Void> refreshBuffer = new UserRefreshBuffer(this);
@Override
protected Void perform() {
refreshPermissions();
return null;
}
};
@Getter @Getter
private final UserDelegate delegate = new UserDelegate(this); private final UserDelegate delegate = new UserDelegate(this);
@ -214,4 +208,20 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
public void cleanup() { public void cleanup() {
userData.cleanup(); userData.cleanup();
} }
private static final class UserRefreshBuffer extends BufferedRequest<Void> {
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;
}
}
} }

View File

@ -28,7 +28,6 @@ package me.lucko.luckperms.common.node;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -50,18 +49,31 @@ import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; 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"}) @ToString(of = {"permission", "value", "override", "server", "world", "expireAt", "contexts"})
public final class ImmutableNode implements Node { public final class ImmutableNode implements Node {
private static final int NODE_SEPARATOR_CHAR = Character.getNumericValue('.'); 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 @Getter
private final String permission; private final String permission;
@Getter private final boolean value;
private final Boolean value;
@Getter @Getter
private boolean override; private boolean override;
@ -80,12 +92,28 @@ public final class ImmutableNode implements Node {
@Getter @Getter
private final ImmutableContextSet fullContexts; 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<String> optServer; private final Optional<String> optServer;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private final Optional<String> optWorld; private final Optional<String> optWorld;
private final int hashCode;
private final boolean isGroup; private final boolean isGroup;
private String groupName; private String groupName;
@ -103,7 +131,7 @@ public final class ImmutableNode implements Node {
private final List<String> resolvedShorthand; private final List<String> resolvedShorthand;
private final String serializedNode; private String serializedNode = null;
/** /**
* Make an immutable node instance * Make an immutable node instance
@ -116,7 +144,7 @@ public final class ImmutableNode implements Node {
* @param contexts any additional contexts applying to this node * @param contexts any additional contexts applying to this node
*/ */
@SuppressWarnings("deprecation") @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("")) { if (permission == null || permission.equals("")) {
throw new IllegalArgumentException("Empty permission"); throw new IllegalArgumentException("Empty permission");
} }
@ -135,18 +163,20 @@ public final class ImmutableNode implements Node {
world = null; world = null;
} }
this.permission = NodeFactory.unescapeDelimiters(permission, "/", "-", "$", "(", ")", "=", ","); this.permission = NodeFactory.unescapeDelimiters(permission, PERMISSION_DELIMS).intern();
this.value = value; this.value = value;
this.override = override; this.override = override;
this.expireAt = expireAt; this.expireAt = expireAt;
this.server = NodeFactory.unescapeDelimiters(server, "/", "-"); this.server = internString(NodeFactory.unescapeDelimiters(server, SERVER_WORLD_DELIMS));
this.world = NodeFactory.unescapeDelimiters(world, "/", "-"); this.world = internString(NodeFactory.unescapeDelimiters(world, SERVER_WORLD_DELIMS));
this.contexts = contexts == null ? ContextSet.empty() : contexts.makeImmutable(); this.contexts = contexts == null ? ContextSet.empty() : contexts.makeImmutable();
String lowerCasePermission = this.permission.toLowerCase();
// Setup state // Setup state
isGroup = this.permission.toLowerCase().startsWith("group."); isGroup = lowerCasePermission.startsWith("group.");
if (isGroup) { if (isGroup) {
groupName = this.permission.substring("group.".length()).toLowerCase(); groupName = lowerCasePermission.substring("group.".length()).intern();
} }
isWildcard = this.permission.endsWith(".*"); isWildcard = this.permission.endsWith(".*");
@ -154,38 +184,48 @@ public final class ImmutableNode implements Node {
isMeta = NodeFactory.isMetaNode(this.permission); isMeta = NodeFactory.isMetaNode(this.permission);
if (isMeta) { if (isMeta) {
List<String> metaPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("meta.".length())); List<String> metaPart = META_SPLITTER.splitToList(this.permission.substring("meta.".length()));
meta = Maps.immutableEntry(MetaUtils.unescapeCharacters(metaPart.get(0)), MetaUtils.unescapeCharacters(metaPart.get(1))); meta = Maps.immutableEntry(MetaUtils.unescapeCharacters(metaPart.get(0)).intern(), MetaUtils.unescapeCharacters(metaPart.get(1)).intern());
} }
isPrefix = NodeFactory.isPrefixNode(this.permission); isPrefix = NodeFactory.isPrefixNode(this.permission);
if (isPrefix) { if (isPrefix) {
List<String> prefixPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("prefix.".length())); List<String> prefixPart = META_SPLITTER.splitToList(this.permission.substring("prefix.".length()));
Integer i = Integer.parseInt(prefixPart.get(0)); 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); isSuffix = NodeFactory.isSuffixNode(this.permission);
if (isSuffix) { if (isSuffix) {
List<String> suffixPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("suffix.".length())); List<String> suffixPart = META_SPLITTER.splitToList(this.permission.substring("suffix.".length()));
Integer i = Integer.parseInt(suffixPart.get(0)); 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())); resolvedShorthand = ImmutableList.copyOf(ShorthandParser.parseShorthand(getPermission()));
serializedNode = calculateSerializedNode();
if (this.server != null || this.world != null) {
MutableContextSet fullContexts = this.contexts.mutableCopy(); MutableContextSet fullContexts = this.contexts.mutableCopy();
if (isServerSpecific()) { if (this.server != null) {
fullContexts.add("server", this.server); fullContexts.add("server", this.server);
} }
if (isWorldSpecific()) { if (this.world != null) {
fullContexts.add("world", this.world); fullContexts.add("world", this.world);
} }
this.fullContexts = fullContexts.makeImmutable(); this.fullContexts = fullContexts.makeImmutable();
} else {
this.fullContexts = this.contexts;
}
this.optServer = Optional.ofNullable(this.server); this.optServer = Optional.ofNullable(this.server);
this.optWorld = Optional.ofNullable(this.world); this.optWorld = Optional.ofNullable(this.world);
this.hashCode = calculateHashCode();
}
@Override
public boolean getValuePrimitive() {
return value;
} }
@Override @Override
@ -225,19 +265,19 @@ public final class ImmutableNode implements Node {
@Override @Override
public long getExpiryUnixTime() { public long getExpiryUnixTime() {
Preconditions.checkState(isTemporary(), "Node does not have an expiry time."); checkState(isTemporary(), "Node does not have an expiry time.");
return expireAt; return expireAt;
} }
@Override @Override
public Date getExpiry() { 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); return new Date(expireAt * 1000L);
} }
@Override @Override
public long getSecondsTilExpiry() { 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(); return expireAt - DateUtil.unixSecondsNow();
} }
@ -253,7 +293,7 @@ public final class ImmutableNode implements Node {
@Override @Override
public String getGroupName() { public String getGroupName() {
Preconditions.checkState(isGroupNode(), "Node is not a group node"); checkState(isGroupNode(), "Node is not a group node");
return groupName; return groupName;
} }
@ -274,7 +314,7 @@ public final class ImmutableNode implements Node {
@Override @Override
public Map.Entry<String, String> getMeta() { public Map.Entry<String, String> getMeta() {
Preconditions.checkState(isMeta(), "Node is not a meta node"); checkState(isMeta(), "Node is not a meta node");
return meta; return meta;
} }
@ -285,7 +325,7 @@ public final class ImmutableNode implements Node {
@Override @Override
public Map.Entry<Integer, String> getPrefix() { public Map.Entry<Integer, String> getPrefix() {
Preconditions.checkState(isPrefix(), "Node is not a prefix node"); checkState(isPrefix(), "Node is not a prefix node");
return prefix; return prefix;
} }
@ -296,7 +336,7 @@ public final class ImmutableNode implements Node {
@Override @Override
public Map.Entry<Integer, String> getSuffix() { public Map.Entry<Integer, String> getSuffix() {
Preconditions.checkState(isSuffix(), "Node is not a suffix node"); checkState(isSuffix(), "Node is not a suffix node");
return suffix; return suffix;
} }
@ -395,7 +435,10 @@ public final class ImmutableNode implements Node {
} }
@Override @Override
public String toSerializedNode() { public synchronized String toSerializedNode() {
if (serializedNode == null) {
serializedNode = calculateSerializedNode();
}
return serializedNode; return serializedNode;
} }
@ -403,16 +446,11 @@ public final class ImmutableNode implements Node {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
if (server != null) { if (server != null) {
builder.append(NodeFactory.escapeDelimiters(server, "/", "-")); builder.append(NodeFactory.escapeDelimiters(server, SERVER_WORLD_DELIMS));
if (world != null) builder.append("-").append(NodeFactory.escapeDelimiters(world, SERVER_WORLD_DELIMS));
if (world != null) {
builder.append("-").append(NodeFactory.escapeDelimiters(world, "/", "-"));
}
builder.append("/"); builder.append("/");
} else { } else {
if (world != null) { if (world != null) builder.append("global-").append(NodeFactory.escapeDelimiters(world, SERVER_WORLD_DELIMS)).append("/");
builder.append("global-").append(NodeFactory.escapeDelimiters(world, "/", "-")).append("/");
}
} }
if (!contexts.isEmpty()) { if (!contexts.isEmpty()) {
@ -420,162 +458,110 @@ public final class ImmutableNode implements Node {
for (Map.Entry<String, String> entry : contexts.toSet()) { for (Map.Entry<String, String> entry : contexts.toSet()) {
builder.append(NodeFactory.escapeDelimiters(entry.getKey(), "=", "(", ")", ",")).append("=").append(NodeFactory.escapeDelimiters(entry.getValue(), "=", "(", ")", ",")).append(","); builder.append(NodeFactory.escapeDelimiters(entry.getKey(), "=", "(", ")", ",")).append("=").append(NodeFactory.escapeDelimiters(entry.getValue(), "=", "(", ")", ",")).append(",");
} }
builder.deleteCharAt(builder.length() - 1); builder.deleteCharAt(builder.length() - 1);
builder.append(")"); builder.append(")");
} }
builder.append(NodeFactory.escapeDelimiters(permission, "/", "-", "$", "(", ")", "=", ",")); builder.append(NodeFactory.escapeDelimiters(permission, "/", "-", "$", "(", ")", "=", ","));
if (expireAt != 0L) builder.append("$").append(expireAt);
if (expireAt != 0L) {
builder.append("$").append(expireAt);
}
return builder.toString(); return builder.toString();
} }
@SuppressWarnings("StringEquality")
@Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (o == this) return true; if (o == this) return true;
if (!(o instanceof Node)) return false; if (!(o instanceof Node)) return false;
final Node other = (Node) o; final Node other = (Node) o;
if (!this.permission.equals(other.getPermission())) return false; if (this.permission != other.getPermission()) return false;
if (!this.getValue().equals(other.getValue())) return false; if (this.value != other.getValuePrimitive()) return false;
if (this.override != other.isOverride()) 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); final String otherServer = other.getServer().orElse(null);
if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false; 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); final String otherWorld = other.getWorld().orElse(null);
if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) return false; 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; final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L;
return this.expireAt == otherExpireAt && this.getContexts().equals(other.getContexts());
return thisExpireAt == otherExpireAt && this.getContexts().equals(other.getContexts());
} }
@Override
public int hashCode() { public int hashCode() {
return this.hashCode;
}
private int calculateHashCode() {
final int PRIME = 59; final int PRIME = 59;
int result = 1; int result = 1;
result = result * PRIME + this.permission.hashCode(); 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); result = result * PRIME + (this.override ? 79 : 97);
result = result * PRIME + (this.server == null ? 43 : this.server.hashCode());
final String server = this.getServer().orElse(null); result = result * PRIME + (this.world == null ? 43 : this.world.hashCode());
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 + (int) (this.expireAt >>> 32 ^ this.expireAt); result = result * PRIME + (int) (this.expireAt >>> 32 ^ this.expireAt);
result = result * PRIME + this.contexts.hashCode(); result = result * PRIME + this.contexts.hashCode();
return result; return result;
} }
@SuppressWarnings("StringEquality")
@Override @Override
public boolean equalsIgnoringValue(Node other) { public boolean equalsIgnoringValue(Node other) {
if (!other.getPermission().equalsIgnoreCase(this.getPermission())) { if (this.permission != other.getPermission()) return false;
return false; if (this.override != other.isOverride()) return false;
}
final String thisServer = this.server;
if (other.isTemporary() != this.isTemporary()) { final String otherServer = other.getServer().orElse(null);
return false; if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false;
}
final String thisWorld = this.world;
if (this.isTemporary()) { final String otherWorld = other.getWorld().orElse(null);
if (other.getExpiryUnixTime() != this.getExpiryUnixTime()) { if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) return false;
return false;
} final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L;
} return this.expireAt == otherExpireAt && this.getContexts().equals(other.getContexts());
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());
} }
@SuppressWarnings("StringEquality")
@Override @Override
public boolean almostEquals(Node other) { public boolean almostEquals(Node other) {
if (!other.getPermission().equalsIgnoreCase(this.getPermission())) { if (this.permission != other.getPermission()) return false;
if (this.override != other.isOverride()) return false;
final String thisServer = this.server;
final String otherServer = other.getServer().orElse(null);
if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer))
return false; 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.isTemporary() != this.isTemporary()) { @SuppressWarnings("StringEquality")
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());
}
@Override @Override
public boolean equalsIgnoringValueOrTemp(Node other) { public boolean equalsIgnoringValueOrTemp(Node other) {
if (!other.getPermission().equalsIgnoreCase(this.getPermission())) { if (this.permission != other.getPermission()) return false;
return false; if (this.override != other.isOverride()) return false;
}
if (other.getServer().isPresent() == this.getServer().isPresent()) { final String thisServer = this.server;
if (other.getServer().isPresent()) { final String otherServer = other.getServer().orElse(null);
if (!other.getServer().get().equalsIgnoreCase(this.getServer().get())) { if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer))
return false; return false;
}
}
} else {
return false;
}
if (other.getWorld().isPresent() == this.getWorld().isPresent()) { final String thisWorld = this.world;
if (other.getWorld().isPresent()) { final String otherWorld = other.getWorld().orElse(null);
if (!other.getWorld().get().equalsIgnoreCase(this.getWorld().get())) { return (thisWorld == null ? otherWorld == null : thisWorld.equals(otherWorld)) &&
return false; this.getContexts().equals(other.getContexts());
}
}
} else {
return false;
}
return other.getContexts().equals(this.getContexts());
} }
@Override @Override
@ -603,39 +589,33 @@ public final class ImmutableNode implements Node {
} }
for (String s : expandedThisStr) { for (String s : expandedThisStr) {
if (p.matcher(s).matches()) { if (p.matcher(s).matches()) return true;
return true;
}
} }
return false; return false;
} }
if (thisStr.toLowerCase().startsWith("r=") && applyRegex) { if (thisStr.toLowerCase().startsWith("r=") && applyRegex) {
Pattern p = PatternCache.compile(thisStr.substring(2)); Pattern p = PatternCache.compile(thisStr.substring(2));
if (p == null) { if (p == null) return false;
return false;
}
for (String s : expandedStr) { for (String s : expandedStr) {
if (p.matcher(s).matches()) { if (p.matcher(s).matches()) return true;
return true;
}
} }
return false; return false;
} }
if (expandedStr.size() <= 1 && expandedThisStr.size() <= 1) { if (expandedStr.size() <= 1 && expandedThisStr.size() <= 1) return false;
return false;
}
for (String t : expandedThisStr) { for (String t : expandedThisStr) {
for (String s : expandedStr) { for (String s : expandedStr) {
if (t.equalsIgnoreCase(s)) { if (t.equalsIgnoreCase(s)) return true;
return true;
}
} }
} }
return false; return false;
} }
private static String internString(String s) {
return s == null ? null : s.intern();
}
} }

View File

@ -82,7 +82,7 @@ class NodeBuilder implements Node.Builder {
NodeBuilder(Node other) { NodeBuilder(Node other) {
this.permission = other.getPermission(); this.permission = other.getPermission();
this.value = other.getValue(); this.value = other.getValuePrimitive();
this.override = other.isOverride(); this.override = other.isOverride();
this.server = other.getServer().orElse(null); this.server = other.getServer().orElse(null);
this.world = other.getWorld().orElse(null); this.world = other.getWorld().orElse(null);

View File

@ -151,7 +151,7 @@ public class NodeFactory {
return appendContextToCommand(sb, node).toString(); 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; ChatMetaType type = node.isPrefix() ? ChatMetaType.PREFIX : ChatMetaType.SUFFIX;
String typeName = type.name().toLowerCase(); String typeName = type.name().toLowerCase();
@ -171,7 +171,7 @@ public class NodeFactory {
return appendContextToCommand(sb, node).toString(); 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 ")); sb.append(node.isTemporary() ? (set ? "meta settemp " : "meta unsettemp ") : (set ? "meta set " : "meta unset "));
if (node.getMeta().getKey().contains(" ")) { if (node.getMeta().getKey().contains(" ")) {
@ -204,7 +204,7 @@ public class NodeFactory {
sb.append(node.getPermission()); sb.append(node.getPermission());
} }
if (set) { if (set) {
sb.append(" ").append(node.getValue()); sb.append(" ").append(node.getValuePrimitive());
if (node.isTemporary()) { if (node.isTemporary()) {
sb.append(" ").append(node.getExpiryUnixTime()); sb.append(" ").append(node.getExpiryUnixTime());

View File

@ -33,6 +33,7 @@ import com.google.common.collect.Multimap;
import me.lucko.luckperms.api.HeldPermission; import me.lucko.luckperms.api.HeldPermission;
import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ContextSet;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
@ -55,7 +56,7 @@ public final class NodeHeldPermission<T> implements HeldPermission<T> {
@Override @Override
public boolean getValue() { public boolean getValue() {
return node.getValue(); return node.getValuePrimitive();
} }
@Override @Override
@ -78,6 +79,11 @@ public final class NodeHeldPermission<T> implements HeldPermission<T> {
return node.getContexts().toMultimap(); return node.getContexts().toMultimap();
} }
@Override
public ContextSet getContexts() {
return node.getContexts();
}
@Override @Override
public Node asNode() { public Node asNode() {
return node; return node;

View File

@ -64,7 +64,7 @@ public final class NodeModel {
public static NodeModel fromNode(Node node) { public static NodeModel fromNode(Node node) {
return NodeModel.of( return NodeModel.of(
node.getPermission(), node.getPermission(),
node.getValue(), node.getValuePrimitive(),
node.getServer().orElse("global"), node.getServer().orElse("global"),
node.getWorld().orElse("global"), node.getWorld().orElse("global"),
node.isTemporary() ? node.getExpiryUnixTime() : 0L, node.isTemporary() ? node.getExpiryUnixTime() : 0L,

View File

@ -220,12 +220,14 @@ public interface LuckPermsPlugin {
/** /**
* Gets the name or "brand" of the running platform * Gets the name or "brand" of the running platform
*
* @return the server brand * @return the server brand
*/ */
String getServerName(); String getServerName();
/** /**
* Gets the version of the running platform * Gets the version of the running platform
*
* @return the server version * @return the server version
*/ */
String getServerVersion(); String getServerVersion();

View File

@ -57,7 +57,7 @@ public class ParentsByWeightHolder extends StoredHolder {
List<Group> groups = new ArrayList<>(); List<Group> groups = new ArrayList<>();
for (Node node : user.filterNodes(contextSet)) { for (Node node : user.filterNodes(contextSet)) {
if (!node.getValue() || !node.isGroupNode()) { if (!node.getValuePrimitive() || !node.isGroupNode()) {
continue; continue;
} }

View File

@ -755,7 +755,7 @@ public class MongoDBBacking extends AbstractBacking {
public static Map<String, Boolean> exportToLegacy(Iterable<Node> nodes) { public static Map<String, Boolean> exportToLegacy(Iterable<Node> nodes) {
Map<String, Boolean> m = new HashMap<>(); Map<String, Boolean> m = new HashMap<>();
for (Node node : nodes) { for (Node node : nodes) {
m.put(node.toSerializedNode(), node.getValue()); m.put(node.toSerializedNode(), node.getValuePrimitive());
} }
return m; return m;
} }

View File

@ -31,8 +31,9 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import me.lucko.luckperms.api.Tristate; 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.utils.PasteUtils;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -194,7 +195,7 @@ public class TreeView {
* @return the url, or null * @return the url, or null
* @see PasteUtils#paste(String, List) * @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 // only paste if there is actually data here
if (!hasData()) { if (!hasData()) {
throw new IllegalStateException(); throw new IllegalStateException();
@ -213,7 +214,7 @@ public class TreeView {
for (Map.Entry<String, String> e : ret) { for (Map.Entry<String, String> e : ret) {
// lookup a permission value for the node // 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 // append the data to the paste
builder.add(getTristateDiffPrefix(tristate) + e.getKey() + e.getValue()); builder.add(getTristateDiffPrefix(tristate) + e.getKey() + e.getValue());

View File

@ -41,7 +41,7 @@ import java.util.concurrent.CompletableFuture;
@UtilityClass @UtilityClass
public class LoginHelper { 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 long startTime = System.currentTimeMillis();
final UuidCache cache = plugin.getUuidCache(); final UuidCache cache = plugin.getUuidCache();
@ -97,6 +97,8 @@ public class LoginHelper {
if (time >= 1000) { if (time >= 1000) {
plugin.getLog().warn("Processing login for " + username + " took " + time + "ms."); plugin.getLog().warn("Processing login for " + username + " took " + time + "ms.");
} }
return user;
} }
public static void refreshPlayer(LuckPermsPlugin plugin, UUID uuid) { public static void refreshPlayer(LuckPermsPlugin plugin, UUID uuid) {

View File

@ -33,13 +33,18 @@ import net.kyori.text.serializer.ComponentSerializers;
import java.util.Arrays; import java.util.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@UtilityClass @UtilityClass
public class TextUtils { public class TextUtils {
public String joinNewline(String... strings) { public String joinNewline(String... strings) {
return Arrays.stream(strings).collect(Collectors.joining("\n")); return joinNewline(Arrays.stream(strings));
}
public String joinNewline(Stream<String> strings) {
return strings.collect(Collectors.joining("\n"));
} }
public TextComponent fromLegacy(String input, char character) { public TextComponent fromLegacy(String input, char character) {

View File

@ -29,6 +29,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet;
/** /**
* Holds the data from a permission check * Holds the data from a permission check
@ -37,11 +38,26 @@ import me.lucko.luckperms.api.Tristate;
@AllArgsConstructor @AllArgsConstructor
public class CheckData { public class CheckData {
/**
* The origin of the check
*/
private final CheckOrigin checkOrigin;
/** /**
* The name of the entity which was checked * The name of the entity which was checked
*/ */
private final String checkTarget; 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 * The permission which was checked for
*/ */

View File

@ -0,0 +1,60 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.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;
}

View File

@ -28,6 +28,7 @@ package me.lucko.luckperms.common.verbose;
import lombok.Setter; import lombok.Setter;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.common.commands.sender.Sender; import me.lucko.luckperms.common.commands.sender.Sender;
import java.util.Map; import java.util.Map;
@ -70,18 +71,23 @@ public class VerboseHandler implements Runnable {
* <p>The check data is added to a queue to be processed later, to avoid blocking * <p>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.</p> * the main thread each time a permission check is made.</p>
* *
* @param checkOrigin the origin of the check
* @param checkTarget the target of the permission 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 permission the permission which was checked for
* @param result the result of the permission check * @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 // don't bother even processing the check if there are no listeners registered
if (!listening) { if (!listening) {
return; return;
} }
//noinspection ThrowableNotThrown
StackTraceElement[] trace = new Exception().getStackTrace();
// add the check data to a queue to be processed later. // 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));
} }
/** /**

View File

@ -33,9 +33,15 @@ import com.google.common.collect.Maps;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.commands.sender.Sender; 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.locale.Message;
import me.lucko.luckperms.common.utils.DateUtil; import me.lucko.luckperms.common.utils.DateUtil;
import me.lucko.luckperms.common.utils.PasteUtils; 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.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -55,6 +61,9 @@ public class VerboseListener {
// how much data should we store before stopping. // how much data should we store before stopping.
private static final int DATA_TRUNCATION = 10000; 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 // the time when the listener was first registered
private final long startTime = System.currentTimeMillis(); private final long startTime = System.currentTimeMillis();
@ -98,7 +107,64 @@ public class VerboseListener {
} }
if (notify) { 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<String> 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 * @return the url
* @see PasteUtils#paste(String, List) * @see PasteUtils#paste(String, List)
*/ */
public String uploadPasteData() { public String uploadPasteData(boolean showTraces) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
String startDate = DATE_FORMAT.format(new Date(startTime)); String startDate = DATE_FORMAT.format(new Date(startTime));
String endDate = DATE_FORMAT.format(new Date(now)); String endDate = DATE_FORMAT.format(new Date(now));
@ -132,9 +198,10 @@ public class VerboseListener {
.add("| Start Time | " + startDate + " |") .add("| Start Time | " + startDate + " |")
.add("| End Time | " + endDate + " |") .add("| End Time | " + endDate + " |")
.add("| Duration | " + duration +" |") .add("| Duration | " + duration +" |")
.add("| Count | **" + matchedCounter.get() + "** / " + counter + " |") .add("| Count | **" + matchedCounter.get() + "** / " + counter.get() + " |")
.add("| User | " + notifiedSender.getName() + " |") .add("| User | " + notifiedSender.getName() + " |")
.add("| Filter | " + filter + " |") .add("| Filter | " + filter + " |")
.add("| Include traces | " + showTraces + " |")
.add(""); .add("");
if (matchedCounter.get() > results.size()) { if (matchedCounter.get() > results.size()) {
@ -142,8 +209,14 @@ public class VerboseListener {
prettyOutput.add(""); 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") prettyOutput.add("### Output")
.add("Format: `<checked>` `<permission>` `<value>`") .add("Format: `<checked>` `<origin>` `<permission>` `<value>`")
.add("") .add("")
.add("___") .add("___")
.add(""); .add("");
@ -151,8 +224,49 @@ public class VerboseListener {
ImmutableList.Builder<String> csvOutput = ImmutableList.<String>builder() ImmutableList.Builder<String> csvOutput = ImmutableList.<String>builder()
.add("User,Permission,Result"); .add("User,Permission,Result");
AtomicInteger printedCount = new AtomicInteger(0);
results.forEach(c -> { 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("<br><code>" + c.getCheckTarget() + "</code> - " + c.getPermission() + " - " + getTristateSymbol(c.getResult()));
} else {
prettyOutput.add("<details><summary><code>" + c.getCheckTarget() + "</code> - " + c.getPermission() + " - " + getTristateSymbol(c.getResult()) + "</summary><p>");
prettyOutput.add("<br><b>Origin:</b> <code>" + c.getCheckOrigin().name() + "</code>");
prettyOutput.add("<br><b>Context:</b> <code>" + Util.stripColor(Util.contextSetToString(c.getCheckContext())) + "</code>");
prettyOutput.add("<br><b>Trace:</b><pre>");
// 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("</pre></p></details>");
}
csvOutput.add(escapeCommas(c.getCheckTarget()) + "," + escapeCommas(c.getPermission()) + "," + c.getResult().name().toLowerCase()); csvOutput.add(escapeCommas(c.getCheckTarget()) + "," + escapeCommas(c.getPermission()) + "," + c.getResult().name().toLowerCase());
}); });
results.clear(); 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 "";
}
}
} }

View File

@ -6,7 +6,7 @@
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
<modules> <modules>
<module>api</module> <module>api</module>
@ -47,7 +47,7 @@
<maven.test.skip>true</maven.test.skip> <maven.test.skip>true</maven.test.skip>
<!-- Manually entered release version --> <!-- Manually entered release version -->
<release.version>3.3</release.version> <release.version>3.4</release.version>
<!-- Get how many commits have been made since the last tag (the previous release) --> <!-- Get how many commits have been made since the last tag (the previous release) -->
<patch.version>${git.closest.tag.commit.count}</patch.version> <patch.version>${git.closest.tag.commit.count}</patch.version>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -54,7 +54,7 @@
<dependency> <dependency>
<groupId>org.spongepowered</groupId> <groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId> <artifactId>spongeapi</artifactId>
<version>7.0.0-20170716.103113-1</version> <version>7.0.0-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Lombok --> <!-- Lombok -->

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>luckperms</artifactId> <artifactId>luckperms</artifactId>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<version>3.3-SNAPSHOT</version> <version>3.4-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -33,7 +33,7 @@
<dependency> <dependency>
<groupId>org.spongepowered</groupId> <groupId>org.spongepowered</groupId>
<artifactId>spongeapi</artifactId> <artifactId>spongeapi</artifactId>
<version>7.0.0-20170716.103113-1</version> <version>7.0.0-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>

View File

@ -72,6 +72,7 @@ public class SpongeListener {
Listening on AFTER_PRE priority to allow plugins to modify username / UUID data here. (auth plugins) */ Listening on AFTER_PRE priority to allow plugins to modify username / UUID data here. (auth plugins) */
final GameProfile p = e.getProfile(); 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)) { if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
plugin.getLog().info("Processing auth event for " + p.getUniqueId() + " - " + p.getName()); 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. - creating a user instance in the UserManager for this connection.
- setting up cached data. */ - setting up cached data. */
try { 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) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();

View File

@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.Contexts;
import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory;
import me.lucko.luckperms.common.calculators.PermissionCalculator; 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.config.ConfigKeys;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.processors.MapProcessor; 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())); 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 @Override
@ -79,7 +84,7 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory {
ret.add("Map"); ret.add("Map");
if (plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_IMPLICIT_WILDCARDS)) ret.add("SpongeWildcard"); 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_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"); if (plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_DEFAULT_SUBJECTS)) ret.add("Defaults");
return ret.build(); return ret.build();
} }

View File

@ -196,8 +196,9 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
} }
@Override @Override
public void cleanup(User user) { public boolean cleanup(User user) {
// Do nothing - this instance uses other means in order to cleanup // Do nothing - this instance uses other means in order to cleanup
return false;
} }
@Override @Override

View File

@ -97,7 +97,7 @@ public class SpongeGroup extends Group {
// TODO move this away from NodeTree // TODO move this away from NodeTree
Map<String, Boolean> permissions = getParent().getAllNodes(ExtractedContexts.generate(getPlugin().getService().calculateContexts(contexts))).stream() Map<String, Boolean> permissions = getParent().getAllNodes(ExtractedContexts.generate(getPlugin().getService().calculateContexts(contexts))).stream()
.map(LocalizedNode::getNode) .map(LocalizedNode::getNode)
.collect(Collectors.toMap(Node::getPermission, Node::getValue)); .collect(Collectors.toMap(Node::getPermission, Node::getValuePrimitive));
return NodeTree.of(permissions); return NodeTree.of(permissions);
}); });

View File

@ -34,6 +34,7 @@ import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.caching.MetaData; import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.model.User; 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.LPSpongePlugin;
import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsService;
import me.lucko.luckperms.sponge.service.LuckPermsSubjectData; import me.lucko.luckperms.sponge.service.LuckPermsSubjectData;
@ -129,7 +130,7 @@ public class SpongeUser extends User {
@Override @Override
public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) { public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) {
try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PERMISSION_VALUE)) { 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);
} }
} }

View File

@ -81,7 +81,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) { for (Map.Entry<ImmutableContextSet, Collection<Node>> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) {
ImmutableMap.Builder<String, Boolean> results = ImmutableMap.builder(); ImmutableMap.Builder<String, Boolean> results = ImmutableMap.builder();
for (Node n : e.getValue()) { for (Node n : e.getValue()) {
results.put(n.getPermission(), n.getValue()); results.put(n.getPermission(), n.getValuePrimitive());
} }
perms.put(e.getKey(), results); perms.put(e.getKey(), results);
} }
@ -322,7 +322,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
Map<ImmutableContextSet, Integer> minSuffixPriority = new HashMap<>(); Map<ImmutableContextSet, Integer> minSuffixPriority = new HashMap<>();
for (Node n : enduring ? holder.getEnduringNodes().values() : holder.getTransientNodes().values()) { 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; if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue;
ImmutableContextSet immutableContexts = n.getFullContexts().makeImmutable(); ImmutableContextSet immutableContexts = n.getFullContexts().makeImmutable();

View File

@ -39,8 +39,10 @@ import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ContextSet;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.calculators.PermissionCalculator; 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.MapProcessor;
import me.lucko.luckperms.common.processors.PermissionProcessor; 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.processors.SpongeWildcardProcessor;
import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPPermissionService;
import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubject;
@ -84,7 +86,7 @@ public class CalculatedSubjectData implements LPSubjectData {
processors.add(new MapProcessor()); processors.add(new MapProcessor());
processors.add(new SpongeWildcardProcessor()); 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)); holder.setPermissions(flattenMap(contexts, permissions));
return holder; return holder;
@ -100,7 +102,7 @@ public class CalculatedSubjectData implements LPSubjectData {
} }
public Tristate getPermissionValue(ContextSet contexts, String permission) { 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<ImmutableContextSet, Map<String, Boolean>> map) { public void replacePermissions(Map<ImmutableContextSet, Map<String, Boolean>> map) {

View File

@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.buffers.BufferedRequest; 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.LuckPermsService;
import me.lucko.luckperms.sponge.service.ProxyFactory; import me.lucko.luckperms.sponge.service.ProxyFactory;
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData; 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) { public Tristate getPermissionValue(@NonNull ImmutableContextSet contexts, @NonNull String node) {
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) {
Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts)); 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; return t;
} }
} }