From a115ff8ce2b70ebd00e1d84ecd464ff45d1e993b Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 8 Nov 2017 22:55:05 +0000 Subject: [PATCH] Final bits of API refactoring, add group data caches, fix issue with LPPermissionAttachment fake map injection --- .../java/me/lucko/luckperms/api/Group.java | 22 + .../lucko/luckperms/api/PermissionHolder.java | 10 + .../java/me/lucko/luckperms/api/User.java | 1 + .../luckperms/api/caching/CachedData.java | 404 ++++++++++++++++++ .../luckperms/api/caching/GroupData.java | 45 ++ .../lucko/luckperms/api/caching/UserData.java | 150 +------ .../api/event/group/GroupCacheLoadEvent.java | 57 +++ .../group/GroupDataRecalculateEvent.java | 57 +++ .../luckperms/bukkit/LPBukkitPlugin.java | 10 +- .../calculators/BukkitCalculatorFactory.java | 6 +- .../listeners/BukkitConnectionListener.java | 6 +- .../luckperms/bukkit/model/LPPermissible.java | 10 +- .../bukkit/model/LPPermissionAttachment.java | 19 +- .../bukkit/model/LPSubscriptionMap.java | 6 +- ...Injector.java => PermissibleInjector.java} | 2 +- .../luckperms/bukkit/vault/VaultChatHook.java | 4 +- .../bukkit/vault/VaultPermissionHook.java | 2 +- .../luckperms/bungee/BungeeConfigAdapter.java | 8 +- .../calculators/BungeeCalculatorFactory.java | 6 +- .../BungeePermissionCheckListener.java | 4 +- .../common/api/delegates/model/ApiGroup.java | 12 + .../delegates/model/ApiPermissionHolder.java | 6 + .../common/api/delegates/model/ApiUser.java | 2 +- .../luckperms/common/caching/GroupCache.java | 44 ++ .../luckperms/common/caching/HolderCache.java | 295 +++++++++++++ .../luckperms/common/caching/MetaCache.java | 2 +- .../common/caching/PermissionCache.java | 16 +- .../luckperms/common/caching/UserCache.java | 188 +------- .../common/calculators/CalculatorFactory.java | 5 +- .../commands/impl/misc/CheckCommand.java | 2 +- .../commands/impl/misc/TreeCommand.java | 2 +- .../common/commands/impl/user/UserInfo.java | 2 +- .../luckperms/common/event/EventFactory.java | 13 + .../event/impl/EventGroupCacheLoad.java | 45 ++ .../event/impl/EventGroupDataRecalculate.java | 45 ++ .../common/managers/GenericUserManager.java | 2 +- .../lucko/luckperms/common/model/Group.java | 40 ++ .../common/model/PermissionHolder.java | 8 + .../me/lucko/luckperms/common/model/User.java | 38 +- .../storage/dao/file/ConfigurateDao.java | 3 + .../common/storage/dao/mongodb/MongoDao.java | 22 +- .../common/storage/dao/sql/SqlDao.java | 9 +- .../common/tasks/CacheHousekeepingTask.java | 6 +- .../luckperms/common/utils/LoginHelper.java | 6 - .../service/model/LPPermissionService.java | 4 - .../calculators/SpongeCalculatorFactory.java | 6 +- .../listeners/SpongeConnectionListener.java | 6 - .../luckperms/sponge/model/SpongeGroup.java | 147 ++----- .../luckperms/sponge/model/SpongeUser.java | 18 +- .../sponge/service/LuckPermsService.java | 7 - .../sponge/service/LuckPermsSubjectData.java | 16 +- 51 files changed, 1267 insertions(+), 579 deletions(-) create mode 100644 api/src/main/java/me/lucko/luckperms/api/caching/CachedData.java create mode 100644 api/src/main/java/me/lucko/luckperms/api/caching/GroupData.java create mode 100644 api/src/main/java/me/lucko/luckperms/api/event/group/GroupCacheLoadEvent.java create mode 100644 api/src/main/java/me/lucko/luckperms/api/event/group/GroupDataRecalculateEvent.java rename bukkit/src/main/java/me/lucko/luckperms/bukkit/model/{Injector.java => PermissibleInjector.java} (99%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/caching/GroupCache.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/caching/HolderCache.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/event/impl/EventGroupCacheLoad.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/event/impl/EventGroupDataRecalculate.java 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 2e6bba30c..55f4f1d9f 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Group.java +++ b/api/src/main/java/me/lucko/luckperms/api/Group.java @@ -25,7 +25,10 @@ package me.lucko.luckperms.api; +import me.lucko.luckperms.api.caching.GroupData; + import java.util.OptionalInt; +import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; @@ -51,4 +54,23 @@ public interface Group extends PermissionHolder { @Nonnull OptionalInt getWeight(); + /** + * Gets the groups's {@link GroupData} cache. + * + * @return the groups cached data. + * @since 4.0 + */ + @Nonnull + @Override + GroupData getCachedData(); + + /** + * Refreshes and applies any changes to the cached group data. + * + * @return the task future + * @since 4.0 + */ + @Nonnull + CompletableFuture refreshCachedData(); + } 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 ae9e745a5..eebda20d7 100644 --- a/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java +++ b/api/src/main/java/me/lucko/luckperms/api/PermissionHolder.java @@ -28,6 +28,7 @@ package me.lucko.luckperms.api; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Multimap; +import me.lucko.luckperms.api.caching.CachedData; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; @@ -70,6 +71,15 @@ public interface PermissionHolder { @Nonnull String getFriendlyName(); + /** + * Gets the holders {@link CachedData} cache. + * + * @return the holders cached data. + * @since 3.2 + */ + @Nonnull + CachedData getCachedData(); + /** * Gets the backing multimap containing every permission this holder has. * 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 95b01a8c6..9a2f51447 100644 --- a/api/src/main/java/me/lucko/luckperms/api/User.java +++ b/api/src/main/java/me/lucko/luckperms/api/User.java @@ -84,6 +84,7 @@ public interface User extends PermissionHolder { * @since 3.2 */ @Nonnull + @Override UserData getCachedData(); /** diff --git a/api/src/main/java/me/lucko/luckperms/api/caching/CachedData.java b/api/src/main/java/me/lucko/luckperms/api/caching/CachedData.java new file mode 100644 index 000000000..3c81b50cf --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/caching/CachedData.java @@ -0,0 +1,404 @@ +/* + * 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.caching; + +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.PermissionHolder; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import javax.annotation.Nonnull; + +/** + * Holds cached permission and meta lookup data for a {@link PermissionHolder}. + * + *

All calls will account for inheritance, as well as any default data provided by + * the platform. This calls are heavily cached and are therefore fast.

+ * + *

For meta, both methods accepting {@link Contexts} and {@link MetaContexts} are provided. The only difference is that + * the latter allows you to define how the meta stack should be structured internally. Where {@link Contexts} are passed, the + * values from the configuration are used.

+ * + * @since 4.0 + */ +public interface CachedData { + + /** + * Gets PermissionData from the cache, given a specified context. + * + * @param contexts the contexts to get the permission data in + * @return a permission data instance + * @throws NullPointerException if contexts is null + */ + @Nonnull + PermissionData getPermissionData(@Nonnull Contexts contexts); + + /** + * Gets MetaData from the cache, given a specified context. + * + * @param contexts the contexts to get the permission data in + * @return a meta data instance + * @throws NullPointerException if contexts is null + * @since 3.2 + */ + @Nonnull + MetaData getMetaData(@Nonnull MetaContexts contexts); + + /** + * Gets MetaData from the cache, given a specified context. + * + * @param contexts the contexts to get the permission data in + * @return a meta data instance + * @throws NullPointerException if contexts is null + */ + @Nonnull + MetaData getMetaData(@Nonnull Contexts contexts); + + /** + * Calculates permission data, bypassing the cache. + * + *

The result of this operation is calculated each time the method is called. + * The result is not added to the internal cache.

+ * + *

It is therefore highly recommended to use {@link #getPermissionData(Contexts)} instead.

+ * + *

The use cases of this method are more around constructing one-time + * instances of {@link PermissionData}, without adding the result to the cache.

+ * + * @param contexts the contexts to get permission data in + * @return a permission data instance + * @throws NullPointerException if contexts is null + */ + @Nonnull + PermissionData calculatePermissions(@Nonnull Contexts contexts); + + /** + * Calculates meta data, bypassing the cache. + * + *

The result of this operation is calculated each time the method is called. + * The result is not added to the internal cache.

+ * + *

It is therefore highly recommended to use {@link #getMetaData(MetaContexts)} instead.

+ * + *

The use cases of this method are more around constructing one-time + * instances of {@link MetaData}, without adding the result to the cache.

+ * + * @param contexts the contexts to get meta data in + * @return a meta data instance + * @throws NullPointerException if contexts is null + * @since 3.2 + */ + @Nonnull + MetaData calculateMeta(@Nonnull MetaContexts contexts); + + /** + * Calculates meta data, bypassing the cache. + * + *

The result of this operation is calculated each time the method is called. + * The result is not added to the internal cache.

+ * + *

It is therefore highly recommended to use {@link #getMetaData(Contexts)} instead.

+ * + *

The use cases of this method are more around constructing one-time + * instances of {@link MetaData}, without adding the result to the cache.

+ * + * @param contexts the contexts to get meta data in + * @return a meta data instance + * @throws NullPointerException if contexts is null + */ + @Nonnull + MetaData calculateMeta(@Nonnull Contexts contexts); + + /** + * (Re)calculates permission data for a given context. + * + *

This method returns immediately in all cases. The (re)calculation is + * performed asynchronously and applied to the cache in the background.

+ * + *

If there was a previous {@link PermissionData} instance associated with + * the given {@link Contexts}, then that instance will continue to be returned by + * {@link #getPermissionData(Contexts)} until the recalculation is completed.

+ * + *

If there was no value calculated and cached prior to the call of this + * method, then one will be calculated.

+ * + * @param contexts the contexts to recalculate in. + * @throws NullPointerException if contexts is null + */ + void recalculatePermissions(@Nonnull Contexts contexts); + + /** + * (Re)calculates meta data for a given context. + * + *

This method returns immediately in all cases. The (re)calculation is + * performed asynchronously and applied to the cache in the background.

+ * + *

If there was a previous {@link MetaData} instance associated with + * the given {@link MetaContexts}, then that instance will continue to be returned by + * {@link #getMetaData(MetaContexts)} until the recalculation is completed.

+ * + *

If there was no value calculated and cached prior to the call of this + * method, then one will be calculated.

+ * + * @param contexts the contexts to recalculate in. + * @throws NullPointerException if contexts is null + * @since 3.2 + */ + void recalculateMeta(@Nonnull MetaContexts contexts); + + /** + * (Re)calculates meta data for a given context. + * + *

This method returns immediately in all cases. The (re)calculation is + * performed asynchronously and applied to the cache in the background.

+ * + *

If there was a previous {@link MetaData} instance associated with + * the given {@link Contexts}, then that instance will continue to be returned by + * {@link #getMetaData(Contexts)} until the recalculation is completed.

+ * + *

If there was no value calculated and cached prior to the call of this + * method, then one will be calculated.

+ * + * @param contexts the contexts to recalculate in. + * @throws NullPointerException if contexts is null + */ + void recalculateMeta(@Nonnull Contexts contexts); + + /** + * (Re)loads permission data for a given context. + * + *

Unlike {@link #recalculatePermissions(Contexts)}, this method immediately + * invalidates any previous {@link PermissionData} values contained within the cache, + * and then schedules a task to reload a new {@link PermissionData} instance to + * replace the one which was invalidated.

+ * + *

The invalidation happens immediately during the execution of this method. + * The result of the re-computation encapsulated by the future.

+ * + *

Subsequent calls to {@link #getPermissionData(Contexts)} will block until + * the result of this operation is complete.

+ * + *

If there was no value calculated and cached prior to the call of this + * method, then one will be calculated.

+ * + *

This method returns a Future so users can optionally choose to wait + * until the recalculation has been performed.

+ * + * @param contexts the contexts to reload in. + * @throws NullPointerException if contexts is null + * @since 4.0 + */ + @Nonnull + CompletableFuture reloadPermissions(@Nonnull Contexts contexts); + + /** + * (Re)loads meta data for a given context. + * + *

Unlike {@link #recalculateMeta(MetaContexts)}, this method immediately + * invalidates any previous {@link MetaData} values contained within the cache, + * and then schedules a task to reload a new {@link MetaData} instance to + * replace the one which was invalidated.

+ * + *

The invalidation happens immediately during the execution of this method. + * The result of the re-computation encapsulated by the future.

+ * + *

Subsequent calls to {@link #getMetaData(MetaContexts)} will block until + * the result of this operation is complete.

+ * + *

If there was no value calculated and cached prior to the call of this + * method, then one will be calculated.

+ * + *

This method returns a Future so users can optionally choose to wait + * until the recalculation has been performed.

+ * + * @param contexts the contexts to reload in. + * @throws NullPointerException if contexts is null + * @since 4.0 + */ + @Nonnull + CompletableFuture reloadMeta(@Nonnull MetaContexts contexts); + + /** + * (Re)loads meta data for a given context. + * + *

Unlike {@link #recalculateMeta(Contexts)}, this method immediately + * invalidates any previous {@link MetaData} values contained within the cache, + * and then schedules a task to reload a new {@link MetaData} instance to + * replace the one which was invalidated.

+ * + *

The invalidation happens immediately during the execution of this method. + * The result of the re-computation encapsulated by the future.

+ * + *

Subsequent calls to {@link #getMetaData(Contexts)} will block until + * the result of this operation is complete.

+ * + *

If there was no value calculated and cached prior to the call of this + * method, then one will be calculated.

+ * + *

This method returns a Future so users can optionally choose to wait + * until the recalculation has been performed.

+ * + * @param contexts the contexts to reload in. + * @throws NullPointerException if contexts is null + * @since 4.0 + */ + @Nonnull + CompletableFuture reloadMeta(@Nonnull Contexts contexts); + + /** + * Recalculates permission data for all known contexts. + * + *

This method returns immediately. The recalculation is performed + * asynchronously and applied to the cache in the background.

+ * + *

The previous {@link PermissionData} instances will continue to be returned + * by {@link #getPermissionData(Contexts)} until the recalculation is completed.

+ */ + void recalculatePermissions(); + + /** + * Recalculates meta data for all known contexts. + * + *

This method returns immediately. The recalculation is performed + * asynchronously and applied to the cache in the background.

+ * + *

The previous {@link MetaData} instances will continue to be returned + * by {@link #getMetaData(MetaContexts)} and {@link #getMetaData(Contexts)} + * until the recalculation is completed.

+ */ + void recalculateMeta(); + + /** + * Reloads permission data for all known contexts. + * + *

Unlike {@link #recalculatePermissions()}, this method immediately + * invalidates all previous {@link PermissionData} values contained within the cache, + * and then schedules a task to reload a new {@link PermissionData} instances to + * replace the ones which were invalidated.

+ * + *

The invalidation happens immediately during the execution of this method. + * The result of the re-computation encapsulated by the future.

+ * + *

Subsequent calls to {@link #getPermissionData(Contexts)} will block until + * the result of this operation is complete.

+ * + *

This method returns a Future so users can optionally choose to wait + * until the recalculation has been performed.

+ * + * @since 4.0 + */ + @Nonnull + CompletableFuture reloadPermissions(); + + /** + * Reloads meta data for all known contexts. + * + *

Unlike {@link #recalculateMeta()}, this method immediately + * invalidates all previous {@link MetaData} values contained within the cache, + * and then schedules a task to reload a new {@link MetaData} instances to + * replace the ones which were invalidated.

+ * + *

The invalidation happens immediately during the execution of this method. + * The result of the re-computation encapsulated by the future.

+ * + *

Subsequent calls to {@link #getMetaData(MetaContexts)} and + * {@link #getMetaData(Contexts)} will block until the result of this operation + * is complete.

+ * + *

This method returns a Future so users can optionally choose to wait + * until the recalculation has been performed.

+ * + * @since 4.0 + */ + @Nonnull + CompletableFuture reloadMeta(); + + /** + * Pre-calculates and caches {@link PermissionData} and {@link MetaData} + * instances for the given contexts. + * + *

If the cache already contains a value for the given context, + * no action is taken.

+ * + *

This method blocks until the calculation is completed.

+ * + * @param contexts a set of contexts + * @throws NullPointerException if contexts is null + */ + default void preCalculate(@Nonnull Set contexts) { + contexts.forEach(this::preCalculate); + } + + /** + * Pre-calculates and caches {@link PermissionData} and {@link MetaData} + * instances for a given context. + * + *

If the cache already contains a value for the given context, + * no action is taken.

+ * + *

This method blocks until the calculation is completed.

+ * + * @param contexts the contexts to pre-calculate for + * @throws NullPointerException if contexts is null + */ + void preCalculate(@Nonnull Contexts contexts); + + /** + * Invalidates any cached {@link PermissionData} instances mapped to the given + * {@link Contexts}. + * + * @param contexts the contexts to invalidate for + * @since 4.0 + */ + void invalidatePermissions(@Nonnull Contexts contexts); + + /** + * Invalidates any cached {@link MetaData} instances mapped to the given + * {@link MetaContexts}. + * + * @param contexts the contexts to invalidate for + * @since 4.0 + */ + void invalidateMeta(@Nonnull MetaContexts contexts); + + /** + * Invalidates any cached {@link MetaData} instances mapped to the given + * {@link Contexts}. + * + * @param contexts the contexts to invalidate for + * @since 4.0 + */ + void invalidateMeta(@Nonnull Contexts contexts); + + /** + * Invalidates all of the underlying Permission calculators. + * + *

Can be called to allow for an update in defaults.

+ */ + void invalidatePermissionCalculators(); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/caching/GroupData.java b/api/src/main/java/me/lucko/luckperms/api/caching/GroupData.java new file mode 100644 index 000000000..968189c4a --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/caching/GroupData.java @@ -0,0 +1,45 @@ +/* + * 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.caching; + +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.Group; + +/** + * Holds cached permission and meta lookup data for a {@link Group}. + * + *

All calls will account for inheritance, as well as any default data provided + * by the platform. This calls are heavily cached and are therefore fast.

+ * + *

For meta, both methods accepting {@link Contexts} and {@link MetaContexts} are provided. The only difference is that + * the latter allows you to define how the meta stack should be structured internally. Where {@link Contexts} are passed, the + * values from the configuration are used.

+ * + * @since 4.0 + */ +public interface GroupData extends CachedData { + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/caching/UserData.java b/api/src/main/java/me/lucko/luckperms/api/caching/UserData.java index f8bd7f376..8de86dcb7 100644 --- a/api/src/main/java/me/lucko/luckperms/api/caching/UserData.java +++ b/api/src/main/java/me/lucko/luckperms/api/caching/UserData.java @@ -26,16 +26,13 @@ package me.lucko.luckperms.api.caching; import me.lucko.luckperms.api.Contexts; - -import java.util.Set; - -import javax.annotation.Nonnull; +import me.lucko.luckperms.api.User; /** - * Holds cached permission and meta lookup data for a {@link me.lucko.luckperms.api.User}. + * Holds cached permission and meta lookup data for a {@link User}. * - *

Data is only likely to be available for online users. All calls will account for inheritance, as well as any - * default data provided by the platform. This calls are heavily cached and are therefore fast.

+ *

All calls will account for inheritance, as well as any default data provided by + * the platform. This calls are heavily cached and are therefore fast.

* *

For meta, both methods accepting {@link Contexts} and {@link MetaContexts} are provided. The only difference is that * the latter allows you to define how the meta stack should be structured internally. Where {@link Contexts} are passed, the @@ -43,143 +40,6 @@ import javax.annotation.Nonnull; * * @since 2.13 */ -public interface UserData { - - /** - * Gets PermissionData from the cache, given a specified context. - * - *

If the data is not cached, it is calculated. Therefore, this call could be costly.

- * - * @param contexts the contexts to get the permission data in - * @return a permission data instance - * @throws NullPointerException if contexts is null - */ - @Nonnull - PermissionData getPermissionData(@Nonnull Contexts contexts); - - /** - * Gets MetaData from the cache, given a specified context. - * - *

If the data is not cached, it is calculated. Therefore, this call could be costly.

- * - * @param contexts the contexts to get the permission data in - * @return a meta data instance - * @throws NullPointerException if contexts is null - * @since 3.2 - */ - @Nonnull - MetaData getMetaData(@Nonnull MetaContexts contexts); - - /** - * Gets MetaData from the cache, given a specified context. - * - *

If the data is not cached, it is calculated. Therefore, this call could be costly.

- * - * @param contexts the contexts to get the permission data in - * @return a meta data instance - * @throws NullPointerException if contexts is null - */ - @Nonnull - MetaData getMetaData(@Nonnull Contexts contexts); - - /** - * Calculates permission data, bypassing the cache. - * - * @param contexts the contexts to get permission data in - * @return a permission data instance - * @throws NullPointerException if contexts is null - */ - @Nonnull - PermissionData calculatePermissions(@Nonnull Contexts contexts); - - /** - * Calculates meta data, bypassing the cache. - * - * @param contexts the contexts to get meta data in - * @return a meta data instance - * @throws NullPointerException if contexts is null - * @since 3.2 - */ - @Nonnull - MetaData calculateMeta(@Nonnull MetaContexts contexts); - - /** - * Calculates meta data, bypassing the cache. - * - * @param contexts the contexts to get meta data in - * @return a meta data instance - * @throws NullPointerException if contexts is null - */ - @Nonnull - MetaData calculateMeta(@Nonnull Contexts contexts); - - /** - * Calculates permission data and stores it in the cache. - * - *

If there is already data cached for the given contexts, and if the resultant output is different, - * the cached value is updated.

- * - * @param contexts the contexts to recalculate in. - * @throws NullPointerException if contexts is null - */ - void recalculatePermissions(@Nonnull Contexts contexts); - - /** - * Calculates meta data and stores it in the cache. - * - *

If there is already data cached for the given contexts, and if the resultant output is different, - * the cached value is updated.

- * - * @param contexts the contexts to recalculate in. - * @throws NullPointerException if contexts is null - * @since 3.2 - */ - void recalculateMeta(@Nonnull MetaContexts contexts); - - /** - * Calculates meta data and stores it in the cache. - * - *

If there is already data cached for the given contexts, and if the resultant output is different, - * the cached value is updated.

- * - * @param contexts the contexts to recalculate in. - * @throws NullPointerException if contexts is null - */ - void recalculateMeta(@Nonnull Contexts contexts); - - /** - * Calls {@link #recalculatePermissions(Contexts)} for all current loaded contexts - */ - void recalculatePermissions(); - - /** - * Calls {@link #recalculateMeta(Contexts)} for all current loaded contexts - */ - void recalculateMeta(); - - /** - * Calls {@link #preCalculate(Contexts)} for the given contexts - * - * @param contexts a set of contexts - * @throws NullPointerException if contexts is null - */ - void preCalculate(@Nonnull Set contexts); - - /** - * Ensures that PermissionData and MetaData is cached for a context. - * - *

If the cache does not contain any data for the context, it will be calculated and saved.

- * - * @param contexts the contexts to pre-calculate for - * @throws NullPointerException if contexts is null - */ - void preCalculate(@Nonnull Contexts contexts); - - /** - * Invalidates all of the underlying Permission calculators. - * - *

Can be called to allow for an update in defaults.

- */ - void invalidatePermissionCalculators(); +public interface UserData extends CachedData { } diff --git a/api/src/main/java/me/lucko/luckperms/api/event/group/GroupCacheLoadEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/group/GroupCacheLoadEvent.java new file mode 100644 index 000000000..8a7c86199 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/event/group/GroupCacheLoadEvent.java @@ -0,0 +1,57 @@ +/* + * 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.group; + +import me.lucko.luckperms.api.Group; +import me.lucko.luckperms.api.caching.GroupData; +import me.lucko.luckperms.api.event.LuckPermsEvent; + +import javax.annotation.Nonnull; + +/** + * Called when a groups {@link GroupData} is loaded. + * + * @since 4.0 + */ +public interface GroupCacheLoadEvent extends LuckPermsEvent { + + /** + * Gets the group whose data was loaded + * + * @return the group + */ + @Nonnull + Group getGroup(); + + /** + * Gets the data that was loaded + * + * @return the loaded data + */ + @Nonnull + GroupData getLoadedData(); + +} diff --git a/api/src/main/java/me/lucko/luckperms/api/event/group/GroupDataRecalculateEvent.java b/api/src/main/java/me/lucko/luckperms/api/event/group/GroupDataRecalculateEvent.java new file mode 100644 index 000000000..cf270b451 --- /dev/null +++ b/api/src/main/java/me/lucko/luckperms/api/event/group/GroupDataRecalculateEvent.java @@ -0,0 +1,57 @@ +/* + * 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.group; + +import me.lucko.luckperms.api.Group; +import me.lucko.luckperms.api.caching.GroupData; +import me.lucko.luckperms.api.event.LuckPermsEvent; + +import javax.annotation.Nonnull; + +/** + * Called when a groups cached data is refreshed + * + * @since 4.0 + */ +public interface GroupDataRecalculateEvent extends LuckPermsEvent { + + /** + * Gets the group whose data was recalculated + * + * @return the group + */ + @Nonnull + Group getGroup(); + + /** + * Gets the data that was recalculated + * + * @return the data + */ + @Nonnull + GroupData getData(); + +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java index dbfecd86d..1c6a47b43 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/LPBukkitPlugin.java @@ -36,8 +36,8 @@ import me.lucko.luckperms.bukkit.contexts.WorldCalculator; import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener; import me.lucko.luckperms.bukkit.listeners.BukkitPlatformListener; import me.lucko.luckperms.bukkit.messaging.BukkitMessagingFactory; -import me.lucko.luckperms.bukkit.model.Injector; import me.lucko.luckperms.bukkit.model.LPPermissible; +import me.lucko.luckperms.bukkit.model.PermissibleInjector; import me.lucko.luckperms.bukkit.model.SubscriptionMapInjector; import me.lucko.luckperms.bukkit.processors.BukkitProcessorsSetupTask; import me.lucko.luckperms.bukkit.processors.ChildPermissionProvider; @@ -305,7 +305,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { scheduler.doSync(() -> { try { LPPermissible lpPermissible = new LPPermissible(player, user, this); - Injector.inject(player, lpPermissible); + PermissibleInjector.inject(player, lpPermissible); } catch (Throwable t) { t.printStackTrace(); } @@ -335,7 +335,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { // uninject from players for (Player player : getServer().getOnlinePlayers()) { try { - Injector.unInject(player, false); + PermissibleInjector.unInject(player, false); } catch (Exception e) { e.printStackTrace(); } @@ -346,7 +346,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { final User user = getUserManager().getIfLoaded(getUuidCache().getUUID(player.getUniqueId())); if (user != null) { - user.getUserData().invalidateCaches(); + user.getCachedData().invalidateCaches(); getUserManager().unload(user); } } @@ -406,7 +406,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin { } if (getConfiguration().get(ConfigKeys.AUTO_OP)) { - Map backing = user.getUserData().getPermissionData(contextManager.getApplicableContexts(player)).getImmutableBacking(); + Map backing = user.getCachedData().getPermissionData(contextManager.getApplicableContexts(player)).getImmutableBacking(); boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false); player.setOp(op); } 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 5be4b649e..76ad1c4d3 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 @@ -37,7 +37,6 @@ 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; import me.lucko.luckperms.common.processors.PermissionProcessor; import me.lucko.luckperms.common.processors.RegexProcessor; @@ -50,7 +49,7 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory { private final LPBukkitPlugin plugin; @Override - public PermissionCalculator build(Contexts contexts, User user) { + public PermissionCalculator build(Contexts contexts, PermissionCalculatorMetadata metadata) { ImmutableList.Builder processors = ImmutableList.builder(); processors.add(new MapProcessor()); @@ -71,8 +70,7 @@ public class BukkitCalculatorFactory extends AbstractCalculatorFactory { processors.add(new DefaultsProcessor(contexts.isOp(), plugin.getDefaultsProvider())); } - PermissionCalculatorMetadata meta = PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()); - return registerCalculator(new PermissionCalculator(plugin, meta, processors.build())); + return registerCalculator(new PermissionCalculator(plugin, metadata, processors.build())); } @Override diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitConnectionListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitConnectionListener.java index bf49251b1..aa4298e6a 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitConnectionListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/listeners/BukkitConnectionListener.java @@ -28,8 +28,8 @@ package me.lucko.luckperms.bukkit.listeners; import lombok.RequiredArgsConstructor; import me.lucko.luckperms.bukkit.LPBukkitPlugin; -import me.lucko.luckperms.bukkit.model.Injector; import me.lucko.luckperms.bukkit.model.LPPermissible; +import me.lucko.luckperms.bukkit.model.PermissibleInjector; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.model.User; @@ -165,7 +165,7 @@ public class BukkitConnectionListener implements Listener { LPPermissible lpPermissible = new LPPermissible(player, user, plugin); // Inject into the player - Injector.inject(player, lpPermissible); + PermissibleInjector.inject(player, lpPermissible); } catch (Throwable t) { t.printStackTrace(); @@ -209,7 +209,7 @@ public class BukkitConnectionListener implements Listener { // Remove the custom permissible try { - Injector.unInject(player, true); + PermissibleInjector.unInject(player, true); } catch (Exception ex) { ex.printStackTrace(); } 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 0f9d21c67..652a59f32 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 @@ -96,13 +96,13 @@ public class LPPermissible extends PermissibleBase { @Override public boolean isPermissionSet(@NonNull String permission) { - Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); + Tristate ts = user.getCachedData().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(), CheckOrigin.PLATFORM_LOOKUP_CHECK); + Tristate ts = user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_LOOKUP_CHECK); if (ts != Tristate.UNDEFINED) { return true; } @@ -116,13 +116,13 @@ public class LPPermissible extends PermissibleBase { @Override public boolean hasPermission(@NonNull String permission) { - Tristate ts = user.getUserData().getPermissionData(calculateContexts()).getPermissionValue(permission, CheckOrigin.PLATFORM_PERMISSION_CHECK); + Tristate ts = user.getCachedData().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(), CheckOrigin.PLATFORM_PERMISSION_CHECK); + Tristate ts = user.getCachedData().getPermissionData(calculateContexts()).getPermissionValue(permission.getName(), CheckOrigin.PLATFORM_PERMISSION_CHECK); if (ts != Tristate.UNDEFINED) { return ts.asBoolean(); } @@ -164,7 +164,7 @@ public class LPPermissible extends PermissibleBase { public Set getEffectivePermissions() { Set perms = new HashSet<>(); perms.addAll( - user.getUserData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet().stream() + user.getCachedData().getPermissionData(calculateContexts()).getImmutableBacking().entrySet().stream() .map(e -> new PermissionAttachmentInfo(player, e.getKey(), null, e.getValue())) .collect(Collectors.toList()) ); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java index 82e8ca888..fb8391fca 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPPermissionAttachment.java @@ -36,7 +36,6 @@ import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.node.ImmutableTransientNode; import me.lucko.luckperms.common.node.NodeFactory; -import org.bukkit.permissions.PermissibleBase; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionRemovedExecutor; import org.bukkit.plugin.Plugin; @@ -64,7 +63,7 @@ public class LPPermissionAttachment extends PermissionAttachment { static { Field permissionAttachmentPermissionsField; try { - permissionAttachmentPermissionsField = PermissibleBase.class.getDeclaredField("permissions"); + permissionAttachmentPermissionsField = PermissionAttachment.class.getDeclaredField("permissions"); permissionAttachmentPermissionsField.setAccessible(true); } catch (NoSuchFieldException e) { throw new RuntimeException(e); @@ -131,9 +130,8 @@ public class LPPermissionAttachment extends PermissionAttachment { FakeBackingMap fakeMap = new FakeBackingMap(); try { - // what's this doing, ay? - // the field we need to modify is in the superclass - it's set to private - // so we have to use reflection to modify it. + // the field we need to modify is in the superclass - it has private + // and final modifiers so we have to use reflection to modify it. PERMISSION_ATTACHMENT_PERMISSIONS_FIELD.set(this, fakeMap); } catch (Exception e) { e.printStackTrace(); @@ -172,7 +170,7 @@ public class LPPermissionAttachment extends PermissionAttachment { // set the transient node User user = permissible.getUser(); if (user.setTransientPermission(transientNode).asBoolean()) { - user.getRefreshBuffer().request(); + user.reloadCachedData(); } } @@ -184,7 +182,7 @@ public class LPPermissionAttachment extends PermissionAttachment { // remove transient permissions from the holder which were added by this attachment & equal the permission User user = permissible.getUser(); if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this && n.getPermission().equals(name))) { - user.getRefreshBuffer().request(); + user.reloadCachedData(); } } @@ -192,7 +190,7 @@ public class LPPermissionAttachment extends PermissionAttachment { // remove all transient permissions added by this attachment User user = permissible.getUser(); if (user.removeIfTransient(n -> n instanceof ImmutableTransientNode && ((ImmutableTransientNode) n).getOwner() == this)) { - user.getRefreshBuffer().request(); + user.reloadCachedData(); } } @@ -228,7 +226,7 @@ public class LPPermissionAttachment extends PermissionAttachment { return; } - // if we're not hooked, thn don't actually apply the change + // if we're not hooked, then don't actually apply the change // it will get applied on hook - if that ever happens if (!hooked) { return; @@ -253,7 +251,7 @@ public class LPPermissionAttachment extends PermissionAttachment { return; } - // if we're not hooked, thn don't actually apply the change + // if we're not hooked, then don't actually apply the change // it will get applied on hook - if that ever happens if (!hooked) { return; @@ -296,7 +294,6 @@ public class LPPermissionAttachment extends PermissionAttachment { @Override public Boolean put(String key, Boolean value) { - // grab the previous result, so we can still satisfy the method signature of Map Boolean previous = perms.get(key); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPSubscriptionMap.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPSubscriptionMap.java index c8a917b58..57decba4f 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPSubscriptionMap.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/LPSubscriptionMap.java @@ -181,7 +181,7 @@ public class LPSubscriptionMap extends HashMap return result; } - // then try the map, if we haven't already + // then try the permissible, if we haven't already if (!isPlayer && key instanceof Permissible) { Permissible p = (Permissible) key; if (p.isPermissionSet(permission)) { @@ -195,8 +195,8 @@ public class LPSubscriptionMap extends HashMap @Override public boolean containsKey(Object key) { - // check the backing map, as well as if the permissible has the perm set - return backing.containsKey(key) || (key instanceof Permissible && ((Permissible) key).isPermissionSet(permission)); + // delegate through the get method + return get(key) != null; } @Override diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/Injector.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/PermissibleInjector.java similarity index 99% rename from bukkit/src/main/java/me/lucko/luckperms/bukkit/model/Injector.java rename to bukkit/src/main/java/me/lucko/luckperms/bukkit/model/PermissibleInjector.java index ff5b5ffe1..9902c61a6 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/Injector.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/model/PermissibleInjector.java @@ -43,7 +43,7 @@ import java.util.List; * checks made by plugins. */ @UtilityClass -public class Injector { +public class PermissibleInjector { /** * All permission checks made on standard Bukkit objects are effectively proxied to a 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 c6cd7fa4d..17e39c968 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 @@ -304,7 +304,7 @@ public class VaultChatHook extends Chat { world = perms.correctWorld(world); perms.log("Getting meta: '" + node + "' for user " + user.getFriendlyName() + " on world " + world + ", server " + perms.getServer()); - String ret = user.getUserData().getMetaData(perms.createContextForWorldLookup(perms.getPlugin().getPlayer(user), world)).getMeta().get(node); + String ret = user.getCachedData().getMetaData(perms.createContextForWorldLookup(perms.getPlugin().getPlayer(user), world)).getMeta().get(node); return ret != null ? ret : defaultValue; } @@ -316,7 +316,7 @@ public class VaultChatHook extends Chat { world = perms.correctWorld(world); perms.log("Getting " + type.name().toLowerCase() + " for user " + user.getFriendlyName() + " on world " + world + ", server " + perms.getServer()); - MetaData data = user.getUserData().getMetaData(perms.createContextForWorldLookup(perms.getPlugin().getPlayer(user), world)); + MetaData data = user.getCachedData().getMetaData(perms.createContextForWorldLookup(perms.getPlugin().getPlayer(user), world)); String ret = type == ChatMetaType.PREFIX ? data.getPrefix() : data.getSuffix(); return ret != null ? ret : ""; } 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 353ae6481..7b5fa0aeb 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 @@ -144,7 +144,7 @@ public class VaultPermissionHook extends Permission { } // Effectively fallback to the standard Bukkit #hasPermission check. - return user.getUserData().getPermissionData(createContextForWorldLookup(player, world)).getPermissionValue(permission, CheckOrigin.INTERNAL).asBoolean(); + return user.getCachedData().getPermissionData(createContextForWorldLookup(player, world)).getPermissionValue(permission, CheckOrigin.INTERNAL).asBoolean(); } @Override diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeConfigAdapter.java b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeConfigAdapter.java index 10da78793..41ff72d76 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeConfigAdapter.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeConfigAdapter.java @@ -54,16 +54,16 @@ public class BungeeConfigAdapter implements ConfigurationAdapter { @SuppressWarnings("ResultOfMethodCallIgnored") private File makeFile(String file) throws IOException { - File cfg = new File(plugin.getDataFolder(), file); + File configFile = new File(plugin.getDataFolder(), file); - if (!cfg.exists()) { + if (!configFile.exists()) { plugin.getDataFolder().mkdir(); try (InputStream is = plugin.getResourceAsStream(file)) { - Files.copy(is, cfg.toPath()); + Files.copy(is, configFile.toPath()); } } - return cfg; + return configFile; } @Override 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 95bdb836b..283cf7352 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 @@ -35,7 +35,6 @@ 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; import me.lucko.luckperms.common.processors.PermissionProcessor; import me.lucko.luckperms.common.processors.RegexProcessor; @@ -48,7 +47,7 @@ public class BungeeCalculatorFactory extends AbstractCalculatorFactory { private final LPBungeePlugin plugin; @Override - public PermissionCalculator build(Contexts contexts, User user) { + public PermissionCalculator build(Contexts contexts, PermissionCalculatorMetadata metadata) { ImmutableList.Builder processors = ImmutableList.builder(); processors.add(new MapProcessor()); @@ -61,8 +60,7 @@ public class BungeeCalculatorFactory extends AbstractCalculatorFactory { processors.add(new WildcardProcessor()); } - PermissionCalculatorMetadata meta = PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()); - return registerCalculator(new PermissionCalculator(plugin, meta, processors.build())); + return registerCalculator(new PermissionCalculator(plugin, metadata, processors.build())); } @Override diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/listeners/BungeePermissionCheckListener.java b/bungee/src/main/java/me/lucko/luckperms/bungee/listeners/BungeePermissionCheckListener.java index 2b1a0e61f..9a8253288 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/listeners/BungeePermissionCheckListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/listeners/BungeePermissionCheckListener.java @@ -60,7 +60,7 @@ public class BungeePermissionCheckListener implements Listener { } Contexts contexts = plugin.getContextManager().getApplicableContexts(player); - Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission(), CheckOrigin.PLATFORM_PERMISSION_CHECK); + Tristate result = user.getCachedData().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 } @@ -83,7 +83,7 @@ public class BungeePermissionCheckListener implements Listener { } Contexts contexts = plugin.getContextManager().getApplicableContexts(player); - Tristate result = user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission(), CheckOrigin.PLATFORM_LOOKUP_CHECK); + Tristate result = user.getCachedData().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/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiGroup.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiGroup.java index 26bad1eca..9ff598b34 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiGroup.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiGroup.java @@ -32,8 +32,10 @@ import lombok.NonNull; import com.google.common.base.Preconditions; import me.lucko.luckperms.api.Group; +import me.lucko.luckperms.api.caching.GroupData; import java.util.OptionalInt; +import java.util.concurrent.CompletableFuture; public final class ApiGroup extends ApiPermissionHolder implements Group { public static me.lucko.luckperms.common.model.Group cast(Group g) { @@ -59,6 +61,16 @@ public final class ApiGroup extends ApiPermissionHolder implements Group { return handle.getWeight(); } + @Override + public GroupData getCachedData() { + return handle.getCachedData(); + } + + @Override + public CompletableFuture refreshCachedData() { + return handle.getRefreshBuffer().request(); + } + public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof ApiGroup)) return false; diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java index b2636570d..ff5de82ee 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiPermissionHolder.java @@ -38,6 +38,7 @@ import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.PermissionHolder; import me.lucko.luckperms.api.Tristate; +import me.lucko.luckperms.api.caching.CachedData; import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.model.Group; @@ -71,6 +72,11 @@ public class ApiPermissionHolder implements PermissionHolder { return handle.getFriendlyName(); } + @Override + public CachedData getCachedData() { + return handle.getCachedData(); + } + @Override public ImmutableSetMultimap getNodes() { return handle.getEnduringNodes(); diff --git a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiUser.java b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiUser.java index 78e5f91e0..b027ca5b3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiUser.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/delegates/model/ApiUser.java @@ -82,7 +82,7 @@ public final class ApiUser extends ApiPermissionHolder implements User { @Override public UserData getCachedData() { - return handle.getUserData(); + return handle.getCachedData(); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/GroupCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/GroupCache.java new file mode 100644 index 000000000..48d796ab7 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/caching/GroupCache.java @@ -0,0 +1,44 @@ +/* + * 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.caching; + +import me.lucko.luckperms.api.caching.GroupData; +import me.lucko.luckperms.common.model.Group; + +/** + * Holds an easily accessible cache of a groups's data in a number of contexts + */ +public class GroupCache extends HolderCache implements GroupData { + + public GroupCache(Group holder) { + super(holder); + } + + @Override + protected String getHolderName() { + return holder.getName(); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/HolderCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/HolderCache.java new file mode 100644 index 000000000..333314bf5 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/caching/HolderCache.java @@ -0,0 +1,295 @@ +/* + * 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.caching; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +import com.github.benmanes.caffeine.cache.CacheLoader; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; + +import me.lucko.luckperms.api.ChatMetaType; +import me.lucko.luckperms.api.Contexts; +import me.lucko.luckperms.api.caching.CachedData; +import me.lucko.luckperms.api.caching.MetaContexts; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.metastacking.SimpleMetaStack; +import me.lucko.luckperms.common.model.PermissionHolder; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * Holds an easily accessible cache of a holders data in a number of contexts + */ +@RequiredArgsConstructor +public abstract class HolderCache implements CachedData { + + /** + * The holder whom this data instance is representing + */ + protected final T holder; + + /** + * The cache used for {@link PermissionCache} instances. + */ + private final LoadingCache permission = Caffeine.newBuilder() + .expireAfterAccess(2, TimeUnit.MINUTES) + .build(new PermissionCacheLoader()); + + /** + * The cache used for {@link MetaCache} instances. + */ + private final LoadingCache meta = Caffeine.newBuilder() + .expireAfterAccess(2, TimeUnit.MINUTES) + .build(new MetaCacheLoader()); + + protected abstract String getHolderName(); + + /** + * Calculates a {@link PermissionCache} instance. + * + * @param contexts the contexts to calculate in + * @param data an old data instance to try to reuse - ignored if null + * @return the calculated instance + */ + private PermissionCache calculatePermissions(@NonNull Contexts contexts, PermissionCache data) { + if (data == null) { + data = new PermissionCache(contexts, getHolderName(), holder.getPlugin().getCalculatorFactory()); + } + + if (contexts == Contexts.allowAll()) { + data.setPermissions(holder.exportNodesAndShorthand(true)); + } else { + data.setPermissions(holder.exportNodesAndShorthand(contexts, true)); + } + + return data; + } + + /** + * Calculates a {@link MetaCache} instance. + * + * @param contexts the contexts to calculate in + * @param data an old data instance to try to reuse - ignored if null + * @return the calculated instance + */ + private MetaCache calculateMeta(@NonNull MetaContexts contexts, MetaCache data) { + if (data == null) { + data = new MetaCache(); + } + + if (contexts.getContexts() == Contexts.allowAll()) { + data.loadMeta(holder.accumulateMeta(newAccumulator(contexts), null)); + } else { + data.loadMeta(holder.accumulateMeta(newAccumulator(contexts), null, contexts.getContexts())); + } + + return data; + } + + @Override + public PermissionCache getPermissionData(@NonNull Contexts contexts) { + //noinspection ConstantConditions + return permission.get(contexts); + } + + @Override + public MetaCache getMetaData(@NonNull MetaContexts contexts) { + //noinspection ConstantConditions + return meta.get(contexts); + } + + @Override + public MetaCache getMetaData(@NonNull Contexts contexts) { + return getMetaData(makeFromMetaContextsConfig(contexts, holder.getPlugin())); + } + + @Override + public PermissionCache calculatePermissions(@NonNull Contexts contexts) { + return calculatePermissions(contexts, null); + } + + @Override + public MetaCache calculateMeta(@NonNull MetaContexts contexts) { + return calculateMeta(contexts, null); + } + + @Override + public MetaCache calculateMeta(@NonNull Contexts contexts) { + return calculateMeta(makeFromMetaContextsConfig(contexts, holder.getPlugin())); + } + + @Override + public void recalculatePermissions(@NonNull Contexts contexts) { + permission.refresh(contexts); + } + + @Override + public void recalculateMeta(@NonNull MetaContexts contexts) { + meta.refresh(contexts); + } + + @Override + public void recalculateMeta(@NonNull Contexts contexts) { + recalculateMeta(makeFromMetaContextsConfig(contexts, holder.getPlugin())); + } + + @Override + public CompletableFuture reloadPermissions(@NonNull Contexts contexts) { + // get the previous value - to use when recalculating + PermissionCache previous = permission.getIfPresent(contexts); + + // invalidate the entry + permission.invalidate(contexts); + + // repopulate the cache + return CompletableFuture.supplyAsync(() -> permission.get(contexts, c -> calculatePermissions(c, previous))); + } + + @Override + public CompletableFuture reloadMeta(@NonNull MetaContexts contexts) { + // get the previous value - to use when recalculating + MetaCache previous = meta.getIfPresent(contexts); + + // invalidate the entry + meta.invalidate(contexts); + + // repopulate the cache + return CompletableFuture.supplyAsync(() -> meta.get(contexts, c -> calculateMeta(c, previous))); + } + + @Override + public CompletableFuture reloadMeta(@NonNull Contexts contexts) { + return reloadMeta(makeFromMetaContextsConfig(contexts, holder.getPlugin())); + } + + @Override + public void recalculatePermissions() { + Set keys = permission.asMap().keySet(); + keys.forEach(this::recalculatePermissions); + } + + @Override + public void recalculateMeta() { + Set keys = meta.asMap().keySet(); + keys.forEach(this::recalculateMeta); + } + + @Override + public CompletableFuture reloadPermissions() { + Set keys = new HashSet<>(permission.asMap().keySet()); + return CompletableFuture.allOf(keys.stream().map(this::reloadPermissions).toArray(CompletableFuture[]::new)); + } + + @Override + public CompletableFuture reloadMeta() { + Set keys = new HashSet<>(meta.asMap().keySet()); + return CompletableFuture.allOf(keys.stream().map(this::reloadMeta).toArray(CompletableFuture[]::new)); + } + + @Override + public void preCalculate(@NonNull Contexts contexts) { + // pre-calculate just by requesting the data from this cache. + // if the data isn't already loaded, it will be calculated. + getPermissionData(contexts); + getMetaData(contexts); + } + + @Override + public void invalidatePermissions(Contexts contexts) { + permission.invalidate(contexts); + } + + @Override + public void invalidateMeta(MetaContexts contexts) { + meta.invalidate(contexts); + } + + @Override + public void invalidateMeta(Contexts contexts) { + meta.invalidate(makeFromMetaContextsConfig(contexts, holder.getPlugin())); + } + + @Override + public void invalidatePermissionCalculators() { + permission.asMap().values().forEach(PermissionCache::invalidateCache); + } + + public void invalidateCaches() { + permission.invalidateAll(); + meta.invalidateAll(); + } + + public void doCacheCleanup() { + permission.cleanUp(); + meta.cleanUp(); + } + + private final class PermissionCacheLoader implements CacheLoader { + @Override + public PermissionCache load(Contexts contexts) { + return calculatePermissions(contexts); + } + + @Override + public PermissionCache reload(Contexts contexts, PermissionCache oldData) { + return calculatePermissions(contexts, oldData); + } + } + + private final class MetaCacheLoader implements CacheLoader { + @Override + public MetaCache load(MetaContexts contexts) { + return calculateMeta(contexts); + } + + @Override + public MetaCache reload(MetaContexts contexts, MetaCache oldData) { + return calculateMeta(contexts, oldData); + } + } + + private static MetaContexts makeFromMetaContextsConfig(Contexts contexts, LuckPermsPlugin plugin) { + return new MetaContexts( + contexts, + plugin.getConfiguration().get(ConfigKeys.PREFIX_FORMATTING_OPTIONS), + plugin.getConfiguration().get(ConfigKeys.SUFFIX_FORMATTING_OPTIONS) + ); + } + + private static MetaAccumulator newAccumulator(MetaContexts contexts) { + return new MetaAccumulator( + new SimpleMetaStack(contexts.getPrefixStackDefinition(), ChatMetaType.PREFIX), + new SimpleMetaStack(contexts.getSuffixStackDefinition(), ChatMetaType.SUFFIX) + ); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/MetaCache.java b/common/src/main/java/me/lucko/luckperms/common/caching/MetaCache.java index 7791ee831..ac70b0120 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/MetaCache.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/MetaCache.java @@ -45,7 +45,7 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** - * Holds a user's cached meta for a given context + * Holds cached meta for a given context */ @Getter @NoArgsConstructor 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 d91726b9d..55899d518 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 @@ -32,7 +32,7 @@ import me.lucko.luckperms.api.Tristate; 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.calculators.PermissionCalculatorMetadata; import me.lucko.luckperms.common.verbose.CheckOrigin; import java.util.Collections; @@ -40,7 +40,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** - * Holds a user's cached permissions for a given context + * Holds cached permissions data for a given context */ public class PermissionCache implements PermissionData { @@ -61,11 +61,13 @@ public class PermissionCache implements PermissionData { */ private final PermissionCalculator calculator; - public PermissionCache(Contexts contexts, User user, CalculatorFactory calculatorFactory) { + public PermissionCache(Contexts contexts, String friendlyName, CalculatorFactory calculatorFactory) { permissions = new ConcurrentHashMap<>(); permissionsUnmodifiable = Collections.unmodifiableMap(permissions); - calculator = calculatorFactory.build(contexts, user); + PermissionCalculatorMetadata metadata = PermissionCalculatorMetadata.of(friendlyName, contexts.getContexts()); + + calculator = calculatorFactory.build(contexts, metadata); calculator.updateBacking(permissions); // Initial setup. } @@ -74,16 +76,16 @@ public class PermissionCache implements PermissionData { calculator.invalidateCache(); } - public void setPermissions(Map permissions) { + private void setPermissionsInternal(Map permissions) { this.permissions.clear(); this.permissions.putAll(permissions); calculator.updateBacking(this.permissions); invalidateCache(); } - public void comparePermissions(Map toApply) { + public void setPermissions(Map toApply) { if (!permissions.equals(toApply)) { - setPermissions(toApply); + setPermissionsInternal(toApply); } } 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 4d102125d..ef36f9b3b 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 @@ -25,198 +25,20 @@ package me.lucko.luckperms.common.caching; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import com.github.benmanes.caffeine.cache.CacheLoader; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; - -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.UserData; -import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.metastacking.SimpleMetaStack; import me.lucko.luckperms.common.model.User; -import me.lucko.luckperms.common.plugin.LuckPermsPlugin; - -import java.util.Set; -import java.util.concurrent.TimeUnit; /** * Holds an easily accessible cache of a user's data in a number of contexts */ -@RequiredArgsConstructor -public class UserCache implements UserData { +public class UserCache extends HolderCache implements UserData { - /** - * The user whom this data instance is representing - */ - private final User user; - - private final LoadingCache permission = Caffeine.newBuilder() - .expireAfterAccess(2, TimeUnit.MINUTES) - .build(new PermissionCacheLoader()); - - private final LoadingCache meta = Caffeine.newBuilder() - .expireAfterAccess(2, TimeUnit.MINUTES) - .build(new MetaCacheLoader()); - - @Override - public PermissionCache getPermissionData(@NonNull Contexts contexts) { - //noinspection ConstantConditions - return permission.get(contexts); + public UserCache(User holder) { + super(holder); } @Override - public MetaCache getMetaData(@NonNull MetaContexts contexts) { - //noinspection ConstantConditions - return meta.get(contexts); + protected String getHolderName() { + return holder.getFriendlyName(); } - - @Override - public MetaCache getMetaData(@NonNull Contexts contexts) { - // just create a MetaContexts instance using the values in the config - return getMetaData(makeFromMetaContextsConfig(contexts, user.getPlugin())); - } - - @Override - public PermissionCache calculatePermissions(@NonNull Contexts contexts) { - PermissionCache data = new PermissionCache(contexts, user, user.getPlugin().getCalculatorFactory()); - - if (contexts == Contexts.allowAll()) { - data.setPermissions(user.exportNodesAndShorthand(true)); - } else { - data.setPermissions(user.exportNodesAndShorthand(contexts, true)); - } - - return data; - } - - @Override - public MetaCache calculateMeta(@NonNull MetaContexts contexts) { - MetaCache data = new MetaCache(); - - if (contexts.getContexts() == Contexts.allowAll()) { - data.loadMeta(user.accumulateMeta(newAccumulator(contexts), null)); - } else { - data.loadMeta(user.accumulateMeta(newAccumulator(contexts), null, contexts.getContexts())); - } - - return data; - } - - @Override - public MetaCache calculateMeta(@NonNull Contexts contexts) { - // just create a MetaContexts instance using the values in the config - return calculateMeta(makeFromMetaContextsConfig(contexts, user.getPlugin())); - } - - @Override - public void recalculatePermissions(@NonNull Contexts contexts) { - permission.refresh(contexts); - } - - @Override - public void recalculateMeta(@NonNull MetaContexts contexts) { - meta.refresh(contexts); - } - - @Override - public void recalculateMeta(@NonNull Contexts contexts) { - recalculateMeta(makeFromMetaContextsConfig(contexts, user.getPlugin())); - } - - @Override - public void recalculatePermissions() { - Set keys = permission.asMap().keySet(); - keys.forEach(this::recalculatePermissions); - } - - @Override - public void recalculateMeta() { - Set keys = meta.asMap().keySet(); - keys.forEach(this::recalculateMeta); - } - - @Override - public void preCalculate(@NonNull Set contexts) { - contexts.forEach(this::preCalculate); - } - - @Override - public void preCalculate(@NonNull Contexts contexts) { - // pre-calculate just by requesting the data from this cache. - // if the data isn't already loaded, it will be calculated. - getPermissionData(contexts); - getMetaData(contexts); - } - - public void invalidateCaches() { - permission.invalidateAll(); - meta.invalidateAll(); - } - - @Override - public void invalidatePermissionCalculators() { - permission.asMap().values().forEach(PermissionCache::invalidateCache); - } - - public void doCacheCleanup() { - permission.cleanUp(); - meta.cleanUp(); - } - - private final class PermissionCacheLoader implements CacheLoader { - @Override - public PermissionCache load(Contexts contexts) { - return calculatePermissions(contexts); - } - - @Override - public PermissionCache reload(Contexts contexts, PermissionCache oldData) { - if (contexts == Contexts.allowAll()) { - oldData.comparePermissions(user.exportNodesAndShorthand(true)); - } else { - oldData.comparePermissions(user.exportNodesAndShorthand(contexts, true)); - } - - return oldData; - } - } - - private final class MetaCacheLoader implements CacheLoader { - @Override - public MetaCache load(MetaContexts contexts) { - return calculateMeta(contexts); - } - - @Override - public MetaCache reload(MetaContexts contexts, MetaCache oldData) { - if (contexts.getContexts() == Contexts.allowAll()) { - oldData.loadMeta(user.accumulateMeta(newAccumulator(contexts), null)); - } else { - oldData.loadMeta(user.accumulateMeta(newAccumulator(contexts), null, contexts.getContexts())); - } - - return oldData; - } - } - - private static MetaContexts makeFromMetaContextsConfig(Contexts contexts, LuckPermsPlugin plugin) { - return new MetaContexts( - contexts, - plugin.getConfiguration().get(ConfigKeys.PREFIX_FORMATTING_OPTIONS), - plugin.getConfiguration().get(ConfigKeys.SUFFIX_FORMATTING_OPTIONS) - ); - } - - private static MetaAccumulator newAccumulator(MetaContexts contexts) { - return new MetaAccumulator( - new SimpleMetaStack(contexts.getPrefixStackDefinition(), ChatMetaType.PREFIX), - new SimpleMetaStack(contexts.getSuffixStackDefinition(), ChatMetaType.SUFFIX) - ); - } - } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/CalculatorFactory.java b/common/src/main/java/me/lucko/luckperms/common/calculators/CalculatorFactory.java index d66e085e5..2bb863585 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculators/CalculatorFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculators/CalculatorFactory.java @@ -26,7 +26,6 @@ package me.lucko.luckperms.common.calculators; import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.common.model.User; import java.util.List; @@ -39,10 +38,10 @@ public interface CalculatorFactory { * Builds a PermissionCalculator for the user in the given context * * @param contexts the contexts to build the calculator in - * @param user the user to build for + * @param metadata the calculator metadata * @return a permission calculator instance */ - PermissionCalculator build(Contexts contexts, User user); + PermissionCalculator build(Contexts contexts, PermissionCalculatorMetadata metadata); /** * Gets the processors which are currently being added to built calculators 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 e03fe43ef..107d88f6e 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 @@ -68,7 +68,7 @@ public class CheckCommand extends SingleCommand { return CommandResult.STATE_ERROR; } - Tristate tristate = user.getUserData().getPermissionData(plugin.getContextForUser(user)).getPermissionValue(permission, CheckOrigin.INTERNAL); + Tristate tristate = user.getCachedData().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/TreeCommand.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/misc/TreeCommand.java index d5fa07600..b07a36a83 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 @@ -86,7 +86,7 @@ public class TreeCommand extends SingleCommand { return CommandResult.STATE_ERROR; } - PermissionCache permissionData = user.getUserData().getPermissionData(plugin.getContextForUser(user)); + PermissionCache permissionData = user.getCachedData().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/user/UserInfo.java b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java index 81cfd34d3..fc2c1dcb7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/impl/user/UserInfo.java @@ -106,7 +106,7 @@ public class UserInfo extends SubCommand { .map(e -> Util.contextToString(e.getKey(), e.getValue())) .collect(Collectors.joining(" ")); - MetaData meta = user.getUserData().getMetaData(contexts); + MetaData meta = user.getCachedData().getMetaData(contexts); if (meta.getPrefix() != null) { prefix = "&f\"" + meta.getPrefix() + "&f\""; } 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 4f14ebab2..87db31960 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 @@ -32,13 +32,16 @@ import com.google.common.collect.ImmutableSet; import me.lucko.luckperms.api.LogEntry; import me.lucko.luckperms.api.Node; +import me.lucko.luckperms.api.caching.GroupData; import me.lucko.luckperms.api.caching.UserData; import me.lucko.luckperms.api.event.LuckPermsEvent; import me.lucko.luckperms.api.event.cause.CreationCause; import me.lucko.luckperms.api.event.cause.DeletionCause; import me.lucko.luckperms.api.event.log.LogBroadcastEvent; import me.lucko.luckperms.common.event.impl.EventConfigReload; +import me.lucko.luckperms.common.event.impl.EventGroupCacheLoad; import me.lucko.luckperms.common.event.impl.EventGroupCreate; +import me.lucko.luckperms.common.event.impl.EventGroupDataRecalculate; import me.lucko.luckperms.common.event.impl.EventGroupDelete; import me.lucko.luckperms.common.event.impl.EventGroupLoad; import me.lucko.luckperms.common.event.impl.EventGroupLoadAll; @@ -88,11 +91,21 @@ public final class EventFactory { eventBus.fireEvent(event); } + public void handleGroupCacheLoad(Group group, GroupData data) { + EventGroupCacheLoad event = new EventGroupCacheLoad(group.getDelegate(), data); + fireEventAsync(event); + } + public void handleGroupCreate(Group group, CreationCause cause) { EventGroupCreate event = new EventGroupCreate(group.getDelegate(), cause); fireEventAsync(event); } + public void handleGroupDataRecalculate(Group group, GroupData data) { + EventGroupDataRecalculate event = new EventGroupDataRecalculate(group.getDelegate(), data); + fireEventAsync(event); + } + public void handleGroupDelete(Group group, DeletionCause cause) { EventGroupDelete event = new EventGroupDelete(group.getName(), ImmutableSet.copyOf(group.getEnduringNodes().values()), cause); fireEventAsync(event); diff --git a/common/src/main/java/me/lucko/luckperms/common/event/impl/EventGroupCacheLoad.java b/common/src/main/java/me/lucko/luckperms/common/event/impl/EventGroupCacheLoad.java new file mode 100644 index 000000000..a737c59a3 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/event/impl/EventGroupCacheLoad.java @@ -0,0 +1,45 @@ +/* + * 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.Group; +import me.lucko.luckperms.api.caching.GroupData; +import me.lucko.luckperms.api.event.group.GroupCacheLoadEvent; +import me.lucko.luckperms.common.event.AbstractEvent; + +@Getter +@ToString +@AllArgsConstructor +public class EventGroupCacheLoad extends AbstractEvent implements GroupCacheLoadEvent { + + private final Group group; + private final GroupData loadedData; + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/event/impl/EventGroupDataRecalculate.java b/common/src/main/java/me/lucko/luckperms/common/event/impl/EventGroupDataRecalculate.java new file mode 100644 index 000000000..16e5c7ecb --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/event/impl/EventGroupDataRecalculate.java @@ -0,0 +1,45 @@ +/* + * 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.Group; +import me.lucko.luckperms.api.caching.GroupData; +import me.lucko.luckperms.api.event.group.GroupDataRecalculateEvent; +import me.lucko.luckperms.common.event.AbstractEvent; + +@Getter +@ToString +@AllArgsConstructor +public class EventGroupDataRecalculate extends AbstractEvent implements GroupDataRecalculateEvent { + + private final Group group; + private final GroupData data; + +} 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 22105b5ff..ed2f0ea5a 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 @@ -101,7 +101,7 @@ public class GenericUserManager extends AbstractManager im plugin.getScheduler().asyncLater(() -> { User user = getIfLoaded(plugin.getUuidCache().getUUID(uuid)); if (user != null && !plugin.isPlayerOnline(uuid)) { - user.getUserData().invalidateCaches(); + user.getCachedData().invalidateCaches(); unload(user); plugin.getUuidCache().clearCache(uuid); } diff --git a/common/src/main/java/me/lucko/luckperms/common/model/Group.java b/common/src/main/java/me/lucko/luckperms/common/model/Group.java index 76d809682..eb93ede01 100644 --- a/common/src/main/java/me/lucko/luckperms/common/model/Group.java +++ b/common/src/main/java/me/lucko/luckperms/common/model/Group.java @@ -32,12 +32,15 @@ import lombok.ToString; import me.lucko.luckperms.api.Node; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.api.delegates.model.ApiGroup; +import me.lucko.luckperms.common.buffers.BufferedRequest; +import me.lucko.luckperms.common.caching.GroupCache; import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.references.GroupReference; import me.lucko.luckperms.common.references.Identifiable; import java.util.Optional; +import java.util.concurrent.CompletableFuture; @ToString(of = {"name"}) @EqualsAndHashCode(of = {"name"}, callSuper = false) @@ -52,9 +55,25 @@ public class Group extends PermissionHolder implements Identifiable { @Getter private final ApiGroup delegate = new ApiGroup(this); + /** + * The groups data cache instance + */ + @Getter + private final GroupCache cachedData; + + @Getter + private BufferedRequest refreshBuffer; + public Group(String name, LuckPermsPlugin plugin) { super(name, plugin); this.name = name.toLowerCase(); + + this.refreshBuffer = new GroupRefreshBuffer(plugin, this); + this.cachedData = new GroupCache(this); + getPlugin().getApiProvider().getEventFactory().handleGroupCacheLoad(this, cachedData); + + // invalidate out caches when data is updated + getStateListeners().add(() -> refreshBuffer.request()); } @Override @@ -91,4 +110,25 @@ public class Group extends PermissionHolder implements Identifiable { public GroupReference toReference() { return GroupReference.of(getId()); } + + private CompletableFuture reloadCachedData() { + return CompletableFuture.allOf(cachedData.reloadPermissions(), cachedData.reloadMeta()).thenAccept(n -> { + getPlugin().getApiProvider().getEventFactory().handleGroupDataRecalculate(this, cachedData); + }); + } + + private static final class GroupRefreshBuffer extends BufferedRequest { + private final Group group; + + private GroupRefreshBuffer(LuckPermsPlugin plugin, Group group) { + super(50L, 5L, plugin.getScheduler().async()); + this.group = group; + } + + @Override + protected Void perform() { + return group.reloadCachedData().join(); + } + } + } 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 e9f3f4d42..e0ea91b94 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 @@ -47,6 +47,7 @@ import me.lucko.luckperms.api.context.ContextSet; import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.api.delegates.model.ApiPermissionHolder; import me.lucko.luckperms.common.buffers.Cache; +import me.lucko.luckperms.common.caching.HolderCache; import me.lucko.luckperms.common.caching.MetaAccumulator; import me.lucko.luckperms.common.caching.handlers.StateListener; import me.lucko.luckperms.common.config.ConfigKeys; @@ -250,6 +251,13 @@ public abstract class PermissionHolder { */ public abstract String getFriendlyName(); + /** + * Gets the holders cached data + * + * @return the holders cached data + */ + public abstract HolderCache getCachedData(); + /** * Forms a HolderReference for this PermissionHolder. * 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 ad2a682b2..21e7b6362 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 @@ -42,6 +42,7 @@ import me.lucko.luckperms.common.references.UserReference; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; @ToString(of = {"uuid"}) @EqualsAndHashCode(of = {"uuid"}, callSuper = false) @@ -65,10 +66,10 @@ public class User extends PermissionHolder implements Identifiable refreshBuffer; @@ -83,8 +84,8 @@ public class User extends PermissionHolder implements Identifiable reloadCachedData() { + return CompletableFuture.allOf(cachedData.reloadPermissions(), cachedData.reloadMeta()).thenAccept(n -> { + getPlugin().getApiProvider().getEventFactory().handleUserDataRecalculate(this, cachedData); + }); } /** @@ -213,14 +210,13 @@ public class User extends PermissionHolder implements Identifiable c = database.getCollection(prefix + "groups"); try (MongoCursor cursor = c.find(new Document("_id", group.getName())).iterator()) { - if (cursor.hasNext()) { - Document d = cursor.next(); - - group.setEnduringNodes(revert((Map) d.get("perms")).entrySet().stream() - .map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue())) - .collect(Collectors.toSet()) - ); - return true; + if (!cursor.hasNext()) { + return false; } - return false; + + Document d = cursor.next(); + group.setEnduringNodes(revert((Map) d.get("perms")).entrySet().stream() + .map(e -> NodeFactory.fromSerializedNode(e.getKey(), e.getValue())) + .collect(Collectors.toSet()) + ); } } catch (Exception e) { return reportException(e); } finally { group.getIoLock().unlock(); } + group.getRefreshBuffer().requestDirectly(); + return true; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/dao/sql/SqlDao.java b/common/src/main/java/me/lucko/luckperms/common/storage/dao/sql/SqlDao.java index fbfbd33c4..edd122d96 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/dao/sql/SqlDao.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/dao/sql/SqlDao.java @@ -388,12 +388,11 @@ public class SqlDao extends AbstractDao { plugin.getUserManager().giveDefaultIfNeeded(user, false); } } - - return true; } finally { user.getIoLock().unlock(); - user.getRefreshBuffer().requestDirectly(); } + user.getRefreshBuffer().requestDirectly(); + return true; } @Override @@ -659,11 +658,11 @@ public class SqlDao extends AbstractDao { } else { group.clearNodes(); } - - return true; } finally { group.getIoLock().unlock(); } + group.getRefreshBuffer().requestDirectly(); + return true; } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/tasks/CacheHousekeepingTask.java b/common/src/main/java/me/lucko/luckperms/common/tasks/CacheHousekeepingTask.java index c1d55cf44..e5246aee0 100644 --- a/common/src/main/java/me/lucko/luckperms/common/tasks/CacheHousekeepingTask.java +++ b/common/src/main/java/me/lucko/luckperms/common/tasks/CacheHousekeepingTask.java @@ -27,6 +27,7 @@ package me.lucko.luckperms.common.tasks; import lombok.RequiredArgsConstructor; +import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; @@ -37,7 +38,10 @@ public class CacheHousekeepingTask implements Runnable { @Override public void run() { for (User user : plugin.getUserManager().getAll().values()) { - user.getUserData().doCacheCleanup(); + user.getCachedData().doCacheCleanup(); + } + for (Group group : plugin.getGroupManager().getAll().values()) { + group.getCachedData().doCacheCleanup(); } } } 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 c917709e7..7c7d7c314 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 @@ -102,10 +102,4 @@ public class LoginHelper { return user; } - public static void refreshPlayer(LuckPermsPlugin plugin, UUID uuid) { - final User user = plugin.getUserManager().getIfLoaded(plugin.getUuidCache().getUUID(uuid)); - if (user != null) { - user.getRefreshBuffer().requestDirectly(); - } - } } diff --git a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java index 5b9103b0d..b75fff212 100644 --- a/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java +++ b/sponge/sponge-service/src/main/java/me/lucko/luckperms/sponge/service/model/LPPermissionService.java @@ -29,8 +29,6 @@ import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import org.spongepowered.api.plugin.PluginContainer; @@ -83,7 +81,5 @@ public interface LPPermissionService { // utils ImmutableList sortSubjects(Collection s); - Contexts calculateContexts(ImmutableContextSet contextSet); - void invalidateAllCaches(LPSubject.CacheLevel cacheLevel); } 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 edd184ee2..228d5f9eb 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 @@ -34,7 +34,6 @@ 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; import me.lucko.luckperms.common.processors.PermissionProcessor; import me.lucko.luckperms.common.processors.RegexProcessor; @@ -50,7 +49,7 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory { private final LPSpongePlugin plugin; @Override - public PermissionCalculator build(Contexts contexts, User user) { + public PermissionCalculator build(Contexts contexts, PermissionCalculatorMetadata metadata) { ImmutableList.Builder processors = ImmutableList.builder(); processors.add(new MapProcessor()); @@ -71,8 +70,7 @@ public class SpongeCalculatorFactory extends AbstractCalculatorFactory { processors.add(new DefaultsProcessor(plugin.getService(), contexts.getContexts().makeImmutable())); } - PermissionCalculatorMetadata meta = PermissionCalculatorMetadata.of(user.getFriendlyName(), contexts.getContexts()); - return registerCalculator(new PermissionCalculator(plugin, meta, processors.build())); + return registerCalculator(new PermissionCalculator(plugin, metadata, processors.build())); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeConnectionListener.java b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeConnectionListener.java index 51481d5cb..90bfe62be 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeConnectionListener.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/listeners/SpongeConnectionListener.java @@ -171,12 +171,6 @@ public class SpongeConnectionListener { } } - @Listener(order = Order.EARLY) - public void onClientJoin(ClientConnectionEvent.Join e) { - // Refresh permissions again - plugin.getScheduler().doAsync(() -> LoginHelper.refreshPlayer(plugin, e.getTargetEntity().getUniqueId())); - } - @Listener(order = Order.POST) public void onClientLeave(ClientConnectionEvent.Disconnect e) { /* We don't actually remove the user instance here, as Sponge likes to keep performing checks 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 78d5a501f..f75d62a6f 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 @@ -27,38 +27,27 @@ package me.lucko.luckperms.sponge.model; import lombok.Getter; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ListMultimap; +import com.google.common.collect.ImmutableSet; -import me.lucko.luckperms.api.ChatMetaType; -import me.lucko.luckperms.api.LocalizedNode; -import me.lucko.luckperms.api.Node; 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.caching.MetaAccumulator; import me.lucko.luckperms.common.model.Group; +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; import me.lucko.luckperms.sponge.service.ProxyFactory; -import me.lucko.luckperms.sponge.service.model.CompatibilityUtil; import me.lucko.luckperms.sponge.service.model.LPSubject; import me.lucko.luckperms.sponge.service.model.LPSubjectCollection; import me.lucko.luckperms.sponge.service.model.SubjectReference; import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.service.permission.NodeTree; import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.Subject; -import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; public class SpongeGroup extends Group { @@ -74,11 +63,7 @@ public class SpongeGroup extends Group { } public static class GroupSubject implements LPSubject { - - @Getter private final SpongeGroup parent; - - @Getter private final LPSpongePlugin plugin; @Getter @@ -87,61 +72,11 @@ public class SpongeGroup extends Group { @Getter private final LuckPermsSubjectData transientSubjectData; - private final LoadingCache permissionCache = Caffeine.newBuilder() - .expireAfterAccess(10, TimeUnit.MINUTES) - .build(contexts -> { - // TODO move this away from NodeTree - Map permissions = getParent().getAllNodes(getPlugin().getService().calculateContexts(contexts)).stream() - .map(LocalizedNode::getNode) - .collect(Collectors.toMap(Node::getPermission, Node::getValuePrimitive)); - - return NodeTree.of(permissions); - }); - - private final LoadingCache> parentCache = Caffeine.newBuilder() - .expireAfterWrite(10, TimeUnit.MINUTES) - .build(contexts -> { - Set subjects = getParent().getAllNodes(getPlugin().getService().calculateContexts(contexts)).stream() - .map(LocalizedNode::getNode) - .filter(Node::isGroupNode) - .map(Node::getGroupName) - .distinct() - .map(n -> Optional.ofNullable(getPlugin().getGroupManager().getIfLoaded(n))) - .filter(Optional::isPresent) - .map(Optional::get) - .map(SpongeGroup::sponge) - .map(LPSubject::toReference) - .collect(Collectors.toSet()); - - subjects.addAll(getPlugin().getService().getGroupSubjects().getDefaults().getParents(contexts)); - subjects.addAll(getPlugin().getService().getDefaults().getParents(contexts)); - - return getService().sortSubjects(subjects); - }); - private GroupSubject(LPSpongePlugin plugin, SpongeGroup parent) { this.parent = parent; this.plugin = plugin; this.subjectData = new LuckPermsSubjectData(true, plugin.getService(), parent, this); this.transientSubjectData = new LuckPermsSubjectData(false, plugin.getService(), parent, this); - - parent.getStateListeners().add(() -> invalidateCaches(CacheLevel.PERMISSION)); - } - - @Override - public void invalidateCaches(CacheLevel type) { - if (type == CacheLevel.OPTION) { - return; // don't invalidate for option changes - } - - permissionCache.invalidateAll(); - parentCache.invalidateAll(); - } - - @Override - public void performCleanup() { - permissionCache.cleanUp(); - parentCache.cleanUp(); } @Override @@ -176,19 +111,7 @@ public class SpongeGroup extends Group { @Override public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) { - NodeTree nt = permissionCache.get(contexts); - Tristate t = CompatibilityUtil.convertTristate(nt.get(permission)); - if (t != Tristate.UNDEFINED) { - return t; - } - - t = plugin.getService().getGroupSubjects().getDefaults().getPermissionValue(contexts, permission); - if (t != Tristate.UNDEFINED) { - return t; - } - - t = plugin.getService().getDefaults().getPermissionValue(contexts, permission); - return t; + return parent.getCachedData().getPermissionData(plugin.getContextManager().formContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); } @Override @@ -198,29 +121,48 @@ public class SpongeGroup extends Group { @Override public ImmutableList getParents(ImmutableContextSet contexts) { - return parentCache.get(contexts); + ImmutableSet.Builder subjects = ImmutableSet.builder(); + + for (String perm : parent.getCachedData().getPermissionData(plugin.getContextManager().formContexts(contexts)).getImmutableBacking().keySet()) { + if (!perm.startsWith("group.")) { + continue; + } + + String groupName = perm.substring("group.".length()); + if (plugin.getGroupManager().isLoaded(groupName)) { + subjects.add(plugin.getService().getGroupSubjects().loadSubject(groupName).join().toReference()); + } + } + + subjects.addAll(plugin.getService().getGroupSubjects().getDefaults().getParents(contexts)); + subjects.addAll(plugin.getService().getDefaults().getParents(contexts)); + + return getService().sortSubjects(subjects.build()); } @Override public Optional getOption(ImmutableContextSet contexts, String s) { - Optional option; + MetaData data = parent.getCachedData().getMetaData(plugin.getContextManager().formContexts(contexts)); if (s.equalsIgnoreCase("prefix")) { - option = getChatMeta(contexts, ChatMetaType.PREFIX); - - } else if (s.equalsIgnoreCase("suffix")) { - option = getChatMeta(contexts, ChatMetaType.SUFFIX); - - } else { - option = getMeta(contexts, s); + if (data.getPrefix() != null) { + return Optional.of(data.getPrefix()); + } } - if (option.isPresent()) { - return option; + if (s.equalsIgnoreCase("suffix")) { + if (data.getSuffix() != null) { + return Optional.of(data.getSuffix()); + } } - option = plugin.getService().getGroupSubjects().getDefaults().getOption(contexts, s); - if (option.isPresent()) { - return option; + String val = data.getMeta().get(s); + if (val != null) { + return Optional.of(val); + } + + Optional v = plugin.getService().getGroupSubjects().getDefaults().getOption(contexts, s); + if (v.isPresent()) { + return v; } return plugin.getService().getDefaults().getOption(contexts, s); @@ -231,16 +173,11 @@ public class SpongeGroup extends Group { return plugin.getContextManager().getApplicableContext(this.sponge()); } - private Optional getChatMeta(ImmutableContextSet contexts, ChatMetaType type) { - MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, plugin.getService().calculateContexts(contexts)); - return Optional.ofNullable(metaAccumulator.getStack(type).toFormattedString()); - } - - private Optional getMeta(ImmutableContextSet contexts, String key) { - MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, plugin.getService().calculateContexts(contexts)); - ListMultimap meta = metaAccumulator.getMeta(); - List ret = meta.get(key); - return ret.isEmpty() ? Optional.empty() : Optional.of(ret.get(0)); + @Override + public void invalidateCaches(CacheLevel cacheLevel) { + // invalidate for all changes + parent.getCachedData().invalidateCaches(); } } + } 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 04a3e1b25..5b41e581f 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 @@ -45,12 +45,12 @@ import me.lucko.luckperms.sponge.service.model.SubjectReference; import org.spongepowered.api.Sponge; import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.Subject; import java.util.Optional; import java.util.UUID; +import java.util.function.Function; public class SpongeUser extends User { @@ -100,13 +100,7 @@ public class SpongeUser extends User { @Override public Optional getCommandSource() { final UUID uuid = plugin.getUuidCache().getExternalUUID(parent.getUuid()); - - Optional p = Sponge.getServer().getPlayer(uuid); - if (p.isPresent()) { - return Optional.of(p.get()); - } - - return Optional.empty(); + return Sponge.getServer().getPlayer(uuid).map(Function.identity()); } @Override @@ -126,7 +120,7 @@ public class SpongeUser extends User { @Override public Tristate getPermissionValue(ImmutableContextSet contexts, String permission) { - return parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); + return parent.getCachedData().getPermissionData(plugin.getContextManager().formContexts(contexts)).getPermissionValue(permission, CheckOrigin.PLATFORM_LOOKUP_CHECK); } @Override @@ -138,7 +132,7 @@ public class SpongeUser extends User { public ImmutableList getParents(ImmutableContextSet contexts) { ImmutableSet.Builder subjects = ImmutableSet.builder(); - for (String perm : parent.getUserData().getPermissionData(plugin.getService().calculateContexts(contexts)).getImmutableBacking().keySet()) { + for (String perm : parent.getCachedData().getPermissionData(plugin.getContextManager().formContexts(contexts)).getImmutableBacking().keySet()) { if (!perm.startsWith("group.")) { continue; } @@ -157,7 +151,7 @@ public class SpongeUser extends User { @Override public Optional getOption(ImmutableContextSet contexts, String s) { - MetaData data = parent.getUserData().getMetaData(plugin.getService().calculateContexts(contexts)); + MetaData data = parent.getCachedData().getMetaData(plugin.getContextManager().formContexts(contexts)); if (s.equalsIgnoreCase("prefix")) { if (data.getPrefix() != null) { return Optional.of(data.getPrefix()); @@ -191,7 +185,7 @@ public class SpongeUser extends User { @Override public void invalidateCaches(CacheLevel cacheLevel) { // invalidate for all changes - parent.getUserData().invalidateCaches(); + parent.getCachedData().invalidateCaches(); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java index 871ff17fd..ba5471e86 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java @@ -35,8 +35,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.api.context.ImmutableContextSet; import me.lucko.luckperms.common.model.Group; import me.lucko.luckperms.common.utils.Predicates; import me.lucko.luckperms.sponge.LPSpongePlugin; @@ -230,11 +228,6 @@ public class LuckPermsService implements LPPermissionService { return ImmutableList.copyOf(ret); } - @Override - public Contexts calculateContexts(ImmutableContextSet contextSet) { - return plugin.getContextManager().formContexts(null, contextSet); - } - @Override public void invalidateAllCaches(LPSubject.CacheLevel cacheLevel) { for (LPSubjectCollection collection : collections.asMap().values()) { 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 aa2d757c9..4339d85c6 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 @@ -353,7 +353,7 @@ public class LuckPermsSubjectData implements LPSubjectData { toRemove.forEach(makeUnsetConsumer(enduring)); - MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, service.calculateContexts(context)); + MetaAccumulator metaAccumulator = holder.accumulateMeta(null, null, service.getPlugin().getContextManager().formContexts(context)); int priority = metaAccumulator.getChatMeta(type).keySet().stream().mapToInt(e -> e).max().orElse(0); priority += 10; @@ -451,23 +451,21 @@ public class LuckPermsSubjectData implements LPSubjectData { } else { if (t instanceof User) { User user = ((User) t); - return service.getPlugin().getStorage().saveUser(user).thenApplyAsync(success -> { + return service.getPlugin().getStorage().saveUser(user).thenComposeAsync(success -> { if (!success) { - return null; + return CompletableFuture.completedFuture(null); } - user.getRefreshBuffer().request().join(); - return null; + return user.getRefreshBuffer().request(); }, service.getPlugin().getScheduler().async()); } else { Group group = ((Group) t); - return service.getPlugin().getStorage().saveGroup(group).thenApplyAsync(success -> { + return service.getPlugin().getStorage().saveGroup(group).thenComposeAsync(success -> { if (!success) { - return null; + return CompletableFuture.completedFuture(null); } - service.getPlugin().getUpdateTaskBuffer().request().join(); - return null; + return service.getPlugin().getUpdateTaskBuffer().request(); }, service.getPlugin().getScheduler().async()); } }