From 096885d91ffaf3243ee3ebe0572cdf013b589a2e Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 10 Sep 2017 21:23:54 +0100 Subject: [PATCH] Add tracing to /lp verbose, API updates/cleanup, add login process event, and utilise string interning for faster context/node comparisons --- api/pom.xml | 2 +- .../java/me/lucko/luckperms/LuckPerms.java | 40 ++- .../me/lucko/luckperms/api/ChatMetaType.java | 19 ++ .../lucko/luckperms/api/DataMutateResult.java | 43 +++ .../java/me/lucko/luckperms/api/Group.java | 2 +- .../lucko/luckperms/api/HeldPermission.java | 13 +- .../lucko/luckperms/api/LPConfiguration.java | 60 ++-- .../me/lucko/luckperms/api/LuckPermsApi.java | 9 +- .../me/lucko/luckperms/api/MetaUtils.java | 9 +- .../java/me/lucko/luckperms/api/Node.java | 85 +++-- .../me/lucko/luckperms/api/NodeFactory.java | 62 ++-- .../lucko/luckperms/api/PermissionHolder.java | 11 +- .../me/lucko/luckperms/api/PlatformType.java | 7 +- .../java/me/lucko/luckperms/api/Storage.java | 13 +- .../java/me/lucko/luckperms/api/Track.java | 9 +- .../java/me/lucko/luckperms/api/Tristate.java | 25 +- .../java/me/lucko/luckperms/api/User.java | 26 +- .../me/lucko/luckperms/api/UuidCache.java | 24 +- .../luckperms/api/caching/MetaContexts.java | 6 +- .../luckperms/api/context/ContextSet.java | 86 ++++- .../api/context/ImmutableContextSet.java | 111 ++++--- .../api/context/MutableContextSet.java | 233 +++++++------- .../api/event/user/UserLoginProcessEvent.java | 72 +++++ .../exceptions/MembershipException.java | 12 +- .../exceptions/ObjectAlreadyHasException.java | 11 +- .../exceptions/ObjectLacksException.java | 11 +- bukkit-legacy/pom.xml | 2 +- bukkit/pom.xml | 2 +- .../luckperms/bukkit/BukkitListener.java | 3 +- .../calculators/BukkitCalculatorFactory.java | 7 +- .../luckperms/bukkit/model/LPPermissible.java | 9 +- .../luckperms/bukkit/vault/VaultChatHook.java | 4 +- .../bukkit/vault/VaultPermissionHook.java | 11 +- bungee/pom.xml | 2 +- .../luckperms/bungee/BungeeListener.java | 8 +- .../calculators/BungeeCalculatorFactory.java | 7 +- common/pom.xml | 2 +- .../luckperms/common/api/ApiHandler.java | 17 +- .../luckperms/common/api/ApiProvider.java | 2 +- .../common/caching/PermissionCache.java | 7 +- .../luckperms/common/caching/UserCache.java | 7 +- .../calculators/PermissionCalculator.java | 7 +- .../PermissionCalculatorMetadata.java | 47 +++ .../generic/permission/PermissionInfo.java | 4 +- .../commands/impl/group/GroupListMembers.java | 2 +- .../common/commands/impl/log/LogNotify.java | 2 +- .../commands/impl/misc/CheckCommand.java | 3 +- .../commands/impl/misc/SearchCommand.java | 2 +- .../commands/impl/misc/TreeCommand.java | 4 +- .../commands/impl/misc/VerboseCommand.java | 3 +- .../common/commands/impl/user/UserDemote.java | 2 +- .../commands/impl/user/UserPromote.java | 2 +- .../common/contexts/ContextSetComparator.java | 47 +-- .../luckperms/common/event/EventFactory.java | 66 ++-- .../event/impl/EventUserLoginProcess.java | 47 +++ .../luckperms/common/locale/CommandSpec.java | 3 +- .../luckperms/common/locale/Message.java | 2 +- .../common/managers/GenericUserManager.java | 128 ++++---- .../common/managers/UserManager.java | 2 +- .../common/model/PermissionHolder.java | 20 +- .../me/lucko/luckperms/common/model/User.java | 24 +- .../luckperms/common/node/ImmutableNode.java | 296 ++++++++---------- .../luckperms/common/node/NodeBuilder.java | 2 +- .../luckperms/common/node/NodeFactory.java | 6 +- .../common/node/NodeHeldPermission.java | 8 +- .../luckperms/common/node/NodeModel.java | 2 +- .../common/plugin/LuckPermsPlugin.java | 2 + .../primarygroup/ParentsByWeightHolder.java | 2 +- .../backing/mongodb/MongoDBBacking.java | 2 +- .../luckperms/common/treeview/TreeView.java | 7 +- .../luckperms/common/utils/LoginHelper.java | 4 +- .../luckperms/common/utils/TextUtils.java | 7 +- .../luckperms/common/verbose/CheckData.java | 16 + .../luckperms/common/verbose/CheckOrigin.java | 60 ++++ .../common/verbose/VerboseHandler.java | 10 +- .../common/verbose/VerboseListener.java | 135 +++++++- pom.xml | 4 +- sponge/pom.xml | 2 +- sponge/sponge-service-api6/pom.xml | 2 +- sponge/sponge-service-api7/pom.xml | 4 +- sponge/sponge-service/pom.xml | 4 +- .../luckperms/sponge/SpongeListener.java | 4 +- .../calculators/SpongeCalculatorFactory.java | 9 +- .../sponge/managers/SpongeUserManager.java | 3 +- .../luckperms/sponge/model/SpongeGroup.java | 2 +- .../luckperms/sponge/model/SpongeUser.java | 3 +- .../sponge/service/LuckPermsSubjectData.java | 4 +- .../calculated/CalculatedSubjectData.java | 6 +- .../service/persisted/PersistedSubject.java | 3 +- 89 files changed, 1389 insertions(+), 728 deletions(-) create mode 100644 api/src/main/java/me/lucko/luckperms/api/event/user/UserLoginProcessEvent.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculatorMetadata.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/event/impl/EventUserLoginProcess.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/verbose/CheckOrigin.java diff --git a/api/pom.xml b/api/pom.xml index 4d62b8898..8e17c0ebf 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT 4.0.0 diff --git a/api/src/main/java/me/lucko/luckperms/LuckPerms.java b/api/src/main/java/me/lucko/luckperms/LuckPerms.java index 71de6dc64..e6d71d66d 100644 --- a/api/src/main/java/me/lucko/luckperms/LuckPerms.java +++ b/api/src/main/java/me/lucko/luckperms/LuckPerms.java @@ -32,49 +32,59 @@ import java.util.Optional; import javax.annotation.Nonnull; /** - * Singleton for the {@link LuckPermsApi}. + * Provides static access to the {@link LuckPermsApi}. * - *

Ideally, the ServiceManager for the platform should be used to obtain and cache an instance, however, this can be - * used if you need static access.

+ *

Ideally, the ServiceManager for the platform should be used to obtain an instance, + * however, this provider can be used if you need static access.

*/ public final class LuckPerms { - private static LuckPermsApi api = null; + private static LuckPermsApi instance = null; /** - * Gets an instance of {@link LuckPermsApi}, throwing {@link IllegalStateException} if the API is not loaded. + * Gets an instance of the {@link LuckPermsApi}, + * throwing {@link IllegalStateException} if an instance is not yet loaded. + * + *

Will never return null.

* * @return an api instance * @throws IllegalStateException if the api is not loaded */ @Nonnull public static LuckPermsApi getApi() { - if (api == null) { + if (instance == null) { throw new IllegalStateException("API is not loaded."); } - return api; + return instance; } /** * Gets an instance of {@link LuckPermsApi}, if it is loaded. * - *

Unlike {@link LuckPerms#getApi}, this method will not throw an {@link IllegalStateException} if the API is - * not loaded, rather return an empty {@link Optional}. + *

Unlike {@link LuckPerms#getApi}, this method will not throw an + * {@link IllegalStateException} if an instance is not yet loaded, rather return + * an empty {@link Optional}. * * @return an optional api instance */ @Nonnull public static Optional getApiSafe() { - return Optional.ofNullable(api); + return Optional.ofNullable(instance); } - /* method used by the implementation to set the singleton instance */ - static void registerProvider(LuckPermsApi luckPermsApi) { - api = luckPermsApi; + /** + * Registers an instance of the {@link LuckPermsApi} with this provider. + * + * @param instance the instance + */ + static void registerProvider(LuckPermsApi instance) { + LuckPerms.instance = instance; } - /* method used by the implementation to remove any previous instance */ + /** + * Removes the current instance from this provider. + */ static void unregisterProvider() { - api = null; + LuckPerms.instance = null; } private LuckPerms() { diff --git a/api/src/main/java/me/lucko/luckperms/api/ChatMetaType.java b/api/src/main/java/me/lucko/luckperms/api/ChatMetaType.java index 6aca7ab99..27a55761a 100644 --- a/api/src/main/java/me/lucko/luckperms/api/ChatMetaType.java +++ b/api/src/main/java/me/lucko/luckperms/api/ChatMetaType.java @@ -28,6 +28,7 @@ package me.lucko.luckperms.api; import com.google.common.base.Preconditions; import java.util.Map; +import java.util.Optional; import javax.annotation.Nonnull; @@ -104,4 +105,22 @@ public enum ChatMetaType { @Nonnull public abstract Map.Entry getEntry(@Nonnull Node node); + /** + * Parses a ChatMetaType from the given node. + * + * @param node the node + * @return the parsed chat meta type + * @since 3.4 + */ + @Nonnull + public static Optional ofNode(@Nonnull Node node) { + if (node.isPrefix()) { + return Optional.of(PREFIX); + } else if (node.isSuffix()) { + return Optional.of(SUFFIX); + } else { + return Optional.empty(); + } + } + } diff --git a/api/src/main/java/me/lucko/luckperms/api/DataMutateResult.java b/api/src/main/java/me/lucko/luckperms/api/DataMutateResult.java index 5ff35bffc..a5b42114d 100644 --- a/api/src/main/java/me/lucko/luckperms/api/DataMutateResult.java +++ b/api/src/main/java/me/lucko/luckperms/api/DataMutateResult.java @@ -30,11 +30,29 @@ import me.lucko.luckperms.exceptions.ObjectLacksException; import java.util.function.Supplier; +/** + * Represents the result of a mutation call. + */ public enum DataMutateResult { + /** + * Indicates the mutation was a success + */ SUCCESS(true, null), + + /** + * Indicates the mutation failed because the subject already has something + */ ALREADY_HAS(false, ObjectAlreadyHasException::new), + + /** + * Indicates the mutation failed because the subject lacks something + */ LACKS(false, ObjectLacksException::new), + + /** + * Indicates the mutation failed + */ FAIL(false, RuntimeException::new); private boolean value; @@ -51,10 +69,35 @@ public enum DataMutateResult { } } + /** + * Gets a boolean representation of the result. + * + * @return a boolean representation + */ public boolean asBoolean() { return value; } + /** + * Gets if the result indicates a success + * + * @return if the result indicates a success + * @since 3.4 + */ + public boolean wasSuccess() { + return value; + } + + /** + * Gets if the result indicates a failure + * + * @return if the result indicates a failure + * @since 3.4 + */ + public boolean wasFailure() { + return !value; + } + // allows us to throw checked exceptions without declaring it, as #throwException throws a number of // exception types. private static void sneakyThrow(Throwable t) { diff --git a/api/src/main/java/me/lucko/luckperms/api/Group.java b/api/src/main/java/me/lucko/luckperms/api/Group.java index 90847c3d3..5d02b05b8 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Group.java +++ b/api/src/main/java/me/lucko/luckperms/api/Group.java @@ -35,7 +35,7 @@ import java.util.OptionalInt; import javax.annotation.Nonnull; /** - * A group which holds permission data. + * An inheritable holder of permission data. */ public interface Group extends PermissionHolder { diff --git a/api/src/main/java/me/lucko/luckperms/api/HeldPermission.java b/api/src/main/java/me/lucko/luckperms/api/HeldPermission.java index 13d8f446d..1f1705388 100644 --- a/api/src/main/java/me/lucko/luckperms/api/HeldPermission.java +++ b/api/src/main/java/me/lucko/luckperms/api/HeldPermission.java @@ -27,13 +27,15 @@ package me.lucko.luckperms.api; import com.google.common.collect.Multimap; +import me.lucko.luckperms.api.context.ContextSet; + import java.util.Optional; import java.util.OptionalLong; import javax.annotation.Nonnull; /** - * A relationship between a Holder and a permission + * A relationship between a PermissionHolder and a permission * * @param the identifier type of the holder * @since 2.17 @@ -90,10 +92,19 @@ public interface HeldPermission { * Gets the context for the permission. * * @return the context + * @deprecated in favour of {@link #getContexts()}. */ @Nonnull + @Deprecated Multimap getContext(); + /** + * Gets the extra context for the permission. + * + * @return the extra context + */ + ContextSet getContexts(); + /** * Converts this permission into a Node * diff --git a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java index 07876b54a..91748e690 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java +++ b/api/src/main/java/me/lucko/luckperms/api/LPConfiguration.java @@ -32,104 +32,119 @@ import java.util.Map; import javax.annotation.Nonnull; /** - * Read-only access to the LuckPerms configuration settings + * Wrapper around parts of the LuckPerms configuration file */ public interface LPConfiguration { /** - * Returns the name of this server + * Gets the name of this server + * * @return the name of this server */ @Nonnull String getServer(); /** - * Returns how often a sync task will run in minutes + * Gets how often a sync task will run in minutes + * * @return how often a sync task will run in minutes */ int getSyncTime(); /** - * Returns if the users on this server will have their global permissions applied + * Gets if the users on this server will have their global permissions applied + * * @return if the users on this server will have their global permissions applied */ boolean getIncludeGlobalPerms(); /** - * Returns if the users on this server will have their global world permissions applied + * Gets if the users on this server will have their global world permissions applied + * * @return if the users on this server will have their global world permissions applied * @since 2.9 */ boolean getIncludeGlobalWorldPerms(); /** - * Returns true if the platform is applying global groups + * Gets if the platform is applying global groups + * * @return true if the platform is applying global groups * @since 2.9 */ boolean getApplyGlobalGroups(); /** - * Returns true if the platform is applying global world groups + * Gets if the platform is applying global world groups + * * @return true if the platform is applying global world groups * @since 2.9 */ boolean getApplyGlobalWorldGroups(); /** - * Returns the online mode setting + * Gets the online mode setting + * * @return the online mode setting */ boolean getOnlineMode(); /** - * Returns if LuckPerms is applying wildcard permissions + * Gets if LuckPerms is applying wildcard permissions + * * @return if LuckPerms is applying wildcard permissions */ boolean getApplyWildcards(); /** * Returns if LuckPerms is resolving and applying regex permissions + * * @return if LuckPerms is resolving and applying regex permissions */ boolean getApplyRegex(); /** - * Returns if LuckPerms is expanding shorthand permissions + * Gets if LuckPerms is expanding shorthand permissions + * * @return if LuckPerms is expanding shorthand permissions */ boolean getApplyShorthand(); /** - * Returns if LuckPerms will send notifications to users when permissions are modified + * Gets if LuckPerms will send notifications to users when permissions are modified + * * @return if LuckPerms will send notifications to users when permissions are modified * @since 2.7 */ boolean getLogNotify(); /** - * Returns true if the vanilla op system is enabled + * Gets if the vanilla op system is enabled + * * @return true if the vanilla op system is enabled * @since 2.8 */ boolean getEnableOps(); /** - * Returns true if opped players are allowed to use LuckPerms commands + * Gets if opped players are allowed to use LuckPerms commands + * * @return true if opped players are allowed to use LuckPerms commands * @since 2.8 */ boolean getCommandsAllowOp(); /** - * Returns true if auto op is enabled + * Gets if auto op is enabled + * * @return true if auto op is enabled * @since 2.9 */ boolean getAutoOp(); /** - * Returns the name of the server used within Vault operations + * Gets the name of the server used within Vault operations + * * @return the name of the server used within Vault operations * @since 2.7 */ @@ -137,35 +152,40 @@ public interface LPConfiguration { String getVaultServer(); /** - * Returns true if global permissions should be considered when retrieving meta or player groups + * Gets if global permissions should be considered when retrieving meta or player groups + * * @return true if global permissions should be considered when retrieving meta or player groups * @since 2.7 */ boolean getVaultIncludeGlobal(); /** - * Returns the values set for data storage in the configuration + * Gets the values set for data storage in the configuration + * * @return the values set for data storage in the configuration */ @Nonnull DatastoreConfiguration getDatastoreConfig(); /** - * Returns the storage method string from the configuration + * Gets the storage method string from the configuration + * * @return the storage method string from the configuration */ @Nonnull String getStorageMethod(); /** - * Returns true if split storage is enabled + * Gets true if split storage is enabled + * * @return true if split storage is enabled * @since 2.7 */ boolean getSplitStorage(); /** - * Returns a map of split storage options + * Gets a map of split storage options + * * @return a map of split storage options, where the key is the storage section, and the value is the storage * method. For example: key = user, value = json * @since 2.7 diff --git a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java index dc2098815..5f641cadb 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java +++ b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java @@ -25,6 +25,7 @@ package me.lucko.luckperms.api; +import me.lucko.luckperms.LuckPerms; import me.lucko.luckperms.api.context.ContextCalculator; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.event.EventBus; @@ -38,7 +39,13 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * The root API interface for LuckPerms + * The LuckPerms API. + * + *

This interface is the base of the entire API package. All API functions + * are accessed via this interface.

+ * + *

An instance can be obtained via {@link LuckPerms#getApi()}, or the platforms + * Services Manager.

*/ public interface LuckPermsApi { diff --git a/api/src/main/java/me/lucko/luckperms/api/MetaUtils.java b/api/src/main/java/me/lucko/luckperms/api/MetaUtils.java index c73b79696..a3ef2876d 100644 --- a/api/src/main/java/me/lucko/luckperms/api/MetaUtils.java +++ b/api/src/main/java/me/lucko/luckperms/api/MetaUtils.java @@ -41,6 +41,7 @@ import java.util.Set; */ @Deprecated public class MetaUtils { + private static final String[] DELIMS = new String[]{".", "/", "-", "$"}; private static String escapeDelimiters(String s, String... delims) { for (String delim : delims) { @@ -68,7 +69,7 @@ public class MetaUtils { throw new NullPointerException(); } - return escapeDelimiters(s, ".", "/", "-", "$"); + return escapeDelimiters(s, DELIMS); } /** @@ -86,7 +87,7 @@ public class MetaUtils { s = s.replace("{SEP}", "."); s = s.replace("{FSEP}", "/"); s = s.replace("{DSEP}", "$"); - s = unescapeDelimiters(s, ".", "/", "-", "$"); + s = unescapeDelimiters(s, DELIMS); return s; } @@ -161,7 +162,7 @@ public class MetaUtils { node = escapeCharacters(node); for (Node n : holder.getPermissions()) { - if (!n.getValue() || !n.isMeta()) continue; + if (!n.getValuePrimitive() || !n.isMeta()) continue; if (!n.shouldApplyOnServer(server, includeGlobal, false)) continue; if (!n.shouldApplyOnWorld(world, includeGlobal, false)) continue; @@ -233,7 +234,7 @@ public class MetaUtils { int priority = Integer.MIN_VALUE; String meta = null; for (Node n : holder.getAllNodes(Contexts.allowAll())) { - if (!n.getValue()) continue; + if (!n.getValuePrimitive()) continue; if (!n.shouldApplyOnServer(server, includeGlobal, false)) continue; if (!n.shouldApplyOnWorld(world, includeGlobal, false)) continue; diff --git a/api/src/main/java/me/lucko/luckperms/api/Node.java b/api/src/main/java/me/lucko/luckperms/api/Node.java index eac4aa8c1..20f9b6872 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Node.java +++ b/api/src/main/java/me/lucko/luckperms/api/Node.java @@ -37,16 +37,18 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * An immutable permission node + * Represents a permission node. * - *

Use {@link LuckPermsApi#buildNode(String)} to get an instance.

+ *

All implementations of this interface must be immutable.

+ * + *

Use the {@link NodeFactory} to obtain and construct instances.

* * @since 2.6 */ public interface Node extends Map.Entry { /** - * Returns the actual permission string + * Gets the permission string * * @return the actual permission node */ @@ -54,36 +56,50 @@ public interface Node extends Map.Entry { String getPermission(); /** - * Gets what value the permission is set to. A negated node would return false. + * Gets the value. + * + *

A negated node would return a value of false.

* * @return the permission's value */ @Override @Nonnull - Boolean getValue(); + default Boolean getValue() { + return getValuePrimitive(); + } /** - * Returns the value of this node as a tristate + * Gets the value. + * + *

A negated node would return a value of false.

+ * + * @return the permission's value + */ + boolean getValuePrimitive(); + + /** + * Gets the value of this node as a {@link Tristate} * * @return the value of this node as a Tristate */ @Nonnull default Tristate getTristate() { - return Tristate.fromBoolean(getValue()); + return Tristate.fromBoolean(getValuePrimitive()); } /** - * Returns if the node is negated + * Gets if the node is negated * * @return true if the node is negated */ default boolean isNegated() { - return !getValue(); + return !getValuePrimitive(); } /** - * If this node is set to override explicitly. - * This value does not persist across saves, and is therefore only useful for transient nodes + * Gets if this node is set to override explicitly. + * + *

This value does not persist across saves, and is therefore only useful for transient nodes

* * @return true if this node is set to override explicitly */ @@ -106,21 +122,21 @@ public interface Node extends Map.Entry { Optional getWorld(); /** - * Returns if this node is server specific + * Gets if this node is server specific * * @return true if this node is server specific */ boolean isServerSpecific(); /** - * Returns if this node is server specific + * Gets if this node is server specific * * @return true if this node is server specific */ boolean isWorldSpecific(); /** - * Returns if this node applies globally, and has no specific context + * Gets if this node applies globally, and therefore has no specific context * * @return true if this node applies globally, and has no specific context * @since 3.1 @@ -128,7 +144,7 @@ public interface Node extends Map.Entry { boolean appliesGlobally(); /** - * Returns if this node has any specific context in order to apply. + * Gets if this node has any specific context in order for it to apply * * @return true if this node has specific context * @since 3.1 @@ -136,7 +152,7 @@ public interface Node extends Map.Entry { boolean hasSpecificContext(); /** - * Returns if this node is able to apply in the given context + * Gets if this node is able to apply in the given context * * @param includeGlobal if global server values should apply * @param includeGlobalWorld if global world values should apply @@ -150,7 +166,7 @@ public interface Node extends Map.Entry { boolean shouldApply(boolean includeGlobal, boolean includeGlobalWorld, @Nullable String server, @Nullable String world, @Nullable ContextSet context, boolean applyRegex); /** - * If this node should apply on a specific server + * Gets if this node should apply on a specific server * * @param server the name of the server * @param includeGlobal if global permissions should apply @@ -160,7 +176,7 @@ public interface Node extends Map.Entry { boolean shouldApplyOnServer(@Nullable String server, boolean includeGlobal, boolean applyRegex); /** - * If this node should apply on a specific world + * Gets if this node should apply on a specific world * * @param world the name of the world * @param includeGlobal if global permissions should apply @@ -170,7 +186,7 @@ public interface Node extends Map.Entry { boolean shouldApplyOnWorld(@Nullable String world, boolean includeGlobal, boolean applyRegex); /** - * If this node should apply in the given context + * Gets if this node should apply in the given context * * @param context the context key value pairs * @param worldAndServer if world and server contexts should be checked @@ -180,7 +196,7 @@ public interface Node extends Map.Entry { boolean shouldApplyWithContext(@Nonnull ContextSet context, boolean worldAndServer); /** - * If this node should apply in the given context + * Gets if this node should apply in the given context * * @param context the context key value pairs * @return true if the node should apply @@ -211,8 +227,10 @@ public interface Node extends Map.Entry { * * @param possibleNodes a list of possible permission nodes * @return a list of permissions that match this wildcard + * @deprecated as this is no longer used internally to resolve wildcards */ @Nonnull + @Deprecated List resolveWildcard(@Nonnull List possibleNodes); /** @@ -224,14 +242,14 @@ public interface Node extends Map.Entry { List resolveShorthand(); /** - * Returns if this node will expire in the future + * Gets if this node will expire in the future * * @return true if this node will expire in the future */ boolean isTemporary(); /** - * Returns if this node will not expire + * Gets if this node will not expire * * @return true if this node will not expire */ @@ -240,7 +258,7 @@ public interface Node extends Map.Entry { } /** - * Returns a unix timestamp in seconds when this node will expire + * Gets a unix timestamp in seconds when this node will expire * * @return the time in Unix time when this node will expire * @throws IllegalStateException if the node is not temporary @@ -248,7 +266,7 @@ public interface Node extends Map.Entry { long getExpiryUnixTime() throws IllegalStateException; /** - * Returns the date when this node will expire + * Gets the date when this node will expire * * @return the {@link Date} when this node will expire * @throws IllegalStateException if the node is not temporary @@ -257,7 +275,7 @@ public interface Node extends Map.Entry { Date getExpiry() throws IllegalStateException; /** - * Return the number of seconds until this permission will expire + * Gets the number of seconds until this permission will expire * * @return the number of seconds until this permission will expire * @throws IllegalStateException if the node is not temporary @@ -265,8 +283,9 @@ public interface Node extends Map.Entry { long getSecondsTilExpiry() throws IllegalStateException; /** - * Return true if the node has expired. - * This also returns false if the node is not temporary + * Gets if the node has expired. + * + *

This also returns false if the node is not temporary.

* * @return true if this node has expired */ @@ -301,14 +320,14 @@ public interface Node extends Map.Entry { String toSerializedNode(); /** - * Returns if this is a group node + * Gets if this is a group node * * @return true if this is a group node */ boolean isGroupNode(); /** - * Returns the name of the group + * Gets the name of the group, if this is a group node. * * @return the name of the group * @throws IllegalStateException if this is not a group node. See {@link #isGroupNode()} @@ -317,7 +336,7 @@ public interface Node extends Map.Entry { String getGroupName() throws IllegalStateException; /** - * Returns if this node is a wildcard node + * Gets if this node is a wildcard node * * @return true if this node is a wildcard node */ @@ -332,7 +351,7 @@ public interface Node extends Map.Entry { int getWildcardLevel() throws IllegalStateException; /** - * Returns if this node is a meta node + * Gets if this node is a meta node * * @return true if this node is a meta node */ @@ -348,7 +367,7 @@ public interface Node extends Map.Entry { Map.Entry getMeta() throws IllegalStateException; /** - * Returns if this node is a prefix node + * Gets if this node is a prefix node * * @return true if this node is a prefix node */ @@ -364,7 +383,7 @@ public interface Node extends Map.Entry { Map.Entry getPrefix() throws IllegalStateException; /** - * Returns if this node is a suffix node + * Gets if this node is a suffix node * * @return true if this node is a suffix node */ diff --git a/api/src/main/java/me/lucko/luckperms/api/NodeFactory.java b/api/src/main/java/me/lucko/luckperms/api/NodeFactory.java index 5c69a5af0..5164e0408 100644 --- a/api/src/main/java/me/lucko/luckperms/api/NodeFactory.java +++ b/api/src/main/java/me/lucko/luckperms/api/NodeFactory.java @@ -28,27 +28,12 @@ package me.lucko.luckperms.api; import javax.annotation.Nonnull; /** - * Builds {@link Node} instances + * Assists with constructing {@link Node} instances. * * @since 2.17 */ public interface NodeFactory { - /** - * Creates a node from a serialised node string - * - * @param serialisedPermission the serialised permission string - * @param value the value of the node - * @return a node instance - * @throws NullPointerException if the permission is null - * @deprecated since this format isn't used internally for permissions anymore - * @see Node#toSerializedNode() - */ - @Deprecated - @Nonnull - Node fromSerialisedNode(@Nonnull String serialisedPermission, boolean value); - - /** * Creates a new node builder from a given base permission string * @@ -69,20 +54,6 @@ public interface NodeFactory { @Nonnull Node.Builder newBuilderFromExisting(@Nonnull Node other); - /** - * Creates a node builder from a serialised node string - * - * @param serialisedPermission the serialised permission string - * @param value the value of the node - * @return a node builder instance - * @throws NullPointerException if the permission is null - * @deprecated since this format isn't used internally for permissions anymore - * @see Node#toSerializedNode() - */ - @Deprecated - @Nonnull - Node.Builder newBuilderFromSerialisedNode(@Nonnull String serialisedPermission, boolean value); - /** * Creates a node builder from a group @@ -141,4 +112,35 @@ public interface NodeFactory { @Nonnull Node.Builder makeSuffixNode(int priority, @Nonnull String suffix); + + /** + * Creates a node from a serialised node string + * + *

This format is what was previously used in YAML/JSON storage files.

+ * + * @param serialisedPermission the serialised permission string + * @param value the value of the node + * @return a node instance + * @throws NullPointerException if the permission is null + * @deprecated since this format isn't used internally for permissions anymore + * @see Node#toSerializedNode() + */ + @Deprecated + @Nonnull + Node fromSerialisedNode(@Nonnull String serialisedPermission, boolean value); + + /** + * Creates a node builder from a serialised node string + * + * @param serialisedPermission the serialised permission string + * @param value the value of the node + * @return a node builder instance + * @throws NullPointerException if the permission is null + * @deprecated since this format isn't used internally for permissions anymore + * @see Node#toSerializedNode() + */ + @Deprecated + @Nonnull + Node.Builder newBuilderFromSerialisedNode(@Nonnull String serialisedPermission, boolean value); + } diff --git a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java index 0d4f977f4..8b66c66de 100644 --- a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java +++ b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java @@ -43,14 +43,15 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * An object capable of holding permissions + * An object which holds permissions. * - *

Any changes made will be lost unless the instance is saved back to the {@link Storage}.

+ *

Any changes made to permission holding objects will be lost unless the + * instance is saved back to the {@link Storage}.

*/ public interface PermissionHolder { /** - * Gets the objects name + * Gets the objects name. * *

{@link User#getUuid()}, {@link User#getName()} or {@link Group#getName()} should normally be used instead of * this method.

@@ -123,7 +124,7 @@ public interface PermissionHolder { SortedSet getPermissions(); /** - * Similar to {@link #getPermissions()}, except without transient permissions + * Similar to {@link #getPermissions()}, except without transient permissions. * *

Unlike transient permissions, enduring permissions will be saved to storage, and exist after the session.

* @@ -134,7 +135,7 @@ public interface PermissionHolder { Set getEnduringPermissions(); /** - * Similar to {@link #getPermissions()}, except without enduring permissions + * Similar to {@link #getPermissions()}, except without enduring permissions. * *

Transient permissions only exist for the duration of the session.

* diff --git a/api/src/main/java/me/lucko/luckperms/api/PlatformType.java b/api/src/main/java/me/lucko/luckperms/api/PlatformType.java index 5d74d08aa..3a9f1dd23 100644 --- a/api/src/main/java/me/lucko/luckperms/api/PlatformType.java +++ b/api/src/main/java/me/lucko/luckperms/api/PlatformType.java @@ -28,7 +28,7 @@ package me.lucko.luckperms.api; import javax.annotation.Nonnull; /** - * The platforms which LuckPerms can run on + * Represents a type of platform which LuckPerms can run on. * * @since 2.7 */ @@ -44,6 +44,11 @@ public enum PlatformType { this.friendlyName = friendlyName; } + /** + * Gets a readable name for the platform type. + * + * @return a readable name + */ @Nonnull public String getFriendlyName() { return friendlyName; diff --git a/api/src/main/java/me/lucko/luckperms/api/Storage.java b/api/src/main/java/me/lucko/luckperms/api/Storage.java index 1bdb6ff41..2b8cd6fe2 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Storage.java +++ b/api/src/main/java/me/lucko/luckperms/api/Storage.java @@ -36,14 +36,14 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * A means of loading and saving data to/from the Storage provider. + * A means of loading and saving permission data to/from the backend. * - *

All methods return {@link CompletableFuture}s, which will be populated with the result once the data has been - * loaded asynchronously. Care should be taken when using the methods to ensure that the main server thread is not + *

All blocking methods return {@link CompletableFuture}s, which will be populated with the result once the data has been + * loaded/saved asynchronously. Care should be taken when using such methods to ensure that the main server thread is not * blocked.

* *

Methods such as {@link CompletableFuture#get()} and equivalent should not be called on the main - * server thread. If you need to use the result of these operations on the main server thread, please register a + * server thread. If you need to use the result of these operations on the main server thread, register a * callback using {@link CompletableFuture#thenAcceptAsync(Consumer, Executor)} and {@link #getSyncExecutor()}.

* * @since 2.14 @@ -59,7 +59,7 @@ public interface Storage { String getName(); /** - * Return whether the storage instance is allowing logins on the platform. + * Gets whether the storage instance is allowing logins on the platform. * * @return true if logins are enabled */ @@ -144,7 +144,8 @@ public interface Storage { /** * Gets a set all "unique" user UUIDs. - * "Unique" meaning the user isn't just a member of the "default" group. + * + *

"Unique" meaning the user isn't just a member of the "default" group.

* * @return a set of uuids, or null if the operation failed. */ diff --git a/api/src/main/java/me/lucko/luckperms/api/Track.java b/api/src/main/java/me/lucko/luckperms/api/Track.java index a6f9ddf6a..aa26ecb60 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Track.java +++ b/api/src/main/java/me/lucko/luckperms/api/Track.java @@ -34,21 +34,24 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * An ordered collection of groups for easy promotions and demotions + * An ordered chain of {@link Group}s. */ public interface Track { /** * Gets the name of this track + * * @return the name of this track */ @Nonnull String getName(); /** - * Gets an ordered list of the groups on this track + * Gets a list of the groups on this track * - *

Index 0 is the first/lowest group in (or start of) the track

+ *

Index 0 is the first/lowest group in (or start of) the track.

+ * + *

The returned collection is immutable, and cannot be modified.

* * @return an ordered {@link List} of the groups on this track */ diff --git a/api/src/main/java/me/lucko/luckperms/api/Tristate.java b/api/src/main/java/me/lucko/luckperms/api/Tristate.java index 587b1c5ef..34016e6f7 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Tristate.java +++ b/api/src/main/java/me/lucko/luckperms/api/Tristate.java @@ -28,37 +28,42 @@ package me.lucko.luckperms.api; import javax.annotation.Nonnull; /** - * Represents a permission setting. + * Represents three different states of a setting. * - *

Consider a value of {@link #TRUE} to be a positive setting, {@link #FALSE} to be a "negated" setting, - * and a value of {@link #UNDEFINED} to be a non-existent value.

+ *

Possible values:

+ *

+ *
    + *
  • {@link #TRUE} - a positive setting
  • + *
  • {@link #FALSE} - a negative (negated) setting
  • + *
  • {@link #UNDEFINED} - a non-existent setting
  • + *
*/ public enum Tristate { /** - * A value indicating a holder has a permission set. + * A value indicating a positive setting */ TRUE(true), /** - * A value indicating a holder has a negated value for a permission. + * A value indicating a negative (negated) setting */ FALSE(false), /** - * A value indicating a holder doesn't have a value for a permission set. + * A value indicating a non-existent setting */ UNDEFINED(false); /** - * Converts from {@link Boolean} a boolean + * Returns a {@link Tristate} from a boolean * - * @param b the boolean + * @param val the boolean value * @return {@link #TRUE} or {@link #FALSE}, depending on the value of the boolean. */ @Nonnull - public static Tristate fromBoolean(boolean b) { - return b ? TRUE : FALSE; + public static Tristate fromBoolean(boolean val) { + return val ? TRUE : FALSE; } private final boolean booleanValue; diff --git a/api/src/main/java/me/lucko/luckperms/api/User.java b/api/src/main/java/me/lucko/luckperms/api/User.java index 752003cfa..bf4c1d663 100644 --- a/api/src/main/java/me/lucko/luckperms/api/User.java +++ b/api/src/main/java/me/lucko/luckperms/api/User.java @@ -38,7 +38,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * A player holding permission data + * A player which holds permission data. */ public interface User extends PermissionHolder { @@ -51,7 +51,9 @@ public interface User extends PermissionHolder { UUID getUuid(); /** - * Gets the users username, or null if no username is associated with this user + * Gets the users username + * + *

Returns null if no username is associated with this user.

* * @return the users username */ @@ -96,6 +98,16 @@ public interface User extends PermissionHolder { @Nonnull UserData getCachedData(); + /** + * Pre-calculates some values in the user's data cache. + * + *

Is it not necessary to call this method before + * using {@link #getCachedData()}.

+ * + * @since 2.17 + */ + void setupDataCache(); + /** * Check to see if the user is a direct member of a group * @@ -128,16 +140,6 @@ public interface User extends PermissionHolder { @Nonnull Optional getUserDataCache(); - /** - * Pre-calculates some values in the user's data cache. - * - *

Is it not necessary to call this method before - * using {@link #getCachedData()}.

- * - * @since 2.17 - */ - void setupDataCache(); - /** * Check to see if a user is a member of a group on a specific server * diff --git a/api/src/main/java/me/lucko/luckperms/api/UuidCache.java b/api/src/main/java/me/lucko/luckperms/api/UuidCache.java index d792c517f..d637b276b 100644 --- a/api/src/main/java/me/lucko/luckperms/api/UuidCache.java +++ b/api/src/main/java/me/lucko/luckperms/api/UuidCache.java @@ -32,34 +32,34 @@ import javax.annotation.Nonnull; /** * A UUID cache for online users, between external Mojang UUIDs, and internal LuckPerms UUIDs. * - *

This UuidCache is a means of allowing users to have the same internal UUID across a network of offline mode - * servers or mixed offline mode and online mode servers. Platforms running in offline mode generate a UUID for a - * user when they first join the server, but this UUID will then not be consistent across the network. LuckPerms will - * instead check the datastore cache, to get a UUID for a user that is consistent across an entire network.

+ *

A user's internal LuckPerms UUID is always the same as their Mojang one, + * unless use-server-uuids is disabled.

* - *

If you want to get a user object from the Storage using the api on a server in offline mode, you will need to use - * this cache, OR use Storage#getUUID, for users that are not online.

+ *

When this setting is disabled, this cache becomes active, and allows you to convert + * between 'internal' and 'server provided' uuids.

* *

This is only effective for online players. Use {@link Storage#getUUID(String)} for offline players.

*/ public interface UuidCache { /** - * Gets a users internal "LuckPerms" UUID, from the one given by the server. + * Gets a users "internal" LuckPerms UUID, from the one given by the server. * - * @param external the UUID assigned by the server, through Player#getUniqueId or ProxiedPlayer#getUniqueId + *

When use-server-uuids is true, this returns the same UUID instance.

+ * + * @param mojangUuid the UUID assigned by the server, through Player#getUniqueId or ProxiedPlayer#getUniqueId * @return the corresponding internal UUID */ @Nonnull - UUID getUUID(@Nonnull UUID external); + UUID getUUID(@Nonnull UUID mojangUuid); /** - * Gets a users external, server assigned or Mojang assigned unique id, from the internal one used within LuckPerms. + * Gets a users "external", server assigned unique id, from the internal one used within LuckPerms. * - * @param internal the UUID used within LuckPerms, through User#getUuid + * @param internalUuid the UUID used within LuckPerms, through User#getUuid * @return the corresponding external UUID */ @Nonnull - UUID getExternalUUID(@Nonnull UUID internal); + UUID getExternalUUID(@Nonnull UUID internalUuid); } diff --git a/api/src/main/java/me/lucko/luckperms/api/caching/MetaContexts.java b/api/src/main/java/me/lucko/luckperms/api/caching/MetaContexts.java index baf2f3faf..9538ec975 100644 --- a/api/src/main/java/me/lucko/luckperms/api/caching/MetaContexts.java +++ b/api/src/main/java/me/lucko/luckperms/api/caching/MetaContexts.java @@ -33,8 +33,10 @@ import me.lucko.luckperms.api.metastacking.MetaStackDefinition; import javax.annotation.Nonnull; /** - * Represents the context for a meta lookup, consisting of a standard {@link Contexts} element, plus options to define how - * the meta stack should be constructed. + * Represents the context for a meta lookup. + * + *

Consisting of a standard {@link Contexts} element, plus options to define how + * the meta stack should be constructed.

* * @since 3.2 */ diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java index 1b8250648..0435be75a 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java @@ -35,7 +35,13 @@ import java.util.Set; import javax.annotation.Nonnull; /** - * Holder of contexts. + * A set of context pairs. + * + *

You can think of ContextSets as a wrapped Multimap<String, String>. + * Each key can be mapped to multiple values.

+ * + *

Keys are automatically converted to lowercase when added, and are therefore + * case-insensitive. Values however are not.

* *

Implementations may be either mutable or immutable.

* @@ -72,18 +78,6 @@ public interface ContextSet { return ImmutableContextSet.of(key1, value1, key2, value2); } - /** - * Creates an ImmutableContextSet from an existing map - * - * @param map the map to copy from - * @return a new ImmutableContextSet representing the pairs from the map - * @throws NullPointerException if the map is null - */ - @Nonnull - static ImmutableContextSet fromMap(@Nonnull Map map) { - return ImmutableContextSet.fromMap(map); - } - /** * Creates an ImmutableContextSet from an existing iterable of Map Entries * @@ -96,6 +90,18 @@ public interface ContextSet { return ImmutableContextSet.fromEntries(iterable); } + /** + * Creates an ImmutableContextSet from an existing map + * + * @param map the map to copy from + * @return a new ImmutableContextSet representing the pairs from the map + * @throws NullPointerException if the map is null + */ + @Nonnull + static ImmutableContextSet fromMap(@Nonnull Map map) { + return ImmutableContextSet.fromMap(map); + } + /** * Creates an ImmutableContextSet from an existing multimap * @@ -133,7 +139,7 @@ public interface ContextSet { } /** - * Check to see if this set is in an immutable form + * Gets if this set is in an immutable form * * @return true if the set is immutable */ @@ -167,12 +173,14 @@ public interface ContextSet { /** * Converts this ContextSet to an immutable {@link Map} * - * NOTE: Use of this method may result in data being lost. ContextSets can contain lots of different values for + * IMPORTANT: Use of this method may result in data being lost. ContextSets can contain lots of different values for * one key. * * @return an immutable map + * @deprecated because the resultant map may not contain all data in the ContextSet */ @Nonnull + @Deprecated Map toMap(); /** @@ -225,6 +233,19 @@ public interface ContextSet { */ boolean has(@Nonnull String key, @Nonnull String value); + /** + * Check if thr set contains a given key mapped to a given value + * + * @param entry the entry to look for + * @return true if the set contains the KV pair + * @throws NullPointerException if the key or value is null + * @since 3.4 + */ + default boolean has(@Nonnull Map.Entry entry) { + Preconditions.checkNotNull(entry, "entry"); + return has(entry.getKey(), entry.getValue()); + } + /** * Same as {@link #has(String, String)}, except ignores the case of the value. * @@ -235,6 +256,19 @@ public interface ContextSet { */ boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value); + /** + * Same as {@link #has(Map.Entry)}, except ignores the case of the value. + * + * @param entry the entry to look for + * @return true if the set contains the KV pair + * @throws NullPointerException if the key or value is null + * @since 3.4 + */ + default boolean hasIgnoreCase(@Nonnull Map.Entry entry) { + Preconditions.checkNotNull(entry, "entry"); + return hasIgnoreCase(entry.getKey(), entry.getValue()); + } + /** * Checks to see if all entries in this context set are also included in another set. * @@ -243,6 +277,18 @@ public interface ContextSet { * @since 3.1 */ default boolean isSatisfiedBy(@Nonnull ContextSet other) { + return isSatisfiedBy(other, true); + } + + /** + * Checks to see if all entries in this context set are also included in another set. + * + * @param other the other set to check + * @param caseSensitive if the lookup should be case sensitive. see {@link #has(Map.Entry)} and {@link #hasIgnoreCase(Map.Entry)}. + * @return true if all entries in this set are also in the other set + * @since 3.4 + */ + default boolean isSatisfiedBy(@Nonnull ContextSet other, boolean caseSensitive) { Preconditions.checkNotNull(other, "other"); if (this.isEmpty()) { // this is empty, so is therefore always satisfied. @@ -256,8 +302,14 @@ public interface ContextSet { } else { // neither are empty, we need to compare the individual entries for (Map.Entry pair : toSet()) { - if (!other.has(pair.getKey(), pair.getValue())) { - return false; + if (caseSensitive) { + if (!other.has(pair)) { + return false; + } + } else { + if (!other.hasIgnoreCase(pair)) { + return false; + } } } diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java index 2ce13b72b..8344daf58 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java @@ -25,12 +25,10 @@ package me.lucko.luckperms.api.context; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Multimap; -import com.google.common.collect.SetMultimap; import java.util.Collection; import java.util.Map; @@ -38,9 +36,14 @@ import java.util.Set; import javax.annotation.Nonnull; +import static com.google.common.base.Preconditions.checkNotNull; + /** * An immutable implementation of {@link ContextSet}. * + *

On construction, all keys/values are {@link String#intern()}ed, in order to increase + * comparison speed.

+ * * @since 2.16 */ public final class ImmutableContextSet implements ContextSet { @@ -56,9 +59,10 @@ public final class ImmutableContextSet implements ContextSet { */ @Nonnull public static ImmutableContextSet singleton(@Nonnull String key, @Nonnull String value) { - Preconditions.checkNotNull(key, "key"); - Preconditions.checkNotNull(value, "value"); - return new ImmutableContextSet(ImmutableSetMultimap.of(key.toLowerCase(), value)); + return new ImmutableContextSet(ImmutableSetMultimap.of( + checkNotNull(key, "key").toLowerCase().intern(), + checkNotNull(value, "value").intern() + )); } /** @@ -74,30 +78,12 @@ public final class ImmutableContextSet implements ContextSet { */ @Nonnull public static ImmutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) { - Preconditions.checkNotNull(key1, "key1"); - Preconditions.checkNotNull(value1, "value1"); - Preconditions.checkNotNull(key2, "key2"); - Preconditions.checkNotNull(value2, "value2"); - return new ImmutableContextSet(ImmutableSetMultimap.of(key1.toLowerCase(), value1, key2.toLowerCase(), value2)); - } - - /** - * Creates an ImmutableContextSet from an existing map - * - * @param map the map to copy from - * @return a new ImmutableContextSet representing the pairs from the map - * @throws NullPointerException if the map is null - */ - @Nonnull - public static ImmutableContextSet fromMap(@Nonnull Map map) { - Preconditions.checkNotNull(map, "map"); - - ImmutableSetMultimap.Builder b = ImmutableSetMultimap.builder(); - for (Map.Entry e : map.entrySet()) { - b.put(e.getKey().toLowerCase(), e.getValue()); - } - - return new ImmutableContextSet(b.build()); + return new ImmutableContextSet(ImmutableSetMultimap.of( + checkNotNull(key1, "key1").toLowerCase().intern(), + checkNotNull(value1, "value1").intern(), + checkNotNull(key2, "key2").toLowerCase().intern(), + checkNotNull(value2, "value2").intern() + )); } /** @@ -109,8 +95,26 @@ public final class ImmutableContextSet implements ContextSet { */ @Nonnull public static ImmutableContextSet fromEntries(@Nonnull Iterable> iterable) { - Preconditions.checkNotNull(iterable, "iterable"); - return MutableContextSet.fromEntries(iterable).makeImmutable(); + checkNotNull(iterable, "iterable"); + + ImmutableSetMultimap.Builder b = ImmutableSetMultimap.builder(); + for (Map.Entry e : iterable) { + b.put(checkNotNull(e.getKey()).toLowerCase().intern(), checkNotNull(e.getValue()).intern()); + } + + return new ImmutableContextSet(b.build()); + } + + /** + * Creates an ImmutableContextSet from an existing map + * + * @param map the map to copy from + * @return a new ImmutableContextSet representing the pairs from the map + * @throws NullPointerException if the map is null + */ + @Nonnull + public static ImmutableContextSet fromMap(@Nonnull Map map) { + return fromEntries(checkNotNull(map, "map").entrySet()); } /** @@ -122,8 +126,7 @@ public final class ImmutableContextSet implements ContextSet { */ @Nonnull public static ImmutableContextSet fromMultimap(@Nonnull Multimap multimap) { - Preconditions.checkNotNull(multimap, "multimap"); - return MutableContextSet.fromMultimap(multimap).makeImmutable(); + return fromEntries(checkNotNull(multimap, "multimap").entries()); } /** @@ -136,7 +139,7 @@ public final class ImmutableContextSet implements ContextSet { */ @Nonnull public static ImmutableContextSet fromSet(@Nonnull ContextSet contextSet) { - return Preconditions.checkNotNull(contextSet, "contextSet").makeImmutable(); + return checkNotNull(contextSet, "contextSet").makeImmutable(); } /** @@ -149,10 +152,10 @@ public final class ImmutableContextSet implements ContextSet { return EMPTY; } - private final SetMultimap map; + private final ImmutableSetMultimap map; - ImmutableContextSet(Multimap contexts) { - this.map = ImmutableSetMultimap.copyOf(contexts); + ImmutableContextSet(ImmutableSetMultimap contexts) { + this.map = contexts; } @Override @@ -176,7 +179,7 @@ public final class ImmutableContextSet implements ContextSet { @Override @Nonnull public Set> toSet() { - return ImmutableSet.copyOf(map.entries()); + return map.entries(); } @Override @@ -197,36 +200,35 @@ public final class ImmutableContextSet implements ContextSet { } @Override + @Nonnull public boolean containsKey(@Nonnull String key) { - Preconditions.checkNotNull(key, "key"); - return map.containsKey(key); + return map.containsKey(checkNotNull(key, "key").toLowerCase().intern()); } @Override + @Nonnull public Set getValues(@Nonnull String key) { - Preconditions.checkNotNull(key, "key"); - Collection c = map.get(key); - return c != null && !c.isEmpty() ? ImmutableSet.copyOf(c) : ImmutableSet.of(); + Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); + return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); } @Override + @Nonnull public boolean has(@Nonnull String key, @Nonnull String value) { - Preconditions.checkNotNull(key, "key"); - Preconditions.checkNotNull(value, "value"); - return map.containsEntry(key, value); + return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern()); } @Override + @Nonnull public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) { - Preconditions.checkNotNull(key, "key"); - Preconditions.checkNotNull(value, "value"); + value = checkNotNull(value, "value").intern(); + Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); - Collection c = map.get(key); - if (c == null || c.isEmpty()) { + if (values == null || values.isEmpty()) { return false; } - for (String val : c) { + for (String val : values) { if (val.equalsIgnoreCase(value)) { return true; } @@ -250,6 +252,11 @@ public final class ImmutableContextSet implements ContextSet { if (!(o instanceof ContextSet)) return false; final ContextSet other = (ContextSet) o; + // saves on copying the multimap + if (other instanceof MutableContextSet) { + return other.equals(this); + } + final Multimap thisContexts = this.toMultimap(); final Multimap otherContexts = other.toMultimap(); return thisContexts.equals(otherContexts); @@ -257,7 +264,7 @@ public final class ImmutableContextSet implements ContextSet { @Override public int hashCode() { - return 59 + (this.map == null ? 43 : this.map.hashCode()); + return map.hashCode(); } @Override diff --git a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java index 544f56bfb..5d39ffaef 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java @@ -40,9 +40,14 @@ import java.util.Set; import javax.annotation.Nonnull; +import static com.google.common.base.Preconditions.checkNotNull; + /** * A mutable implementation of {@link ContextSet}. * + *

On construction, all keys/values are {@link String#intern()}ed, in order to increase + * comparison speed.

+ * * @since 2.16 */ public final class MutableContextSet implements ContextSet { @@ -57,9 +62,9 @@ public final class MutableContextSet implements ContextSet { */ @Nonnull public static MutableContextSet singleton(@Nonnull String key, @Nonnull String value) { - Preconditions.checkNotNull(key, "key"); - Preconditions.checkNotNull(value, "value"); - MutableContextSet set = new MutableContextSet(); + checkNotNull(key, "key"); + checkNotNull(value, "value"); + MutableContextSet set = MutableContextSet.create(); set.add(key, value); return set; } @@ -77,27 +82,13 @@ public final class MutableContextSet implements ContextSet { */ @Nonnull public static MutableContextSet of(@Nonnull String key1, @Nonnull String value1, @Nonnull String key2, @Nonnull String value2) { - Preconditions.checkNotNull(key1, "key1"); - Preconditions.checkNotNull(value1, "value1"); - Preconditions.checkNotNull(key2, "key2"); - Preconditions.checkNotNull(value2, "value2"); - MutableContextSet ret = singleton(key1, value1); - ret.add(key2, value2); - return ret; - } - - /** - * Creates a MutableContextSet from an existing map - * - * @param map the map to copy from - * @return a new MutableContextSet representing the pairs from the map - * @throws NullPointerException if the map is null - */ - @Nonnull - public static MutableContextSet fromMap(@Nonnull Map map) { - Preconditions.checkNotNull(map, "map"); - MutableContextSet set = new MutableContextSet(); - set.addAll(map); + checkNotNull(key1, "key1"); + checkNotNull(value1, "value1"); + checkNotNull(key2, "key2"); + checkNotNull(value2, "value2"); + MutableContextSet set = MutableContextSet.create(); + set.add(key1, value1); + set.add(key2, value2); return set; } @@ -110,12 +101,27 @@ public final class MutableContextSet implements ContextSet { */ @Nonnull public static MutableContextSet fromEntries(@Nonnull Iterable> iterable) { - Preconditions.checkNotNull(iterable, "iterable"); - MutableContextSet set = new MutableContextSet(); + checkNotNull(iterable, "iterable"); + MutableContextSet set = MutableContextSet.create(); set.addAll(iterable); return set; } + /** + * Creates a MutableContextSet from an existing map + * + * @param map the map to copy from + * @return a new MutableContextSet representing the pairs from the map + * @throws NullPointerException if the map is null + */ + @Nonnull + public static MutableContextSet fromMap(@Nonnull Map map) { + checkNotNull(map, "map"); + MutableContextSet set = MutableContextSet.create(); + set.addAll(map); + return set; + } + /** * Creates a MutableContextSet from an existing multimap * @@ -125,8 +131,10 @@ public final class MutableContextSet implements ContextSet { */ @Nonnull public static MutableContextSet fromMultimap(@Nonnull Multimap multimap) { - Preconditions.checkNotNull(multimap, "multimap"); - return fromEntries(multimap.entries()); + checkNotNull(multimap, "multimap"); + MutableContextSet set = MutableContextSet.create(); + set.addAll(multimap); + return set; } /** @@ -161,8 +169,8 @@ public final class MutableContextSet implements ContextSet { this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create()); } - private MutableContextSet(Multimap contexts) { - this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(contexts)); + private MutableContextSet(MutableContextSet other) { + this.map = Multimaps.synchronizedSetMultimap(HashMultimap.create(other.map)); } @Override @@ -170,21 +178,25 @@ public final class MutableContextSet implements ContextSet { return false; } + @Nonnull @Override public ImmutableContextSet makeImmutable() { - return new ImmutableContextSet(map); + return new ImmutableContextSet(ImmutableSetMultimap.copyOf(map)); } + @Nonnull @Override public MutableContextSet mutableCopy() { - return new MutableContextSet(map); + return new MutableContextSet(this); } + @Nonnull @Override public Set> toSet() { return ImmutableSet.copyOf(map.entries()); } + @Nonnull @Override public Map toMap() { ImmutableMap.Builder m = ImmutableMap.builder(); @@ -195,57 +207,42 @@ public final class MutableContextSet implements ContextSet { return m.build(); } + @Nonnull @Override public Multimap toMultimap() { return ImmutableSetMultimap.copyOf(map); } + @Nonnull @Override - public boolean containsKey(String key) { - if (key == null) { - throw new NullPointerException("key"); - } - - return map.containsKey(key); + public boolean containsKey(@Nonnull String key) { + return map.containsKey(checkNotNull(key, "key").toLowerCase().intern()); } + @Nonnull @Override - public Set getValues(String key) { - if (key == null) { - throw new NullPointerException("key"); - } - - Collection c = map.get(key); - return c != null && !c.isEmpty() ? ImmutableSet.copyOf(c) : ImmutableSet.of(); + public Set getValues(@Nonnull String key) { + Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); + return values != null ? ImmutableSet.copyOf(values) : ImmutableSet.of(); } + @Nonnull @Override - public boolean has(String key, String value) { - if (key == null) { - throw new NullPointerException("key"); - } - if (value == null) { - throw new NullPointerException("value"); - } - - return map.containsEntry(key, value); + public boolean has(@Nonnull String key, @Nonnull String value) { + return map.containsEntry(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern()); } + @Nonnull @Override - public boolean hasIgnoreCase(String key, String value) { - if (key == null) { - throw new NullPointerException("key"); - } - if (value == null) { - throw new NullPointerException("value"); - } + public boolean hasIgnoreCase(@Nonnull String key, @Nonnull String value) { + value = checkNotNull(value, "value").intern(); + Collection values = map.get(checkNotNull(key, "key").toLowerCase().intern()); - Collection c = map.get(key); - if (c == null || c.isEmpty()) { + if (values == null || values.isEmpty()) { return false; } - for (String val : c) { + for (String val : values) { if (val.equalsIgnoreCase(value)) { return true; } @@ -270,15 +267,8 @@ public final class MutableContextSet implements ContextSet { * @param value the value to add * @throws NullPointerException if the key or value is null */ - public void add(String key, String value) { - if (key == null) { - throw new NullPointerException("key"); - } - if (value == null) { - throw new NullPointerException("value"); - } - - map.put(key.toLowerCase(), value); + public void add(@Nonnull String key, @Nonnull String value) { + map.put(checkNotNull(key, "key").toLowerCase().intern(), checkNotNull(value, "value").intern()); } /** @@ -287,12 +277,9 @@ public final class MutableContextSet implements ContextSet { * @param entry the entry to add * @throws NullPointerException if the entry is null */ - public void add(Map.Entry entry) { - if (entry == null) { - throw new NullPointerException("context"); - } - - map.put(entry.getKey().toLowerCase(), entry.getValue()); + public void add(@Nonnull Map.Entry entry) { + checkNotNull(entry, "entry"); + add(entry.getKey(), entry.getValue()); } /** @@ -301,13 +288,9 @@ public final class MutableContextSet implements ContextSet { * @param iterable an iterable of key value context pairs * @throws NullPointerException if iterable is null */ - public void addAll(Iterable> iterable) { - if (iterable == null) { - throw new NullPointerException("iterable"); - } - - for (Map.Entry e : iterable) { - this.map.put(e.getKey().toLowerCase(), e.getValue()); + public void addAll(@Nonnull Iterable> iterable) { + for (Map.Entry e : checkNotNull(iterable, "iterable")) { + add(e); } } @@ -317,11 +300,19 @@ public final class MutableContextSet implements ContextSet { * @param map the map to add from * @throws NullPointerException if the map is null */ - public void addAll(Map map) { - if (map == null) { - throw new NullPointerException("map"); - } - addAll(map.entrySet()); + public void addAll(@Nonnull Map map) { + addAll(checkNotNull(map, "map").entrySet()); + } + + /** + * Adds the entries of a multimap to the set + * + * @param multimap the multimap to add from + * @throws NullPointerException if the map is null + * @since 3.4 + */ + public void addAll(@Nonnull Multimap multimap) { + addAll(checkNotNull(multimap, "multimap").entries()); } /** @@ -330,12 +321,14 @@ public final class MutableContextSet implements ContextSet { * @param contextSet the set to add from * @throws NullPointerException if the contextSet is null */ - public void addAll(ContextSet contextSet) { - if (contextSet == null) { - throw new NullPointerException("contextSet"); + public void addAll(@Nonnull ContextSet contextSet) { + checkNotNull(contextSet, "contextSet"); + if (contextSet instanceof MutableContextSet) { + MutableContextSet other = ((MutableContextSet) contextSet); + this.map.putAll(other.map); + } else { + addAll(contextSet.toMultimap()); } - - this.map.putAll(contextSet.toMultimap()); } /** @@ -345,15 +338,12 @@ public final class MutableContextSet implements ContextSet { * @param value the value to remove (case sensitive) * @throws NullPointerException if the key or value is null */ - public void remove(String key, String value) { - if (key == null) { - throw new NullPointerException("key"); - } - if (value == null) { - throw new NullPointerException("value"); - } + public void remove(@Nonnull String key, @Nonnull String value) { + String k = checkNotNull(key, "key").toLowerCase().intern(); + String v = checkNotNull(value, "value").intern(); - map.entries().removeIf(entry -> entry.getKey().equals(key) && entry.getValue().equals(value)); + //noinspection StringEquality + map.entries().removeIf(entry -> entry.getKey() == k && entry.getValue() == v); } /** @@ -363,15 +353,12 @@ public final class MutableContextSet implements ContextSet { * @param value the value to remove * @throws NullPointerException if the key or value is null */ - public void removeIgnoreCase(String key, String value) { - if (key == null) { - throw new NullPointerException("key"); - } - if (value == null) { - throw new NullPointerException("value"); - } + public void removeIgnoreCase(@Nonnull String key, @Nonnull String value) { + String k = checkNotNull(key, "key").toLowerCase().intern(); + String v = checkNotNull(value, "value").intern(); - map.entries().removeIf(e -> e.getKey().equalsIgnoreCase(key) && e.getValue().equalsIgnoreCase(value)); + //noinspection StringEquality + map.entries().removeIf(e -> e.getKey() == k && e.getValue().equalsIgnoreCase(v)); } /** @@ -380,12 +367,8 @@ public final class MutableContextSet implements ContextSet { * @param key the key to remove * @throws NullPointerException if the key is null */ - public void removeAll(String key) { - if (key == null) { - throw new NullPointerException("key"); - } - - map.removeAll(key.toLowerCase()); + public void removeAll(@Nonnull String key) { + map.removeAll(checkNotNull(key, "key").toLowerCase()); } /** @@ -401,14 +384,20 @@ public final class MutableContextSet implements ContextSet { if (!(o instanceof ContextSet)) return false; final ContextSet other = (ContextSet) o; - final Multimap thisContexts = this.toMultimap(); - final Multimap otherContexts = other.toMultimap(); - return thisContexts == null ? otherContexts == null : thisContexts.equals(otherContexts); + final Multimap otherContexts; + + if (other instanceof MutableContextSet) { + otherContexts = ((MutableContextSet) other).map; + } else { + otherContexts = other.toMultimap(); + } + + return this.map.equals(otherContexts); } @Override public int hashCode() { - return 59 + (this.map == null ? 43 : this.map.hashCode()); + return map.hashCode(); } @Override diff --git a/api/src/main/java/me/lucko/luckperms/api/event/user/UserLoginProcessEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/user/UserLoginProcessEvent.java new file mode 100644 index 000000000..739e809ee --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/event/user/UserLoginProcessEvent.java @@ -0,0 +1,72 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.api.event.user; + +import me.lucko.luckperms.api.User; +import me.lucko.luckperms.api.event.LuckPermsEvent; + +import java.util.UUID; + +import javax.annotation.Nonnull; + +/** + * Called when LuckPerms has finished processing a certain Player's connection. + * + *

This event will always execute during the platforms async login/auth event. + * All handlers will be called instantly.

+ * + *

This, among other things, allows you to wait until permission data is loaded + * for a User during the BungeeCord 'LoginEvent', as event priorities are ignored + * by the current implementation.

+ * + * @since 3.4 + */ +public interface UserLoginProcessEvent extends LuckPermsEvent { + + /** + * Gets the UUID of the connection which was processed + * + * @return the uuid of the connection which was processed + */ + @Nonnull + UUID getUuid(); + + /** + * Gets the username of the connection which was processed + * + * @return the username of the connection which was processed + */ + @Nonnull + String getUsername(); + + /** + * Gets the resultant User instance which was loaded. + * + * @return the user instance + */ + User getUser(); + +} diff --git a/api/src/main/java/me/lucko/luckperms/exceptions/MembershipException.java b/api/src/main/java/me/lucko/luckperms/exceptions/MembershipException.java index 7989601c9..06d94a450 100644 --- a/api/src/main/java/me/lucko/luckperms/exceptions/MembershipException.java +++ b/api/src/main/java/me/lucko/luckperms/exceptions/MembershipException.java @@ -26,8 +26,16 @@ package me.lucko.luckperms.exceptions; /** - * Thrown when a permission holding object doesn't / already has a permission or isn't / is already is a member of a - * group + * Thrown when a certain membership state is / isn't met. + * + * For example, when: + *

+ *
    + *
  • a permission holding object doesn't have a permission
  • + *
  • a permission holding object already has a permission
  • + *
  • a permission holding object is already a member of a group
  • + *
  • a permission holding object isn't already a member of a group
  • + *
* * @since 2.7 */ diff --git a/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java index 52c2c7f15..59b2a4dde 100644 --- a/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java +++ b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectAlreadyHasException.java @@ -26,8 +26,15 @@ package me.lucko.luckperms.exceptions; /** - * Thrown when a permission holding object already has a permission, is already a member of a group, or when a track - * already contains a group. + * Thrown when an object already has something. + * + *

For example, when:

+ *

+ *
    + *
  • a permission holding object already has a permission
  • + *
  • a permission holding object is already a member of a group
  • + *
  • a track already contains a group
  • + *
*/ public class ObjectAlreadyHasException extends MembershipException { } diff --git a/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java index f6bf0ad84..47d52e767 100644 --- a/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java +++ b/api/src/main/java/me/lucko/luckperms/exceptions/ObjectLacksException.java @@ -26,8 +26,15 @@ package me.lucko.luckperms.exceptions; /** - * Thrown when a permission holding object does not already have a permission, is not already a member of a group, - * or when a track doesn't contain a group. + * Thrown when an object lacks something. + * + *

For example, when:

+ *

+ *
    + *
  • a permission holding object doesn't have a permission
  • + *
  • a permission holding object isn't already a member of a group
  • + *
  • a track doesn't contain a group
  • + *
*/ public class ObjectLacksException extends MembershipException { } diff --git a/bukkit-legacy/pom.xml b/bukkit-legacy/pom.xml index a825fc0f5..63fbb71bf 100644 --- a/bukkit-legacy/pom.xml +++ b/bukkit-legacy/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT 4.0.0 diff --git a/bukkit/pom.xml b/bukkit/pom.xml index 21450fb30..539be15d0 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT 4.0.0 diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java index 4daaf8fc0..9150ba273 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitListener.java @@ -102,7 +102,8 @@ public class BukkitListener implements Listener { - creating a user instance in the UserManager for this connection. - setting up cached data. */ try { - LoginHelper.loadUser(plugin, e.getUniqueId(), e.getName(), false); + User user = LoginHelper.loadUser(plugin, e.getUniqueId(), e.getName(), false); + plugin.getApiProvider().getEventFactory().handleUserLoginProcess(e.getUniqueId(), e.getName(), user); } catch (Exception ex) { ex.printStackTrace(); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java index fc6c616f3..6fbb46227 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/BukkitCalculatorFactory.java @@ -38,6 +38,7 @@ import me.lucko.luckperms.bukkit.processors.ChildProcessor; import me.lucko.luckperms.bukkit.processors.DefaultsProcessor; import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.PermissionCalculator; +import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.processors.MapProcessor; @@ -82,7 +83,11 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory { processors.add(new DefaultsProcessor(contexts.isOp(), plugin.getDefaultsProvider())); } - return registerCalculator(new PermissionCalculator(plugin, user.getFriendlyName(), processors.build())); + return registerCalculator(new PermissionCalculator( + plugin, + PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()), + processors.build() + )); } @Override diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java index 8b85eef4c..508881515 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissible.java @@ -35,6 +35,7 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin; import me.lucko.luckperms.common.caching.UserCache; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.verbose.CheckOrigin; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -110,13 +111,13 @@ public class LPPermissible extends PermissibleBase { @Override public boolean isPermissionSet(@NonNull String permission) { - Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission); + Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); return ts != Tristate.UNDEFINED || Permission.DEFAULT_PERMISSION.getValue(isOp()); } @Override public boolean isPermissionSet(@NonNull Permission permission) { - Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName()); + Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK); if (ts != Tristate.UNDEFINED) { return true; } @@ -130,13 +131,13 @@ public class LPPermissible extends PermissibleBase { @Override public boolean hasPermission(@NonNull String permission) { - Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission); + Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK); return ts != Tristate.UNDEFINED ? ts.asBoolean() : Permission.DEFAULT_PERMISSION.getValue(isOp()); } @Override public boolean hasPermission(@NonNull Permission permission) { - Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName()); + Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK); if (ts != Tristate.UNDEFINED) { return ts.asBoolean(); } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java index 7c9639de9..c3821ead9 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultChatHook.java @@ -331,7 +331,7 @@ public class VaultChatHook extends Chat { perms.log("Getting meta: '" + node + "' for group " + group.getName() + " on world " + world + ", server " + perms.getServer()); for (Node n : group.getOwnNodes()) { - if (!n.getValue()) continue; + if (!n.getValuePrimitive()) continue; if (!n.isMeta()) continue; if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) continue; @@ -357,7 +357,7 @@ public class VaultChatHook extends Chat { ExtractedContexts ec = ExtractedContexts.generate(Contexts.of(perms.createContextForWorldLookup(world).getContexts(), perms.isIncludeGlobal(), true, true, true, true, false)); for (Node n : group.getAllNodes(ec)) { - if (!n.getValue()) continue; + if (!n.getValuePrimitive()) continue; if (type.shouldIgnore(n)) continue; if (!n.shouldApplyWithContext(perms.createContextForWorldLookup(world).getContexts())) continue; diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java index 0a5d3581a..57f7368d3 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/vault/VaultPermissionHook.java @@ -32,16 +32,17 @@ import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.DataMutateResult; import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.Tristate; -import me.lucko.luckperms.api.caching.PermissionData; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.api.context.MutableContextSet; import me.lucko.luckperms.bukkit.LPBukkitPlugin; +import me.lucko.luckperms.common.caching.PermissionCache; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.contexts.ExtractedContexts; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.NodeFactory; +import me.lucko.luckperms.common.verbose.CheckOrigin; import net.milkbowl.vault.permission.Permission; @@ -147,7 +148,7 @@ public class VaultPermissionHook extends Permission { } // Effectively fallback to the standard Bukkit #hasPermission check. - return user.getUserData().getPermissionData(createContextForWorldLookup(player, world)).getPermissionValue(permission).asBoolean(); + return user.getUserData().getPermissionData(createContextForWorldLookup(player, world)).getPermissionValue(permission, CheckOrigin.INTERNAL).asBoolean(); } @Override @@ -396,7 +397,7 @@ public class VaultPermissionHook extends Permission { // we need to do the complex PGO checking. (it's been enabled in the config.) if (isPgoCheckInherited()) { // we can just check the cached data - PermissionData data = user.getUserData().getPermissionData(createContextForWorldLookup(plugin.getPlayer(user), world)); + PermissionCache data = user.getUserData().getPermissionData(createContextForWorldLookup(plugin.getPlayer(user), world)); for (Map.Entry e : data.getImmutableBacking().entrySet()) { if (!e.getValue()) continue; if (!e.getKey().toLowerCase().startsWith("vault.primarygroup.")) continue; @@ -409,7 +410,7 @@ public class VaultPermissionHook extends Permission { } if (isPgoCheckMemberOf()) { - if (data.getPermissionValue("group." + group) != Tristate.TRUE) { + if (data.getPermissionValue("group." + group, CheckOrigin.INTERNAL) != Tristate.TRUE) { continue; } } @@ -419,7 +420,7 @@ public class VaultPermissionHook extends Permission { } else { // we need to check the users permissions only for (Node node : user.getOwnNodes()) { - if (!node.getValue()) continue; + if (!node.getValuePrimitive()) continue; if (!node.getPermission().toLowerCase().startsWith("vault.primarygroup.")) continue; if (!node.shouldApplyOnServer(getServer(), isIncludeGlobal(), false)) continue; if (!node.shouldApplyOnWorld(world, true, false)) continue; diff --git a/bungee/pom.xml b/bungee/pom.xml index 36d7698d5..4cd8d7e68 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT 4.0.0 diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java index 3e1ed1658..a2126f56a 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeListener.java @@ -35,6 +35,7 @@ import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.utils.LoginHelper; +import me.lucko.luckperms.common.verbose.CheckOrigin; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.PendingConnection; @@ -100,7 +101,8 @@ public class BungeeListener implements Listener { - creating a user instance in the UserManager for this connection. - setting up cached data. */ try { - LoginHelper.loadUser(plugin, c.getUniqueId(), c.getName(), true); + User user = LoginHelper.loadUser(plugin, c.getUniqueId(), c.getName(), true); + plugin.getApiProvider().getEventFactory().handleUserLoginProcess(c.getUniqueId(), c.getName(), user); } catch (Exception ex) { ex.printStackTrace(); @@ -159,7 +161,7 @@ public class BungeeListener implements Listener { } Contexts contexts = plugin.getContextManager().getApplicableContexts(player); - Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission()); + Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission(), CheckOrigin.PLATFORM_PERMISSION_CHECK); if (result == Tristate.UNDEFINED && plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) { return; // just use the result provided by the proxy when the event was created } @@ -182,7 +184,7 @@ public class BungeeListener implements Listener { } Contexts contexts = plugin.getContextManager().getApplicableContexts(player); - Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission()); + Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission(), CheckOrigin.PLATFORM_LOOKUP_CHECK); if (result == Tristate.UNDEFINED && plugin.getConfiguration().get(ConfigKeys.APPLY_BUNGEE_CONFIG_PERMISSIONS)) { return; // just use the result provided by the proxy when the event was created } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/calculators/BungeeCalculatorFactory.java b/bungee/src/main/java/me/lucko/luckperms/bungee/calculators/BungeeCalculatorFactory.java index 457042fca..b6786be58 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/calculators/BungeeCalculatorFactory.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/calculators/BungeeCalculatorFactory.java @@ -33,6 +33,7 @@ import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.bungee.LPBungeePlugin; import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.PermissionCalculator; +import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.processors.MapProcessor; @@ -60,7 +61,11 @@ public class BungeeCalculatorFactory extends AbstractCalculatorFactory { processors.add(new WildcardProcessor()); } - return registerCalculator(new PermissionCalculator(plugin, user.getFriendlyName(), processors.build())); + return registerCalculator(new PermissionCalculator( + plugin, + PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()), + processors.build() + )); } @Override diff --git a/common/pom.xml b/common/pom.xml index 0d4686639..fb4c6c7e4 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT 4.0.0 diff --git a/common/src/main/java/me/lucko/luckperms/common/api/ApiHandler.java b/common/src/main/java/me/lucko/luckperms/common/api/ApiHandler.java index d769a0837..ebaff00b0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/ApiHandler.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/ApiHandler.java @@ -31,18 +31,23 @@ import me.lucko.luckperms.api.LuckPermsApi; import java.lang.reflect.Method; public class ApiHandler { - private static Method REGISTER; - private static Method UNREGISTER; + private static final Method REGISTER; + private static final Method UNREGISTER; static { + Method register = null; + Method unregister = null; try { - REGISTER = LuckPerms.class.getDeclaredMethod("registerProvider", LuckPermsApi.class); - REGISTER.setAccessible(true); + register = LuckPerms.class.getDeclaredMethod("registerProvider", LuckPermsApi.class); + register.setAccessible(true); - UNREGISTER = LuckPerms.class.getDeclaredMethod("unregisterProvider"); - UNREGISTER.setAccessible(true); + unregister = LuckPerms.class.getDeclaredMethod("unregisterProvider"); + unregister.setAccessible(true); } catch (Exception e) { e.printStackTrace(); } + + REGISTER = register; + UNREGISTER = unregister; } public static void registerProvider(LuckPermsApi luckPermsApi) { diff --git a/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java index 2f194e4e5..1b3044e67 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/ApiProvider.java @@ -88,7 +88,7 @@ public class ApiProvider implements LuckPermsApi { @Override public double getApiVersion() { - return 3.3; + return 3.4; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/PermissionCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/PermissionCache.java index 1b8a6cac7..d91726b9d 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/PermissionCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/PermissionCache.java @@ -33,6 +33,7 @@ import me.lucko.luckperms.api.caching.PermissionData; import me.lucko.luckperms.common.calculators.CalculatorFactory; import me.lucko.luckperms.common.calculators.PermissionCalculator; import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.verbose.CheckOrigin; import java.util.Collections; import java.util.Map; @@ -93,6 +94,10 @@ public class PermissionCache implements PermissionData { @Override public Tristate getPermissionValue(@NonNull String permission) { - return calculator.getPermissionValue(permission); + return calculator.getPermissionValue(permission, CheckOrigin.API); + } + + public Tristate getPermissionValue(@NonNull String permission, CheckOrigin origin) { + return calculator.getPermissionValue(permission, origin); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java index b60b0437d..23a359b34 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/UserCache.java @@ -36,7 +36,6 @@ import com.google.common.collect.ImmutableSet; import me.lucko.luckperms.api.ChatMetaType; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.caching.MetaContexts; -import me.lucko.luckperms.api.caching.MetaData; import me.lucko.luckperms.api.caching.PermissionData; import me.lucko.luckperms.api.caching.UserData; import me.lucko.luckperms.common.config.ConfigKeys; @@ -68,17 +67,17 @@ public class UserCache implements UserData { .build(new MetaCacheLoader()); @Override - public PermissionData getPermissionData(@NonNull Contexts contexts) { + public PermissionCache getPermissionData(@NonNull Contexts contexts) { return permission.get(contexts); } @Override - public MetaData getMetaData(@NonNull MetaContexts contexts) { + public MetaCache getMetaData(@NonNull MetaContexts contexts) { return meta.get(contexts); } @Override - public MetaData getMetaData(@NonNull Contexts contexts) { + public MetaCache getMetaData(@NonNull Contexts contexts) { // just create a MetaContexts instance using the values in the config return getMetaData(makeFromMetaContextsConfig(contexts, user.getPlugin())); } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java index e631bbefc..c0cb3628e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculator.java @@ -33,6 +33,7 @@ import com.github.benmanes.caffeine.cache.LoadingCache; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.processors.PermissionProcessor; +import me.lucko.luckperms.common.verbose.CheckOrigin; import java.util.List; import java.util.Map; @@ -43,7 +44,7 @@ import java.util.Map; @RequiredArgsConstructor public class PermissionCalculator { private final LuckPermsPlugin plugin; - private final String objectName; + private final PermissionCalculatorMetadata metadata; private final List processors; // caches lookup calls. @@ -54,7 +55,7 @@ public class PermissionCalculator { lookupCache.invalidateAll(); } - public Tristate getPermissionValue(String permission) { + public Tristate getPermissionValue(String permission, CheckOrigin origin) { // convert the permission to lowercase, as all values in the backing map are also lowercase. // this allows fast case insensitive lookups @@ -64,7 +65,7 @@ public class PermissionCalculator { Tristate result = lookupCache.get(permission); // log this permission lookup to the verbose handler - plugin.getVerboseHandler().offerCheckData(objectName, permission, result); + plugin.getVerboseHandler().offerCheckData(origin, metadata.getObjectName(), metadata.getContext(), permission, result); // return the result return result; diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculatorMetadata.java b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculatorMetadata.java new file mode 100644 index 000000000..585673a74 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/calculators/PermissionCalculatorMetadata.java @@ -0,0 +1,47 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.calculators; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import me.lucko.luckperms.api.context.ContextSet; + +@Getter +@AllArgsConstructor(staticName = "of") +public class PermissionCalculatorMetadata { + + /** + * The name of the object which owns the permission calculator + */ + private final String objectName; + + /** + * The context the permission calculator works with + */ + private final ContextSet context; + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java index eeab5a897..101a5e02b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/generic/permission/PermissionInfo.java @@ -173,7 +173,7 @@ public class PermissionInfo extends SharedSubCommand { } for (Node node : page) { - String s = "&3> " + (node.getValue() ? "&a" : "&c") + node.getPermission() + (console ? " &7(" + node.getValue() + "&7)" : "") + Util.getAppendableNodeContextString(node) + "\n"; + String s = "&3> " + (node.getValuePrimitive() ? "&a" : "&c") + node.getPermission() + (console ? " &7(" + node.getValuePrimitive() + "&7)" : "") + Util.getAppendableNodeContextString(node) + "\n"; if (temp) { s += "&2- expires in " + DateUtil.formatDateDiff(node.getExpiryUnixTime()) + "\n"; } @@ -186,7 +186,7 @@ public class PermissionInfo extends SharedSubCommand { private static Consumer> makeFancy(PermissionHolder holder, String label, Node node) { HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( - "¥3> " + (node.getValue() ? "¥a" : "¥c") + node.getPermission(), + "¥3> " + (node.getValuePrimitive() ? "¥a" : "¥c") + node.getPermission(), " ", "¥7Click to remove this node from " + holder.getFriendlyName() ), '¥')); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java index 1e7ae0039..dd1dcaa48 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/group/GroupListMembers.java @@ -192,7 +192,7 @@ public class GroupListMembers extends SubCommand { private static Consumer> makeFancy(String holderName, boolean group, String label, HeldPermission perm) { HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( - "&3> " + (perm.asNode().getValue() ? "&a" : "&c") + perm.asNode().getGroupName(), + "&3> " + (perm.asNode().getValuePrimitive() ? "&a" : "&c") + perm.asNode().getGroupName(), " ", "&7Click to remove this parent from " + holderName ), Constants.FORMAT_CHAR)); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/log/LogNotify.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/log/LogNotify.java index 313d52181..2ce1f8e89 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/log/LogNotify.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/log/LogNotify.java @@ -61,7 +61,7 @@ public class LogNotify extends SubCommand { // if they don't have the perm, they're not ignoring // if set to false, ignore it, return false - return ret.map(Node::getValue).orElse(false); + return ret.map(Node::getValuePrimitive).orElse(false); } private static void setIgnoring(LuckPermsPlugin plugin, UUID uuid, boolean state) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java index 5296ec230..4f4e48a29 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/CheckCommand.java @@ -39,6 +39,7 @@ import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.utils.Predicates; +import me.lucko.luckperms.common.verbose.CheckOrigin; import java.util.List; import java.util.UUID; @@ -67,7 +68,7 @@ public class CheckCommand extends SingleCommand { return CommandResult.STATE_ERROR; } - Tristate tristate = user.getUserData().getPermissionData(plugin.getContextForUser(user)).getPermissionValue(permission); + Tristate tristate = user.getUserData().getPermissionData(plugin.getContextForUser(user)).getPermissionValue(permission, CheckOrigin.INTERNAL); Message.CHECK_RESULT.send(sender, user.getFriendlyName(), permission, Util.formatTristate(tristate)); return CommandResult.SUCCESS; } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java index 4340dcd91..7910b917e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/SearchCommand.java @@ -191,7 +191,7 @@ public class SearchCommand extends SingleCommand { private static Consumer> makeFancy(String holderName, boolean group, String label, HeldPermission perm) { HoverEvent hoverEvent = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline( - "&3> " + (perm.asNode().getValue() ? "&a" : "&c") + perm.asNode().getPermission(), + "&3> " + (perm.asNode().getValuePrimitive() ? "&a" : "&c") + perm.asNode().getPermission(), " ", "&7Click to remove this node from " + holderName ), Constants.FORMAT_CHAR)); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java index 8957ed0ab..dd27cbf03 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.common.commands.impl.misc; -import me.lucko.luckperms.api.caching.PermissionData; +import me.lucko.luckperms.common.caching.PermissionCache; import me.lucko.luckperms.common.commands.CommandException; import me.lucko.luckperms.common.commands.CommandResult; import me.lucko.luckperms.common.commands.abstraction.SingleCommand; @@ -86,7 +86,7 @@ public class TreeCommand extends SingleCommand { return CommandResult.STATE_ERROR; } - PermissionData permissionData = user.getUserData().getPermissionData(plugin.getContextForUser(user)); + PermissionCache permissionData = user.getUserData().getPermissionData(plugin.getContextForUser(user)); TreeView view = TreeViewBuilder.newBuilder().rootPosition(selection).maxLevels(maxLevel).build(plugin.getPermissionVault()); if (!view.hasData()) { diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java index 6a455c065..39d681782 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/VerboseCommand.java @@ -63,6 +63,7 @@ public class VerboseCommand extends SingleCommand { return CommandResult.INVALID_ARGS; } + boolean noTraces = args.remove("--notrace") || args.remove("--notraces") || args.remove("--slim") || args.remove("-s"); String mode = args.get(0).toLowerCase(); if (mode.equals("on") || mode.equals("true") || mode.equals("record")) { @@ -108,7 +109,7 @@ public class VerboseCommand extends SingleCommand { } else { Message.VERBOSE_RECORDING_UPLOAD_START.send(sender); - String url = listener.uploadPasteData(); + String url = listener.uploadPasteData(!noTraces); if (url == null) { url = "null"; } diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java index 619feeb0c..4f31a6f13 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserDemote.java @@ -99,7 +99,7 @@ public class UserDemote extends SubCommand { // Load applicable groups Set nodes = user.getEnduringNodes().values().stream() .filter(Node::isGroupNode) - .filter(Node::getValue) + .filter(Node::getValuePrimitive) .filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable())) .collect(Collectors.toSet()); diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java index f0482ead7..ec55e6d48 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserPromote.java @@ -97,7 +97,7 @@ public class UserPromote extends SubCommand { // Load applicable groups Set nodes = user.getEnduringNodes().values().stream() .filter(Node::isGroupNode) - .filter(Node::getValue) + .filter(Node::getValuePrimitive) .filter(node -> node.getFullContexts().makeImmutable().equals(context.makeImmutable())) .collect(Collectors.toSet()); diff --git a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetComparator.java b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetComparator.java index 1432c77fe..4105f3552 100644 --- a/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetComparator.java +++ b/common/src/main/java/me/lucko/luckperms/common/contexts/ContextSetComparator.java @@ -27,20 +27,13 @@ package me.lucko.luckperms.common.contexts; import me.lucko.luckperms.api.context.ImmutableContextSet; +import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; +import java.util.List; import java.util.Map; -import java.util.TreeSet; public class ContextSetComparator implements Comparator { - private static final Comparator> STRING_ENTRY_COMPARATOR = (o1, o2) -> { - int ret = o1.getKey().compareTo(o2.getKey()); - if (ret != 0) { - return ret; - } - - return o1.getValue().compareTo(o2.getValue()); - }; private static final ContextSetComparator INSTANCE = new ContextSetComparator(); public static Comparator get() { @@ -75,16 +68,15 @@ public class ContextSetComparator implements Comparator { return o1Size > o2Size ? 1 : -1; } - // we *have* to maintain transitivity in this comparator. this may be expensive, but it's necessary, as these - // values are stored in a treemap. + // we *have* to maintain transitivity in this comparator. this may be expensive, but it's necessary, as this + // comparator is used in the PermissionHolder nodes treemap // in order to have consistent ordering, we have to compare the content of the context sets by ordering the // elements and then comparing which set is greater. - TreeSet> o1Map = new TreeSet<>(STRING_ENTRY_COMPARATOR); - TreeSet> o2Map = new TreeSet<>(STRING_ENTRY_COMPARATOR); - - o1Map.addAll(o1.toMultimap().entries()); - o2Map.addAll(o2.toMultimap().entries()); + List> o1Map = new ArrayList<>(o1.toSet()); + List> o2Map = new ArrayList<>(o2.toSet()); + o1Map.sort(STRING_ENTRY_COMPARATOR); + o2Map.sort(STRING_ENTRY_COMPARATOR); int o1MapSize = o1Map.size(); int o2MapSize = o2Map.size(); @@ -101,18 +93,33 @@ public class ContextSetComparator implements Comparator { Map.Entry ent2 = it2.next(); // compare these values. - if (ent1.getKey().equals(ent2.getKey()) && ent1.getValue().equals(ent2.getValue())) { + //noinspection StringEquality - strings are intern'd + if (ent1.getKey() == ent2.getKey() && ent1.getValue() == ent2.getValue()) { // identical entries. just move on continue; } - // these values are at the same position in the ordered sets. + // these entries are at the same position in the ordered sets. // if ent1 is "greater" than ent2, then at this first position, o1 has a "greater" entry, and can therefore be considered - // a greater set. + // a greater set, and vice versa return STRING_ENTRY_COMPARATOR.compare(ent1, ent2); } - // shouldn't ever reach this point. ever. + // shouldn't ever reach this point. return 0; } + + private static final Comparator FAST_STRING_COMPARATOR = (o1, o2) -> { + //noinspection StringEquality + return o1 == o2 ? 0 : o1.compareTo(o2); + }; + + private static final Comparator> STRING_ENTRY_COMPARATOR = (o1, o2) -> { + int ret = FAST_STRING_COMPARATOR.compare(o1.getKey(), o2.getKey()); + if (ret != 0) { + return ret; + } + + return FAST_STRING_COMPARATOR.compare(o1.getValue(), o2.getValue()); + }; } diff --git a/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java b/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java index 04a9ab7f3..4f14ebab2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/event/EventFactory.java @@ -64,6 +64,7 @@ import me.lucko.luckperms.common.event.impl.EventUserDataRecalculate; import me.lucko.luckperms.common.event.impl.EventUserDemote; import me.lucko.luckperms.common.event.impl.EventUserFirstLogin; import me.lucko.luckperms.common.event.impl.EventUserLoad; +import me.lucko.luckperms.common.event.impl.EventUserLoginProcess; import me.lucko.luckperms.common.event.impl.EventUserPromote; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.PermissionHolder; @@ -79,158 +80,167 @@ import java.util.concurrent.atomic.AtomicBoolean; public final class EventFactory { private final LuckPermsEventBus eventBus; - private void fireEvent(LuckPermsEvent event) { + private void fireEventAsync(LuckPermsEvent event) { eventBus.fireEventAsync(event); } + private void fireEvent(LuckPermsEvent event) { + eventBus.fireEvent(event); + } + public void handleGroupCreate(Group group, CreationCause cause) { EventGroupCreate event = new EventGroupCreate(group.getDelegate(), cause); - fireEvent(event); + fireEventAsync(event); } public void handleGroupDelete(Group group, DeletionCause cause) { EventGroupDelete event = new EventGroupDelete(group.getName(), ImmutableSet.copyOf(group.getEnduringNodes().values()), cause); - fireEvent(event); + fireEventAsync(event); } public void handleGroupLoadAll() { EventGroupLoadAll event = new EventGroupLoadAll(); - fireEvent(event); + fireEventAsync(event); } public void handleGroupLoad(Group group) { EventGroupLoad event = new EventGroupLoad(group.getDelegate()); - fireEvent(event); + fireEventAsync(event); } public boolean handleLogBroadcast(boolean initialState, LogEntry entry, LogBroadcastEvent.Origin origin) { AtomicBoolean cancel = new AtomicBoolean(initialState); EventLogBroadcast event = new EventLogBroadcast(cancel, entry, origin); - eventBus.fireEvent(event); + fireEvent(event); return cancel.get(); } public boolean handleLogPublish(boolean initialState, LogEntry entry) { AtomicBoolean cancel = new AtomicBoolean(initialState); EventLogPublish event = new EventLogPublish(cancel, entry); - eventBus.fireEvent(event); + fireEvent(event); return cancel.get(); } public boolean handleLogNetworkPublish(boolean initialState, UUID id, LogEntry entry) { AtomicBoolean cancel = new AtomicBoolean(initialState); EventLogNetworkPublish event = new EventLogNetworkPublish(cancel, id, entry); - eventBus.fireEvent(event); + fireEvent(event); return cancel.get(); } public void handleLogReceive(UUID id, LogEntry entry) { EventLogReceive event = new EventLogReceive(id, entry); - fireEvent(event); + fireEventAsync(event); } public void handleNodeAdd(Node node, PermissionHolder target, Collection before, Collection after) { EventNodeAdd event = new EventNodeAdd(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); - fireEvent(event); + fireEventAsync(event); } public void handleNodeClear(PermissionHolder target, Collection before, Collection after) { EventNodeClear event = new EventNodeClear(target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); - fireEvent(event); + fireEventAsync(event); } public void handleNodeRemove(Node node, PermissionHolder target, Collection before, Collection after) { EventNodeRemove event = new EventNodeRemove(node, target.getDelegate(), ImmutableSet.copyOf(before), ImmutableSet.copyOf(after)); - fireEvent(event); + fireEventAsync(event); } public void handleConfigReload() { EventConfigReload event = new EventConfigReload(); - fireEvent(event); + fireEventAsync(event); } public void handlePostSync() { EventPostSync event = new EventPostSync(); - fireEvent(event); + fireEventAsync(event); } public boolean handleNetworkPreSync(boolean initialState, UUID id) { AtomicBoolean cancel = new AtomicBoolean(initialState); EventPreNetworkSync event = new EventPreNetworkSync(cancel, id); - eventBus.fireEvent(event); + fireEvent(event); return cancel.get(); } public boolean handlePreSync(boolean initialState) { AtomicBoolean cancel = new AtomicBoolean(initialState); EventPreSync event = new EventPreSync(cancel); - eventBus.fireEvent(event); + fireEvent(event); return cancel.get(); } public void handleTrackCreate(Track track, CreationCause cause) { EventTrackCreate event = new EventTrackCreate(track.getDelegate(), cause); - fireEvent(event); + fireEventAsync(event); } public void handleTrackDelete(Track track, DeletionCause cause) { EventTrackDelete event = new EventTrackDelete(track.getName(), ImmutableList.copyOf(track.getGroups()), cause); - fireEvent(event); + fireEventAsync(event); } public void handleTrackLoadAll() { EventTrackLoadAll event = new EventTrackLoadAll(); - fireEvent(event); + fireEventAsync(event); } public void handleTrackLoad(Track track) { EventTrackLoad event = new EventTrackLoad(track.getDelegate()); - fireEvent(event); + fireEventAsync(event); } public void handleTrackAddGroup(Track track, String group, List before, List after) { EventTrackAddGroup event = new EventTrackAddGroup(group, track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.copyOf(after)); - fireEvent(event); + fireEventAsync(event); } public void handleTrackClear(Track track, List before) { EventTrackClear event = new EventTrackClear(track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.of()); - fireEvent(event); + fireEventAsync(event); } public void handleTrackRemoveGroup(Track track, String group, List before, List after) { EventTrackRemoveGroup event = new EventTrackRemoveGroup(group, track.getDelegate(), ImmutableList.copyOf(before), ImmutableList.copyOf(after)); - fireEvent(event); + fireEventAsync(event); } public void handleUserCacheLoad(User user, UserData data) { EventUserCacheLoad event = new EventUserCacheLoad(user.getDelegate(), data); - fireEvent(event); + fireEventAsync(event); } public void handleUserDataRecalculate(User user, UserData data) { EventUserDataRecalculate event = new EventUserDataRecalculate(user.getDelegate(), data); - fireEvent(event); + fireEventAsync(event); } public void handleUserFirstLogin(UUID uuid, String username) { EventUserFirstLogin event = new EventUserFirstLogin(uuid, username); - fireEvent(event); + fireEventAsync(event); } public void handleUserLoad(User user) { EventUserLoad event = new EventUserLoad(user.getDelegate()); + fireEventAsync(event); + } + + public void handleUserLoginProcess(UUID uuid, String username, User user) { + EventUserLoginProcess event = new EventUserLoginProcess(uuid, username, user.getDelegate()); fireEvent(event); } public void handleUserDemote(User user, Track track, String from, String to) { EventUserDemote event = new EventUserDemote(track.getDelegate(), user.getDelegate(), from, to); - fireEvent(event); + fireEventAsync(event); } public void handleUserPromote(User user, Track track, String from, String to) { EventUserPromote event = new EventUserPromote(track.getDelegate(), user.getDelegate(), from, to); - fireEvent(event); + fireEventAsync(event); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/event/impl/EventUserLoginProcess.java b/common/src/main/java/me/lucko/luckperms/common/event/impl/EventUserLoginProcess.java new file mode 100644 index 000000000..92cc20400 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/event/impl/EventUserLoginProcess.java @@ -0,0 +1,47 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.event.impl; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import me.lucko.luckperms.api.User; +import me.lucko.luckperms.api.event.user.UserLoginProcessEvent; +import me.lucko.luckperms.common.event.AbstractEvent; + +import java.util.UUID; + +@Getter +@ToString +@AllArgsConstructor +public class EventUserLoginProcess extends AbstractEvent implements UserLoginProcessEvent { + + private final UUID uuid; + private final String username; + private final User user; + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java b/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java index dc87ed187..374b69ef3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/CommandSpec.java @@ -57,7 +57,8 @@ public enum CommandSpec { VERBOSE("Manage verbose permission checking", "/%s verbose [filter]", Arg.list( Arg.create("on|record|off|paste", true, "whether to enable/disable logging, or to paste the logged output"), - Arg.create("filter", false, "the filter to match entries against") + Arg.create("filter", false, "the filter to match entries against"), + Arg.create("--slim", false, "add \"--slim\" to exclude trace data from the pasted output") ) ), TREE("Generate a tree view of permissions", "/%s tree [selection] [max level] [player]", diff --git a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java index ea8ae746a..c4d3ee779 100644 --- a/common/src/main/java/me/lucko/luckperms/common/locale/Message.java +++ b/common/src/main/java/me/lucko/luckperms/common/locale/Message.java @@ -57,7 +57,7 @@ public enum Message { OP_DISABLED("&bThe vanilla OP system is disabled on this server.", false), OP_DISABLED_SPONGE("&2Server Operator status has no effect when a permission plugin is installed. Please edit user data directly.", true), LOG("&3LOG &3&l> {0}", true), - VERBOSE_LOG("&3VERBOSE &3&l> {0}", true), + VERBOSE_LOG("&3VB &3&l> {0}", true), EXPORT_LOG("&3EXPORT &3&l> &f{0}", true), EXPORT_LOG_PROGRESS("&3EXPORT &3&l> &7{0}", true), diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java b/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java index b96fcf928..1e9b4f703 100644 --- a/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/managers/GenericUserManager.java @@ -39,35 +39,8 @@ import java.util.UUID; @RequiredArgsConstructor public class GenericUserManager extends AbstractManager implements UserManager { - public static boolean giveDefaultIfNeeded(User user, boolean save, LuckPermsPlugin plugin) { - boolean hasGroup = false; - if (user.getPrimaryGroup().getStoredValue() != null && !user.getPrimaryGroup().getStoredValue().isEmpty()) { - for (Node node : user.getEnduringNodes().values()) { - if (node.hasSpecificContext()) { - continue; - } - - if (node.isGroupNode()) { - hasGroup = true; - break; - } - } - } - - if (hasGroup) { - return false; - } - - user.getPrimaryGroup().setStoredValue("default"); - user.setPermission(NodeFactory.make("group.default")); - - if (save) { - plugin.getStorage().saveUser(user); - } - - return true; - } + private final LuckPermsPlugin plugin; @Override public User getOrMake(UserIdentifier id) { @@ -78,39 +51,6 @@ public class GenericUserManager extends AbstractManager im return ret; } - /** - * Check whether the user's state indicates that they should be persisted to storage. - * - * @param user the user to check - * @return true if the user should be saved - */ - public static boolean shouldSave(User user) { - if (user.getEnduringNodes().size() != 1) { - return true; - } - - for (Node node : user.getEnduringNodes().values()) { - // There's only one. - if (!node.isGroupNode()) { - return true; - } - - if (node.isTemporary() || node.isServerSpecific() || node.isWorldSpecific()) { - return true; - } - - if (!node.getGroupName().equalsIgnoreCase("default")) { - // The user's only node is not the default group one. - return true; - } - } - - // Not in the default primary group - return !user.getPrimaryGroup().getStoredValue().equalsIgnoreCase("default"); - } - - private final LuckPermsPlugin plugin; - @Override public User apply(UserIdentifier id) { return !id.getUsername().isPresent() ? @@ -140,9 +80,12 @@ public class GenericUserManager extends AbstractManager im } @Override - public void cleanup(User user) { + public boolean cleanup(User user) { if (!plugin.isPlayerOnline(plugin.getUuidCache().getExternalUUID(user.getUuid()))) { unload(user); + return true; + } else { + return false; } } @@ -177,4 +120,65 @@ public class GenericUserManager extends AbstractManager im }); }); } + + public static boolean giveDefaultIfNeeded(User user, boolean save, LuckPermsPlugin plugin) { + boolean hasGroup = false; + + if (user.getPrimaryGroup().getStoredValue() != null && !user.getPrimaryGroup().getStoredValue().isEmpty()) { + for (Node node : user.getEnduringNodes().values()) { + if (node.hasSpecificContext()) { + continue; + } + + if (node.isGroupNode()) { + hasGroup = true; + break; + } + } + } + + if (hasGroup) { + return false; + } + + user.getPrimaryGroup().setStoredValue("default"); + user.setPermission(NodeFactory.make("group.default")); + + if (save) { + plugin.getStorage().saveUser(user); + } + + return true; + } + + /** + * Check whether the user's state indicates that they should be persisted to storage. + * + * @param user the user to check + * @return true if the user should be saved + */ + public static boolean shouldSave(User user) { + if (user.getEnduringNodes().size() != 1) { + return true; + } + + for (Node node : user.getEnduringNodes().values()) { + // There's only one. + if (!node.isGroupNode()) { + return true; + } + + if (node.isTemporary() || node.isServerSpecific() || node.isWorldSpecific()) { + return true; + } + + if (!node.getGroupName().equalsIgnoreCase("default")) { + // The user's only node is not the default group one. + return true; + } + } + + // Not in the default primary group + return !user.getPrimaryGroup().getStoredValue().equalsIgnoreCase("default"); + } } diff --git a/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java b/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java index 99f7756d9..10ac993e3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/managers/UserManager.java @@ -61,7 +61,7 @@ public interface UserManager extends Manager { * * @param user The user to be cleaned up */ - void cleanup(User user); + boolean cleanup(User user); /** * Schedules a task to cleanup a user after a certain period of time, if they're not on the server anymore. diff --git a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java index 113987f22..a53340726 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/PermissionHolder.java @@ -546,7 +546,7 @@ public abstract class PermissionHolder { if (!n.isGroupNode()) continue; String groupName = n.getGroupName(); - if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValue()) continue; + if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue; if (!((contexts.isApplyGlobalGroups() || n.isServerSpecific()) && (contexts.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) { continue; @@ -624,7 +624,7 @@ public abstract class PermissionHolder { if (!n.isGroupNode()) continue; String groupName = n.getGroupName(); - if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValue()) continue; + if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue; Group g = plugin.getGroupManager().getIfLoaded(groupName); if (g != null) { @@ -717,11 +717,11 @@ public abstract class PermissionHolder { for (Node node : entries) { String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission(); - if (perms.putIfAbsent(perm, node.getValue()) == null) { + if (perms.putIfAbsent(perm, node.getValuePrimitive()) == null) { if (applyShorthand) { List sh = node.resolveShorthand(); if (!sh.isEmpty()) { - sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValue())); + sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValuePrimitive())); } } } @@ -738,11 +738,11 @@ public abstract class PermissionHolder { for (Node node : entries) { String perm = lowerCase ? node.getPermission().toLowerCase() : node.getPermission(); - if (perms.putIfAbsent(perm, node.getValue()) == null) { + if (perms.putIfAbsent(perm, node.getValuePrimitive()) == null) { if (applyShorthand) { List sh = node.resolveShorthand(); if (!sh.isEmpty()) { - sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValue())); + sh.stream().map(s -> lowerCase ? s.toLowerCase() : s).forEach(s -> perms.putIfAbsent(s, node.getValuePrimitive())); } } } @@ -770,7 +770,7 @@ public abstract class PermissionHolder { List nodes = filterNodes(context.getContextSet()); for (Node node : nodes) { - if (!node.getValue()) continue; + if (!node.getValuePrimitive()) continue; if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue; if (!((contexts.isIncludeGlobal() || node.isServerSpecific()) && (contexts.isIncludeGlobalWorld() || node.isWorldSpecific()))) { @@ -793,7 +793,7 @@ public abstract class PermissionHolder { if (!n.isGroupNode()) continue; String groupName = n.getGroupName(); - if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValue()) continue; + if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue; if (!((contexts.isApplyGlobalGroups() || n.isServerSpecific()) && (contexts.isApplyGlobalWorldGroups() || n.isWorldSpecific()))) { continue; @@ -834,7 +834,7 @@ public abstract class PermissionHolder { List nodes = getOwnNodes(); for (Node node : nodes) { - if (!node.getValue()) continue; + if (!node.getValuePrimitive()) continue; if (!node.isMeta() && !node.isPrefix() && !node.isSuffix()) continue; accumulator.accumulateNode(ImmutableLocalizedNode.of(node, getObjectName())); @@ -853,7 +853,7 @@ public abstract class PermissionHolder { if (!n.isGroupNode()) continue; String groupName = n.getGroupName(); - if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValue()) continue; + if (!processedGroups.add(groupName) || excludedGroups.contains(groupName) || !n.getValuePrimitive()) continue; Group g = plugin.getGroupManager().getIfLoaded(groupName); if (g != null) { diff --git a/common/src/main/java/me/lucko/luckperms/common/model/User.java b/common/src/main/java/me/lucko/luckperms/common/model/User.java index fc664af46..05a1fe773 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/User.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/User.java @@ -70,13 +70,7 @@ public class User extends PermissionHolder implements Identifiable refreshBuffer = new BufferedRequest(250L, 50L, r -> getPlugin().doAsync(r)) { - @Override - protected Void perform() { - refreshPermissions(); - return null; - } - }; + private BufferedRequest refreshBuffer = new UserRefreshBuffer(this); @Getter private final UserDelegate delegate = new UserDelegate(this); @@ -214,4 +208,20 @@ public class User extends PermissionHolder implements Identifiable { + private final User user; + + private UserRefreshBuffer(User user) { + super(250L, 50L, r -> user.getPlugin().doAsync(r)); + this.user = user; + } + + @Override + protected Void perform() { + user.refreshPermissions(); + return null; + } + } + } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java index cf88a9fe2..8c0242c73 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/ImmutableNode.java @@ -28,7 +28,6 @@ package me.lucko.luckperms.common.node; import lombok.Getter; import lombok.ToString; -import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; @@ -50,18 +49,31 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static com.google.common.base.Preconditions.checkState; + /** - * An immutable permission node + * An immutable implementation of {@link Node}. */ @ToString(of = {"permission", "value", "override", "server", "world", "expireAt", "contexts"}) public final class ImmutableNode implements Node { private static final int NODE_SEPARATOR_CHAR = Character.getNumericValue('.'); + private static final String[] PERMISSION_DELIMS = new String[]{"/", "-", "$", "(", ")", "=", ","}; + private static final String[] SERVER_WORLD_DELIMS = new String[]{"/", "-"}; + private static final Splitter META_SPLITTER = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2); + + + /* + * NODE STATE + * + * This are the actual node parameters, and are + * basically what this class wraps. + */ + @Getter private final String permission; - @Getter - private final Boolean value; + private final boolean value; @Getter private boolean override; @@ -80,12 +92,28 @@ public final class ImmutableNode implements Node { @Getter private final ImmutableContextSet fullContexts; - // Cached state + /* + * CACHED STATE + * + * These values are based upon the node state above, and are stored here + * to make node comparison and manipulation faster. + * + * This increases the memory footprint of this class by a bit, but it is + * worth it for the gain in speed. + * + * The methods on this class are called v. frequently. + */ - // these save on lots of instance creation when comparing nodes + // storing optionals as a field type is usually a bad idea, however, the + // #getServer and #getWorld methods are called frequently when comparing nodes. + // without caching these values, it creates quite a bit of object churn + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") private final Optional optServer; + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") private final Optional optWorld; + private final int hashCode; + private final boolean isGroup; private String groupName; @@ -103,7 +131,7 @@ public final class ImmutableNode implements Node { private final List resolvedShorthand; - private final String serializedNode; + private String serializedNode = null; /** * Make an immutable node instance @@ -116,7 +144,7 @@ public final class ImmutableNode implements Node { * @param contexts any additional contexts applying to this node */ @SuppressWarnings("deprecation") - public ImmutableNode(String permission, boolean value, boolean override, long expireAt, String server, String world, ContextSet contexts) { + ImmutableNode(String permission, boolean value, boolean override, long expireAt, String server, String world, ContextSet contexts) { if (permission == null || permission.equals("")) { throw new IllegalArgumentException("Empty permission"); } @@ -135,18 +163,20 @@ public final class ImmutableNode implements Node { world = null; } - this.permission = NodeFactory.unescapeDelimiters(permission, "/", "-", "$", "(", ")", "=", ","); + this.permission = NodeFactory.unescapeDelimiters(permission, PERMISSION_DELIMS).intern(); this.value = value; this.override = override; this.expireAt = expireAt; - this.server = NodeFactory.unescapeDelimiters(server, "/", "-"); - this.world = NodeFactory.unescapeDelimiters(world, "/", "-"); + this.server = internString(NodeFactory.unescapeDelimiters(server, SERVER_WORLD_DELIMS)); + this.world = internString(NodeFactory.unescapeDelimiters(world, SERVER_WORLD_DELIMS)); this.contexts = contexts == null ? ContextSet.empty() : contexts.makeImmutable(); + String lowerCasePermission = this.permission.toLowerCase(); + // Setup state - isGroup = this.permission.toLowerCase().startsWith("group."); + isGroup = lowerCasePermission.startsWith("group."); if (isGroup) { - groupName = this.permission.substring("group.".length()).toLowerCase(); + groupName = lowerCasePermission.substring("group.".length()).intern(); } isWildcard = this.permission.endsWith(".*"); @@ -154,38 +184,48 @@ public final class ImmutableNode implements Node { isMeta = NodeFactory.isMetaNode(this.permission); if (isMeta) { - List metaPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("meta.".length())); - meta = Maps.immutableEntry(MetaUtils.unescapeCharacters(metaPart.get(0)), MetaUtils.unescapeCharacters(metaPart.get(1))); + List metaPart = META_SPLITTER.splitToList(this.permission.substring("meta.".length())); + meta = Maps.immutableEntry(MetaUtils.unescapeCharacters(metaPart.get(0)).intern(), MetaUtils.unescapeCharacters(metaPart.get(1)).intern()); } isPrefix = NodeFactory.isPrefixNode(this.permission); if (isPrefix) { - List prefixPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("prefix.".length())); + List prefixPart = META_SPLITTER.splitToList(this.permission.substring("prefix.".length())); Integer i = Integer.parseInt(prefixPart.get(0)); - prefix = Maps.immutableEntry(i, MetaUtils.unescapeCharacters(prefixPart.get(1))); + prefix = Maps.immutableEntry(i, MetaUtils.unescapeCharacters(prefixPart.get(1)).intern()); } isSuffix = NodeFactory.isSuffixNode(this.permission); if (isSuffix) { - List suffixPart = Splitter.on(PatternCache.compileDelimitedMatcher(".", "\\")).limit(2).splitToList(getPermission().substring("suffix.".length())); + List suffixPart = META_SPLITTER.splitToList(this.permission.substring("suffix.".length())); Integer i = Integer.parseInt(suffixPart.get(0)); - suffix = Maps.immutableEntry(i, MetaUtils.unescapeCharacters(suffixPart.get(1))); + suffix = Maps.immutableEntry(i, MetaUtils.unescapeCharacters(suffixPart.get(1)).intern()); } resolvedShorthand = ImmutableList.copyOf(ShorthandParser.parseShorthand(getPermission())); - serializedNode = calculateSerializedNode(); - MutableContextSet fullContexts = this.contexts.mutableCopy(); - if (isServerSpecific()) { - fullContexts.add("server", this.server); - } - if (isWorldSpecific()) { - fullContexts.add("world", this.world); + if (this.server != null || this.world != null) { + MutableContextSet fullContexts = this.contexts.mutableCopy(); + if (this.server != null) { + fullContexts.add("server", this.server); + } + if (this.world != null) { + fullContexts.add("world", this.world); + } + + this.fullContexts = fullContexts.makeImmutable(); + } else { + this.fullContexts = this.contexts; } - this.fullContexts = fullContexts.makeImmutable(); this.optServer = Optional.ofNullable(this.server); this.optWorld = Optional.ofNullable(this.world); + this.hashCode = calculateHashCode(); + } + + @Override + public boolean getValuePrimitive() { + return value; } @Override @@ -225,19 +265,19 @@ public final class ImmutableNode implements Node { @Override public long getExpiryUnixTime() { - Preconditions.checkState(isTemporary(), "Node does not have an expiry time."); + checkState(isTemporary(), "Node does not have an expiry time."); return expireAt; } @Override public Date getExpiry() { - Preconditions.checkState(isTemporary(), "Node does not have an expiry time."); + checkState(isTemporary(), "Node does not have an expiry time."); return new Date(expireAt * 1000L); } @Override public long getSecondsTilExpiry() { - Preconditions.checkState(isTemporary(), "Node does not have an expiry time."); + checkState(isTemporary(), "Node does not have an expiry time."); return expireAt - DateUtil.unixSecondsNow(); } @@ -253,7 +293,7 @@ public final class ImmutableNode implements Node { @Override public String getGroupName() { - Preconditions.checkState(isGroupNode(), "Node is not a group node"); + checkState(isGroupNode(), "Node is not a group node"); return groupName; } @@ -274,7 +314,7 @@ public final class ImmutableNode implements Node { @Override public Map.Entry getMeta() { - Preconditions.checkState(isMeta(), "Node is not a meta node"); + checkState(isMeta(), "Node is not a meta node"); return meta; } @@ -285,7 +325,7 @@ public final class ImmutableNode implements Node { @Override public Map.Entry getPrefix() { - Preconditions.checkState(isPrefix(), "Node is not a prefix node"); + checkState(isPrefix(), "Node is not a prefix node"); return prefix; } @@ -296,7 +336,7 @@ public final class ImmutableNode implements Node { @Override public Map.Entry getSuffix() { - Preconditions.checkState(isSuffix(), "Node is not a suffix node"); + checkState(isSuffix(), "Node is not a suffix node"); return suffix; } @@ -395,7 +435,10 @@ public final class ImmutableNode implements Node { } @Override - public String toSerializedNode() { + public synchronized String toSerializedNode() { + if (serializedNode == null) { + serializedNode = calculateSerializedNode(); + } return serializedNode; } @@ -403,16 +446,11 @@ public final class ImmutableNode implements Node { StringBuilder builder = new StringBuilder(); if (server != null) { - builder.append(NodeFactory.escapeDelimiters(server, "/", "-")); - - if (world != null) { - builder.append("-").append(NodeFactory.escapeDelimiters(world, "/", "-")); - } + builder.append(NodeFactory.escapeDelimiters(server, SERVER_WORLD_DELIMS)); + if (world != null) builder.append("-").append(NodeFactory.escapeDelimiters(world, SERVER_WORLD_DELIMS)); builder.append("/"); } else { - if (world != null) { - builder.append("global-").append(NodeFactory.escapeDelimiters(world, "/", "-")).append("/"); - } + if (world != null) builder.append("global-").append(NodeFactory.escapeDelimiters(world, SERVER_WORLD_DELIMS)).append("/"); } if (!contexts.isEmpty()) { @@ -420,162 +458,110 @@ public final class ImmutableNode implements Node { for (Map.Entry entry : contexts.toSet()) { builder.append(NodeFactory.escapeDelimiters(entry.getKey(), "=", "(", ")", ",")).append("=").append(NodeFactory.escapeDelimiters(entry.getValue(), "=", "(", ")", ",")).append(","); } - builder.deleteCharAt(builder.length() - 1); builder.append(")"); } builder.append(NodeFactory.escapeDelimiters(permission, "/", "-", "$", "(", ")", "=", ",")); - - if (expireAt != 0L) { - builder.append("$").append(expireAt); - } - + if (expireAt != 0L) builder.append("$").append(expireAt); return builder.toString(); } + @SuppressWarnings("StringEquality") + @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Node)) return false; final Node other = (Node) o; - if (!this.permission.equals(other.getPermission())) return false; - if (!this.getValue().equals(other.getValue())) return false; + if (this.permission != other.getPermission()) return false; + if (this.value != other.getValuePrimitive()) return false; if (this.override != other.isOverride()) return false; - final String thisServer = this.getServer().orElse(null); + final String thisServer = this.server; final String otherServer = other.getServer().orElse(null); if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false; - final String thisWorld = this.getWorld().orElse(null); + final String thisWorld = this.world; final String otherWorld = other.getWorld().orElse(null); if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) return false; - final long thisExpireAt = this.isTemporary() ? this.getExpiryUnixTime() : 0L; final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L; - - return thisExpireAt == otherExpireAt && this.getContexts().equals(other.getContexts()); + return this.expireAt == otherExpireAt && this.getContexts().equals(other.getContexts()); } + @Override public int hashCode() { + return this.hashCode; + } + + private int calculateHashCode() { final int PRIME = 59; int result = 1; result = result * PRIME + this.permission.hashCode(); - result = result * PRIME + Boolean.hashCode(this.value); + result = result * PRIME + (this.value ? 79 : 97); result = result * PRIME + (this.override ? 79 : 97); - - final String server = this.getServer().orElse(null); - result = result * PRIME + (server == null ? 43 : server.hashCode()); - - final String world = this.getWorld().orElse(null); - result = result * PRIME + (world == null ? 43 : world.hashCode()); - + result = result * PRIME + (this.server == null ? 43 : this.server.hashCode()); + result = result * PRIME + (this.world == null ? 43 : this.world.hashCode()); result = result * PRIME + (int) (this.expireAt >>> 32 ^ this.expireAt); result = result * PRIME + this.contexts.hashCode(); return result; } + @SuppressWarnings("StringEquality") @Override public boolean equalsIgnoringValue(Node other) { - if (!other.getPermission().equalsIgnoreCase(this.getPermission())) { - return false; - } + if (this.permission != other.getPermission()) return false; + if (this.override != other.isOverride()) return false; - if (other.isTemporary() != this.isTemporary()) { - return false; - } + final String thisServer = this.server; + final String otherServer = other.getServer().orElse(null); + if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false; - if (this.isTemporary()) { - if (other.getExpiryUnixTime() != this.getExpiryUnixTime()) { - return false; - } - } + final String thisWorld = this.world; + final String otherWorld = other.getWorld().orElse(null); + if (thisWorld == null ? otherWorld != null : !thisWorld.equals(otherWorld)) return false; - if (other.getServer().isPresent() == this.getServer().isPresent()) { - if (other.getServer().isPresent()) { - if (!other.getServer().get().equalsIgnoreCase(this.getServer().get())) { - return false; - } - } - } else { - return false; - } - - if (other.getWorld().isPresent() == this.getWorld().isPresent()) { - if (other.getWorld().isPresent()) { - if (!other.getWorld().get().equalsIgnoreCase(this.getWorld().get())) { - return false; - } - } - } else { - return false; - } - - return other.getContexts().equals(this.getContexts()); + final long otherExpireAt = other.isTemporary() ? other.getExpiryUnixTime() : 0L; + return this.expireAt == otherExpireAt && this.getContexts().equals(other.getContexts()); } + @SuppressWarnings("StringEquality") @Override public boolean almostEquals(Node other) { - if (!other.getPermission().equalsIgnoreCase(this.getPermission())) { - return false; - } + if (this.permission != other.getPermission()) return false; + if (this.override != other.isOverride()) return false; - if (other.isTemporary() != this.isTemporary()) { + final String thisServer = this.server; + final String otherServer = other.getServer().orElse(null); + if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false; - } - if (other.getServer().isPresent() == this.getServer().isPresent()) { - if (other.getServer().isPresent()) { - if (!other.getServer().get().equalsIgnoreCase(this.getServer().get())) { - return false; - } - } - } else { - return false; - } + final String thisWorld = this.world; + final String otherWorld = other.getWorld().orElse(null); + return (thisWorld == null ? otherWorld == null : thisWorld.equals(otherWorld)) && + this.isTemporary() == other.isTemporary() && + this.getContexts().equals(other.getContexts()); - if (other.getWorld().isPresent() == this.getWorld().isPresent()) { - if (other.getWorld().isPresent()) { - if (!other.getWorld().get().equalsIgnoreCase(this.getWorld().get())) { - return false; - } - } - } else { - return false; - } - - return other.getContexts().equals(this.getContexts()); } + @SuppressWarnings("StringEquality") @Override public boolean equalsIgnoringValueOrTemp(Node other) { - if (!other.getPermission().equalsIgnoreCase(this.getPermission())) { - return false; - } + if (this.permission != other.getPermission()) return false; + if (this.override != other.isOverride()) return false; - if (other.getServer().isPresent() == this.getServer().isPresent()) { - if (other.getServer().isPresent()) { - if (!other.getServer().get().equalsIgnoreCase(this.getServer().get())) { - return false; - } - } - } else { + final String thisServer = this.server; + final String otherServer = other.getServer().orElse(null); + if (thisServer == null ? otherServer != null : !thisServer.equals(otherServer)) return false; - } - if (other.getWorld().isPresent() == this.getWorld().isPresent()) { - if (other.getWorld().isPresent()) { - if (!other.getWorld().get().equalsIgnoreCase(this.getWorld().get())) { - return false; - } - } - } else { - return false; - } - - return other.getContexts().equals(this.getContexts()); + final String thisWorld = this.world; + final String otherWorld = other.getWorld().orElse(null); + return (thisWorld == null ? otherWorld == null : thisWorld.equals(otherWorld)) && + this.getContexts().equals(other.getContexts()); } @Override @@ -603,39 +589,33 @@ public final class ImmutableNode implements Node { } for (String s : expandedThisStr) { - if (p.matcher(s).matches()) { - return true; - } + if (p.matcher(s).matches()) return true; } return false; } if (thisStr.toLowerCase().startsWith("r=") && applyRegex) { Pattern p = PatternCache.compile(thisStr.substring(2)); - if (p == null) { - return false; - } + if (p == null) return false; for (String s : expandedStr) { - if (p.matcher(s).matches()) { - return true; - } + if (p.matcher(s).matches()) return true; } return false; } - if (expandedStr.size() <= 1 && expandedThisStr.size() <= 1) { - return false; - } + if (expandedStr.size() <= 1 && expandedThisStr.size() <= 1) return false; for (String t : expandedThisStr) { for (String s : expandedStr) { - if (t.equalsIgnoreCase(s)) { - return true; - } + if (t.equalsIgnoreCase(s)) return true; } } return false; } + private static String internString(String s) { + return s == null ? null : s.intern(); + } + } diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeBuilder.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeBuilder.java index 683edf99a..fc2359d3e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeBuilder.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeBuilder.java @@ -82,7 +82,7 @@ class NodeBuilder implements Node.Builder { NodeBuilder(Node other) { this.permission = other.getPermission(); - this.value = other.getValue(); + this.value = other.getValuePrimitive(); this.override = other.isOverride(); this.server = other.getServer().orElse(null); this.world = other.getWorld().orElse(null); diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java index f8c8797fc..f1e3a3b79 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeFactory.java @@ -151,7 +151,7 @@ public class NodeFactory { return appendContextToCommand(sb, node).toString(); } - if (node.getValue() && (node.isPrefix() || node.isSuffix())) { + if (node.getValuePrimitive() && (node.isPrefix() || node.isSuffix())) { ChatMetaType type = node.isPrefix() ? ChatMetaType.PREFIX : ChatMetaType.SUFFIX; String typeName = type.name().toLowerCase(); @@ -171,7 +171,7 @@ public class NodeFactory { return appendContextToCommand(sb, node).toString(); } - if (node.getValue() && node.isMeta()) { + if (node.getValuePrimitive() && node.isMeta()) { sb.append(node.isTemporary() ? (set ? "meta settemp " : "meta unsettemp ") : (set ? "meta set " : "meta unset ")); if (node.getMeta().getKey().contains(" ")) { @@ -204,7 +204,7 @@ public class NodeFactory { sb.append(node.getPermission()); } if (set) { - sb.append(" ").append(node.getValue()); + sb.append(" ").append(node.getValuePrimitive()); if (node.isTemporary()) { sb.append(" ").append(node.getExpiryUnixTime()); diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeHeldPermission.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeHeldPermission.java index 09ad28318..5e81f2cd0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeHeldPermission.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeHeldPermission.java @@ -33,6 +33,7 @@ import com.google.common.collect.Multimap; import me.lucko.luckperms.api.HeldPermission; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.context.ContextSet; import java.util.Optional; import java.util.OptionalLong; @@ -55,7 +56,7 @@ public final class NodeHeldPermission implements HeldPermission { @Override public boolean getValue() { - return node.getValue(); + return node.getValuePrimitive(); } @Override @@ -78,6 +79,11 @@ public final class NodeHeldPermission implements HeldPermission { return node.getContexts().toMultimap(); } + @Override + public ContextSet getContexts() { + return node.getContexts(); + } + @Override public Node asNode() { return node; diff --git a/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java b/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java index 19644f521..c2293923c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/NodeModel.java @@ -64,7 +64,7 @@ public final class NodeModel { public static NodeModel fromNode(Node node) { return NodeModel.of( node.getPermission(), - node.getValue(), + node.getValuePrimitive(), node.getServer().orElse("global"), node.getWorld().orElse("global"), node.isTemporary() ? node.getExpiryUnixTime() : 0L, diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java index 4e1fedace..c63bf45bc 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/LuckPermsPlugin.java @@ -220,12 +220,14 @@ public interface LuckPermsPlugin { /** * Gets the name or "brand" of the running platform + * * @return the server brand */ String getServerName(); /** * Gets the version of the running platform + * * @return the server version */ String getServerVersion(); diff --git a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java index 7215786fa..3e95ee937 100644 --- a/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/primarygroup/ParentsByWeightHolder.java @@ -57,7 +57,7 @@ public class ParentsByWeightHolder extends StoredHolder { List groups = new ArrayList<>(); for (Node node : user.filterNodes(contextSet)) { - if (!node.getValue() || !node.isGroupNode()) { + if (!node.getValuePrimitive() || !node.isGroupNode()) { continue; } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java b/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java index df3a5a680..a9ace9186 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/backing/mongodb/MongoDBBacking.java @@ -755,7 +755,7 @@ public class MongoDBBacking extends AbstractBacking { public static Map exportToLegacy(Iterable nodes) { Map m = new HashMap<>(); for (Node node : nodes) { - m.put(node.toSerializedNode(), node.getValue()); + m.put(node.toSerializedNode(), node.getValuePrimitive()); } return m; } diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java index 123d425b8..76f0a30d2 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/TreeView.java @@ -31,8 +31,9 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import me.lucko.luckperms.api.Tristate; -import me.lucko.luckperms.api.caching.PermissionData; +import me.lucko.luckperms.common.caching.PermissionCache; import me.lucko.luckperms.common.utils.PasteUtils; +import me.lucko.luckperms.common.verbose.CheckOrigin; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -194,7 +195,7 @@ public class TreeView { * @return the url, or null * @see PasteUtils#paste(String, List) */ - public String uploadPasteData(String version, String username, PermissionData checker) { + public String uploadPasteData(String version, String username, PermissionCache checker) { // only paste if there is actually data here if (!hasData()) { throw new IllegalStateException(); @@ -213,7 +214,7 @@ public class TreeView { for (Map.Entry e : ret) { // lookup a permission value for the node - Tristate tristate = checker.getPermissionValue(e.getValue()); + Tristate tristate = checker.getPermissionValue(e.getValue(), CheckOrigin.INTERNAL); // append the data to the paste builder.add(getTristateDiffPrefix(tristate) + e.getKey() + e.getValue()); diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java b/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java index 8fb0acfbd..70ff83451 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/LoginHelper.java @@ -41,7 +41,7 @@ import java.util.concurrent.CompletableFuture; @UtilityClass public class LoginHelper { - public static void loadUser(LuckPermsPlugin plugin, UUID u, String username, boolean joinUuidSave) { + public static User loadUser(LuckPermsPlugin plugin, UUID u, String username, boolean joinUuidSave) { final long startTime = System.currentTimeMillis(); final UuidCache cache = plugin.getUuidCache(); @@ -97,6 +97,8 @@ public class LoginHelper { if (time >= 1000) { plugin.getLog().warn("Processing login for " + username + " took " + time + "ms."); } + + return user; } public static void refreshPlayer(LuckPermsPlugin plugin, UUID uuid) { diff --git a/common/src/main/java/me/lucko/luckperms/common/utils/TextUtils.java b/common/src/main/java/me/lucko/luckperms/common/utils/TextUtils.java index 876c56e62..c99831a5f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/utils/TextUtils.java +++ b/common/src/main/java/me/lucko/luckperms/common/utils/TextUtils.java @@ -33,13 +33,18 @@ import net.kyori.text.serializer.ComponentSerializers; import java.util.Arrays; import java.util.stream.Collectors; +import java.util.stream.Stream; @SuppressWarnings("deprecation") @UtilityClass public class TextUtils { public String joinNewline(String... strings) { - return Arrays.stream(strings).collect(Collectors.joining("\n")); + return joinNewline(Arrays.stream(strings)); + } + + public String joinNewline(Stream strings) { + return strings.collect(Collectors.joining("\n")); } public TextComponent fromLegacy(String input, char character) { diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java index e0705ddd9..e394ad78c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckData.java @@ -29,6 +29,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import me.lucko.luckperms.api.Tristate; +import me.lucko.luckperms.api.context.ImmutableContextSet; /** * Holds the data from a permission check @@ -37,11 +38,26 @@ import me.lucko.luckperms.api.Tristate; @AllArgsConstructor public class CheckData { + /** + * The origin of the check + */ + private final CheckOrigin checkOrigin; + /** * The name of the entity which was checked */ private final String checkTarget; + /** + * The contexts where the check took place + */ + private final ImmutableContextSet checkContext; + + /** + * The stack trace when the check took place + */ + private final StackTraceElement[] checkTrace; + /** * The permission which was checked for */ diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/CheckOrigin.java b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckOrigin.java new file mode 100644 index 000000000..6108fdfa0 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/CheckOrigin.java @@ -0,0 +1,60 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.verbose; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Represents the origin of a permission check + */ +@Getter +@AllArgsConstructor +public enum CheckOrigin { + + /** + * Indicates the check was caused by a 'hasPermission' check on the platform + */ + PLATFORM_PERMISSION_CHECK('C'), + + /** + * Indicates the check was caused by a 'hasPermissionSet' type check on the platform + */ + PLATFORM_LOOKUP_CHECK('L'), + + /** + * Indicates the check was caused by an API call + */ + API('A'), + + /** + * Indicates the check was caused by a LuckPerms internal + */ + INTERNAL('I'); + + private final char code; + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java index f08488394..6cfb10650 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseHandler.java @@ -28,6 +28,7 @@ package me.lucko.luckperms.common.verbose; import lombok.Setter; import me.lucko.luckperms.api.Tristate; +import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.common.commands.sender.Sender; import java.util.Map; @@ -70,18 +71,23 @@ public class VerboseHandler implements Runnable { *

The check data is added to a queue to be processed later, to avoid blocking * the main thread each time a permission check is made.

* + * @param checkOrigin the origin of the check * @param checkTarget the target of the permission check + * @param checkContext the contexts where the check occurred * @param permission the permission which was checked for * @param result the result of the permission check */ - public void offerCheckData(String checkTarget, String permission, Tristate result) { + public void offerCheckData(CheckOrigin checkOrigin, String checkTarget, ContextSet checkContext, String permission, Tristate result) { // don't bother even processing the check if there are no listeners registered if (!listening) { return; } + //noinspection ThrowableNotThrown + StackTraceElement[] trace = new Exception().getStackTrace(); + // add the check data to a queue to be processed later. - queue.offer(new CheckData(checkTarget, permission, result)); + queue.offer(new CheckData(checkOrigin, checkTarget, checkContext.makeImmutable(), trace, permission, result)); } /** diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java index b6e333673..b0e76c092 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java @@ -33,9 +33,15 @@ import com.google.common.collect.Maps; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.common.commands.sender.Sender; +import me.lucko.luckperms.common.commands.utils.Util; +import me.lucko.luckperms.common.constants.Constants; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.utils.DateUtil; import me.lucko.luckperms.common.utils.PasteUtils; +import me.lucko.luckperms.common.utils.TextUtils; + +import net.kyori.text.TextComponent; +import net.kyori.text.event.HoverEvent; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -55,6 +61,9 @@ public class VerboseListener { // how much data should we store before stopping. private static final int DATA_TRUNCATION = 10000; + // how many traces should we add + private static final int TRACE_DATA_TRUNCATION = 250; + // the time when the listener was first registered private final long startTime = System.currentTimeMillis(); @@ -98,7 +107,64 @@ public class VerboseListener { } if (notify) { - Message.VERBOSE_LOG.send(notifiedSender, "&a" + data.getCheckTarget() + "&7 -- &a" + data.getPermission() + "&7 -- " + getTristateColor(data.getResult()) + data.getResult().name().toLowerCase() + ""); + StringBuilder msgContent = new StringBuilder(); + msgContent.append("&a") + .append(data.getCheckTarget()); + + if (notifiedSender.isConsole()) { + msgContent.append("&7 - &8[&2") + .append(data.getCheckOrigin().getCode()) + .append("&8]"); + } + + msgContent.append("&7 - &a") + .append(data.getPermission()) + .append("&7 - ") + .append(getTristateColor(data.getResult())) + .append(data.getResult().name().toLowerCase()); + + if (notifiedSender.isConsole()) { + msgContent.append("&7 - ").append(Util.contextSetToString(data.getCheckContext())); + } + + if (notifiedSender.isConsole()) { + Message.VERBOSE_LOG.send(notifiedSender, msgContent.toString()); + } else { + TextComponent textComponent = TextUtils.fromLegacy(Message.VERBOSE_LOG.asString(notifiedSender.getPlatform().getLocaleManager(), msgContent.toString())); + List hover = new ArrayList<>(); + hover.add("&bOrigin: &2" + data.getCheckOrigin().name()); + hover.add("&bContext: &r" + Util.contextSetToString(data.getCheckContext())); + hover.add("&bTrace: &r"); + + StackTraceElement[] checkTrace = data.getCheckTrace(); + + // how many lines have been printed + int count = 0; + // if we're printing elements yet + boolean printing = false; + + for (StackTraceElement e : checkTrace) { + // start printing when we escape LP internals code + if (!printing && !e.getClassName().startsWith("me.lucko.luckperms.")) { + printing = true; + } + + if (!printing) continue; + if (count >= 15) break; + + hover.add("&7" + e.getClassName() + "." + e.getMethodName() + (e.getLineNumber() >= 0 ? ":" + e.getLineNumber() : "")); + count++; + } + + if (checkTrace.length > 15) { + int remain = checkTrace.length - 15; + hover.add("&f... and " + remain + " more"); + } + + HoverEvent e = new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextUtils.fromLegacy(TextUtils.joinNewline(hover.stream()), Constants.FORMAT_CHAR)); + TextComponent msg = textComponent.toBuilder().applyDeep(comp -> comp.hoverEvent(e)).build(); + notifiedSender.sendMessage(msg); + } } } @@ -108,7 +174,7 @@ public class VerboseListener { * @return the url * @see PasteUtils#paste(String, List) */ - public String uploadPasteData() { + public String uploadPasteData(boolean showTraces) { long now = System.currentTimeMillis(); String startDate = DATE_FORMAT.format(new Date(startTime)); String endDate = DATE_FORMAT.format(new Date(now)); @@ -132,9 +198,10 @@ public class VerboseListener { .add("| Start Time | " + startDate + " |") .add("| End Time | " + endDate + " |") .add("| Duration | " + duration +" |") - .add("| Count | **" + matchedCounter.get() + "** / " + counter + " |") + .add("| Count | **" + matchedCounter.get() + "** / " + counter.get() + " |") .add("| User | " + notifiedSender.getName() + " |") .add("| Filter | " + filter + " |") + .add("| Include traces | " + showTraces + " |") .add(""); if (matchedCounter.get() > results.size()) { @@ -142,8 +209,14 @@ public class VerboseListener { prettyOutput.add(""); } + if (showTraces && results.size() > TRACE_DATA_TRUNCATION) { + prettyOutput.add("**WARN:** Result set exceeded size of " + TRACE_DATA_TRUNCATION + ". The traced output below was truncated to " + TRACE_DATA_TRUNCATION + " entries. "); + prettyOutput.add("Either refine the query using a more specific filter, or disable tracing by adding '--slim' to the end of the paste command."); + prettyOutput.add(""); + } + prettyOutput.add("### Output") - .add("Format: `` `` ``") + .add("Format: `` `` `` ``") .add("") .add("___") .add(""); @@ -151,8 +224,49 @@ public class VerboseListener { ImmutableList.Builder csvOutput = ImmutableList.builder() .add("User,Permission,Result"); + AtomicInteger printedCount = new AtomicInteger(0); + results.forEach(c -> { - prettyOutput.add("`" + c.getCheckTarget() + "` - " + c.getPermission() + " - **" + c.getResult().toString() + "** "); + if (!showTraces) { + prettyOutput.add("`" + c.getCheckTarget() + "` - " + c.getPermission() + " - " + getTristateSymbol(c.getResult()) + " "); + } else if (printedCount.incrementAndGet() > TRACE_DATA_TRUNCATION) { + prettyOutput.add("
" + c.getCheckTarget() + " - " + c.getPermission() + " - " + getTristateSymbol(c.getResult())); + } else { + prettyOutput.add("
" + c.getCheckTarget() + " - " + c.getPermission() + " - " + getTristateSymbol(c.getResult()) + "

"); + + prettyOutput.add("
Origin: " + c.getCheckOrigin().name() + ""); + prettyOutput.add("
Context: " + Util.stripColor(Util.contextSetToString(c.getCheckContext())) + ""); + prettyOutput.add("
Trace:

");
+
+                // add trace
+                StackTraceElement[] checkTrace = c.getCheckTrace();
+
+                // how many lines have been printed
+                int count = 0;
+                // if we're printing elements yet
+                boolean printing = false;
+
+                for (StackTraceElement e : checkTrace) {
+                    // start printing when we escape LP internals code
+                    if (!printing && !e.getClassName().startsWith("me.lucko.luckperms.")) {
+                        printing = true;
+                    }
+
+                    if (!printing) continue;
+                    if (count >= 30) break;
+
+                    prettyOutput.add(e.getClassName() + "." + e.getMethodName() + (e.getLineNumber() >= 0 ? ":" + e.getLineNumber() : ""));
+                    count++;
+                }
+
+                if (checkTrace.length > 30) {
+                    int remain = checkTrace.length - 30;
+                    prettyOutput.add("... and " + remain + " more");
+                }
+
+                prettyOutput.add("

"); + } + csvOutput.add(escapeCommas(c.getCheckTarget()) + "," + escapeCommas(c.getPermission()) + "," + c.getResult().name().toLowerCase()); }); results.clear(); @@ -180,4 +294,15 @@ public class VerboseListener { } } + private static String getTristateSymbol(Tristate tristate) { + switch (tristate) { + case TRUE: + return "✔️"; + case FALSE: + return "❌"; + default: + return "❔"; + } + } + } diff --git a/pom.xml b/pom.xml index a2cea2a78..8a39b3664 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ me.lucko.luckperms luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT api @@ -47,7 +47,7 @@ true - 3.3 + 3.4 ${git.closest.tag.commit.count} diff --git a/sponge/pom.xml b/sponge/pom.xml index 6ab38f934..e933f41de 100644 --- a/sponge/pom.xml +++ b/sponge/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT 4.0.0 diff --git a/sponge/sponge-service-api6/pom.xml b/sponge/sponge-service-api6/pom.xml index 0273ebc02..215a31556 100644 --- a/sponge/sponge-service-api6/pom.xml +++ b/sponge/sponge-service-api6/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/sponge/sponge-service-api7/pom.xml b/sponge/sponge-service-api7/pom.xml index db343c8e1..d3b4749aa 100644 --- a/sponge/sponge-service-api7/pom.xml +++ b/sponge/sponge-service-api7/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT ../../pom.xml 4.0.0 @@ -54,7 +54,7 @@ org.spongepowered spongeapi - 7.0.0-20170716.103113-1 + 7.0.0-SNAPSHOT provided diff --git a/sponge/sponge-service/pom.xml b/sponge/sponge-service/pom.xml index 0bcede21c..bf0354044 100644 --- a/sponge/sponge-service/pom.xml +++ b/sponge/sponge-service/pom.xml @@ -5,7 +5,7 @@ luckperms me.lucko.luckperms - 3.3-SNAPSHOT + 3.4-SNAPSHOT ../../pom.xml 4.0.0 @@ -33,7 +33,7 @@ org.spongepowered spongeapi - 7.0.0-20170716.103113-1 + 7.0.0-SNAPSHOT provided true diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java index 68b086a4c..3bc1de858 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeListener.java @@ -72,6 +72,7 @@ public class SpongeListener { Listening on AFTER_PRE priority to allow plugins to modify username / UUID data here. (auth plugins) */ final GameProfile p = e.getProfile(); + final String username = p.getName().orElseThrow(() -> new RuntimeException("No username present for user " + p.getUniqueId())); if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { plugin.getLog().info("Processing auth event for " + p.getUniqueId() + " - " + p.getName()); @@ -107,7 +108,8 @@ public class SpongeListener { - creating a user instance in the UserManager for this connection. - setting up cached data. */ try { - LoginHelper.loadUser(plugin, p.getUniqueId(), p.getName().orElseThrow(() -> new RuntimeException("No username present for user " + p.getUniqueId())), false); + User user = LoginHelper.loadUser(plugin, p.getUniqueId(), username, false); + plugin.getApiProvider().getEventFactory().handleUserLoginProcess(p.getUniqueId(), username, user); } catch (Exception ex) { ex.printStackTrace(); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeCalculatorFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeCalculatorFactory.java index 5a113e545..2e09ad31f 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeCalculatorFactory.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeCalculatorFactory.java @@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableList; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.PermissionCalculator; +import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.processors.MapProcessor; @@ -70,7 +71,11 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory { processors.add(new DefaultsProcessor(plugin.getService(), contexts.getContexts().makeImmutable())); } - return registerCalculator(new PermissionCalculator(plugin, user.getFriendlyName(), processors.build())); + return registerCalculator(new PermissionCalculator( + plugin, + PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()), + processors.build() + )); } @Override @@ -79,7 +84,7 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory { ret.add("Map"); if (plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_IMPLICIT_WILDCARDS)) ret.add("SpongeWildcard"); if (plugin.getConfiguration().get(ConfigKeys.APPLYING_REGEX)) ret.add("Regex"); - if (plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) ret.add("Wildcards"); + if (plugin.getConfiguration().get(ConfigKeys.APPLYING_WILDCARDS)) ret.add("Wildcard"); if (plugin.getConfiguration().get(ConfigKeys.APPLY_SPONGE_DEFAULT_SUBJECTS)) ret.add("Defaults"); return ret.build(); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java index 0ef0088aa..d9fc9a9ef 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java @@ -196,8 +196,9 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection { } @Override - public void cleanup(User user) { + public boolean cleanup(User user) { // Do nothing - this instance uses other means in order to cleanup + return false; } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java index 38c77022c..5b3f7c1d7 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeGroup.java @@ -97,7 +97,7 @@ public class SpongeGroup extends Group { // TODO move this away from NodeTree Map permissions = getParent().getAllNodes(ExtractedContexts.generate(getPlugin().getService().calculateContexts(contexts))).stream() .map(LocalizedNode::getNode) - .collect(Collectors.toMap(Node::getPermission, Node::getValue)); + .collect(Collectors.toMap(Node::getPermission, Node::getValuePrimitive)); return NodeTree.of(permissions); }); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java index bb6144d72..3338a7081 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/model/SpongeUser.java @@ -34,6 +34,7 @@ import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.caching.MetaData; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.verbose.CheckOrigin; import me.lucko.luckperms.sponge.LPSpongePlugin; import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.LuckPermsSubjectData; @@ -129,7 +130,7 @@ public class SpongeUser extends User { @Override public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) { try (Timing ignored = plugin.getTimings().time(LPTiming.USER_GET_PERMISSION_VALUE)) { - return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission); + return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java index dc7dee632..1f7b91905 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java @@ -81,7 +81,7 @@ public class LuckPermsSubjectData implements LPSubjectData { for (Map.Entry> e : (enduring ? holder.getEnduringNodes() : holder.getTransientNodes()).asMap().entrySet()) { ImmutableMap.Builder results = ImmutableMap.builder(); for (Node n : e.getValue()) { - results.put(n.getPermission(), n.getValue()); + results.put(n.getPermission(), n.getValuePrimitive()); } perms.put(e.getKey(), results); } @@ -322,7 +322,7 @@ public class LuckPermsSubjectData implements LPSubjectData { Map minSuffixPriority = new HashMap<>(); for (Node n : enduring ? holder.getEnduringNodes().values() : holder.getTransientNodes().values()) { - if (!n.getValue()) continue; + if (!n.getValuePrimitive()) continue; if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue; ImmutableContextSet immutableContexts = n.getFullContexts().makeImmutable(); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java index 4ab02667f..d42d1177e 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java @@ -39,8 +39,10 @@ import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.calculators.PermissionCalculator; +import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata; import me.lucko.luckperms.common.processors.MapProcessor; import me.lucko.luckperms.common.processors.PermissionProcessor; +import me.lucko.luckperms.common.verbose.CheckOrigin; import me.lucko.luckperms.sponge.processors.SpongeWildcardProcessor; import me.lucko.luckperms.sponge.service.model.LPPermissionService; import me.lucko.luckperms.sponge.service.model.LPSubject; @@ -84,7 +86,7 @@ public class CalculatedSubjectData implements LPSubjectData { processors.add(new MapProcessor()); processors.add(new SpongeWildcardProcessor()); - CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(service.getPlugin(), calculatorDisplayName, processors.build())); + CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(service.getPlugin(), PermissionCalculatorMetadata.of(calculatorDisplayName, contexts), processors.build())); holder.setPermissions(flattenMap(contexts, permissions)); return holder; @@ -100,7 +102,7 @@ public class CalculatedSubjectData implements LPSubjectData { } public Tristate getPermissionValue(ContextSet contexts, String permission) { - return permissionCache.get(contexts).getCalculator().getPermissionValue(permission); + return permissionCache.get(contexts).getCalculator().getPermissionValue(permission, CheckOrigin.INTERNAL); } public void replacePermissions(Map> map) { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java index 60484511d..5bc7ec1ec 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java @@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableList; import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.buffers.BufferedRequest; +import me.lucko.luckperms.common.verbose.CheckOrigin; import me.lucko.luckperms.sponge.service.LuckPermsService; import me.lucko.luckperms.sponge.service.ProxyFactory; import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData; @@ -247,7 +248,7 @@ public class PersistedSubject implements LPSubject { public Tristate getPermissionValue(@NonNull ImmutableContextSet contexts, @NonNull String node) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) { Tristate t = permissionLookupCache.get(PermissionLookup.of(node, contexts)); - service.getPlugin().getVerboseHandler().offerCheckData("local:" + getParentCollection().getIdentifier() + "/" + identifier, node, t); + service.getPlugin().getVerboseHandler().offerCheckData(CheckOrigin.INTERNAL, "local:" + getParentCollection().getIdentifier() + "/" + identifier, contexts, node, t); return t; } }