Final bits of API refactoring, add group data caches, fix issue with LPPermissionAttachment fake map injection

This commit is contained in:
Luck 2017-11-08 22:55:05 +00:00
parent d75b29f51d
commit a115ff8ce2
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
51 changed files with 1267 additions and 579 deletions

View File

@ -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<Void> refreshCachedData();
}

View File

@ -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.
*

View File

@ -84,6 +84,7 @@ public interface User extends PermissionHolder {
* @since 3.2
*/
@Nonnull
@Override
UserData getCachedData();
/**

View File

@ -0,0 +1,404 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.api.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}.
*
* <p>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.</p>
*
* <p>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.</p>
*
* @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.
*
* <p>The result of this operation is calculated each time the method is called.
* The result is not added to the internal cache.</p>
*
* <p>It is therefore highly recommended to use {@link #getPermissionData(Contexts)} instead.</p>
*
* <p>The use cases of this method are more around constructing one-time
* instances of {@link PermissionData}, without adding the result to the cache.</p>
*
* @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.
*
* <p>The result of this operation is calculated each time the method is called.
* The result is not added to the internal cache.</p>
*
* <p>It is therefore highly recommended to use {@link #getMetaData(MetaContexts)} instead.</p>
*
* <p>The use cases of this method are more around constructing one-time
* instances of {@link MetaData}, without adding the result to the cache.</p>
*
* @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.
*
* <p>The result of this operation is calculated each time the method is called.
* The result is not added to the internal cache.</p>
*
* <p>It is therefore highly recommended to use {@link #getMetaData(Contexts)} instead.</p>
*
* <p>The use cases of this method are more around constructing one-time
* instances of {@link MetaData}, without adding the result to the cache.</p>
*
* @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.
*
* <p>This method returns immediately in all cases. The (re)calculation is
* performed asynchronously and applied to the cache in the background.</p>
*
* <p>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.</p>
*
* <p>If there was no value calculated and cached prior to the call of this
* method, then one will be calculated.</p>
*
* @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.
*
* <p>This method returns immediately in all cases. The (re)calculation is
* performed asynchronously and applied to the cache in the background.</p>
*
* <p>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.</p>
*
* <p>If there was no value calculated and cached prior to the call of this
* method, then one will be calculated.</p>
*
* @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.
*
* <p>This method returns immediately in all cases. The (re)calculation is
* performed asynchronously and applied to the cache in the background.</p>
*
* <p>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.</p>
*
* <p>If there was no value calculated and cached prior to the call of this
* method, then one will be calculated.</p>
*
* @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.
*
* <p>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.</p>
*
* <p>The invalidation happens immediately during the execution of this method.
* The result of the re-computation encapsulated by the future.</p>
*
* <p>Subsequent calls to {@link #getPermissionData(Contexts)} will block until
* the result of this operation is complete.</p>
*
* <p>If there was no value calculated and cached prior to the call of this
* method, then one will be calculated.</p>
*
* <p>This method returns a Future so users can optionally choose to wait
* until the recalculation has been performed.</p>
*
* @param contexts the contexts to reload in.
* @throws NullPointerException if contexts is null
* @since 4.0
*/
@Nonnull
CompletableFuture<? extends PermissionData> reloadPermissions(@Nonnull Contexts contexts);
/**
* (Re)loads meta data for a given context.
*
* <p>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.</p>
*
* <p>The invalidation happens immediately during the execution of this method.
* The result of the re-computation encapsulated by the future.</p>
*
* <p>Subsequent calls to {@link #getMetaData(MetaContexts)} will block until
* the result of this operation is complete.</p>
*
* <p>If there was no value calculated and cached prior to the call of this
* method, then one will be calculated.</p>
*
* <p>This method returns a Future so users can optionally choose to wait
* until the recalculation has been performed.</p>
*
* @param contexts the contexts to reload in.
* @throws NullPointerException if contexts is null
* @since 4.0
*/
@Nonnull
CompletableFuture<? extends MetaData> reloadMeta(@Nonnull MetaContexts contexts);
/**
* (Re)loads meta data for a given context.
*
* <p>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.</p>
*
* <p>The invalidation happens immediately during the execution of this method.
* The result of the re-computation encapsulated by the future.</p>
*
* <p>Subsequent calls to {@link #getMetaData(Contexts)} will block until
* the result of this operation is complete.</p>
*
* <p>If there was no value calculated and cached prior to the call of this
* method, then one will be calculated.</p>
*
* <p>This method returns a Future so users can optionally choose to wait
* until the recalculation has been performed.</p>
*
* @param contexts the contexts to reload in.
* @throws NullPointerException if contexts is null
* @since 4.0
*/
@Nonnull
CompletableFuture<? extends MetaData> reloadMeta(@Nonnull Contexts contexts);
/**
* Recalculates permission data for all known contexts.
*
* <p>This method returns immediately. The recalculation is performed
* asynchronously and applied to the cache in the background.</p>
*
* <p>The previous {@link PermissionData} instances will continue to be returned
* by {@link #getPermissionData(Contexts)} until the recalculation is completed.</p>
*/
void recalculatePermissions();
/**
* Recalculates meta data for all known contexts.
*
* <p>This method returns immediately. The recalculation is performed
* asynchronously and applied to the cache in the background.</p>
*
* <p>The previous {@link MetaData} instances will continue to be returned
* by {@link #getMetaData(MetaContexts)} and {@link #getMetaData(Contexts)}
* until the recalculation is completed.</p>
*/
void recalculateMeta();
/**
* Reloads permission data for all known contexts.
*
* <p>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.</p>
*
* <p>The invalidation happens immediately during the execution of this method.
* The result of the re-computation encapsulated by the future.</p>
*
* <p>Subsequent calls to {@link #getPermissionData(Contexts)} will block until
* the result of this operation is complete.</p>
*
* <p>This method returns a Future so users can optionally choose to wait
* until the recalculation has been performed.</p>
*
* @since 4.0
*/
@Nonnull
CompletableFuture<Void> reloadPermissions();
/**
* Reloads meta data for all known contexts.
*
* <p>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.</p>
*
* <p>The invalidation happens immediately during the execution of this method.
* The result of the re-computation encapsulated by the future.</p>
*
* <p>Subsequent calls to {@link #getMetaData(MetaContexts)} and
* {@link #getMetaData(Contexts)} will block until the result of this operation
* is complete.</p>
*
* <p>This method returns a Future so users can optionally choose to wait
* until the recalculation has been performed.</p>
*
* @since 4.0
*/
@Nonnull
CompletableFuture<Void> reloadMeta();
/**
* Pre-calculates and caches {@link PermissionData} and {@link MetaData}
* instances for the given contexts.
*
* <p>If the cache already contains a value for the given context,
* no action is taken.</p>
*
* <p>This method blocks until the calculation is completed.</p>
*
* @param contexts a set of contexts
* @throws NullPointerException if contexts is null
*/
default void preCalculate(@Nonnull Set<Contexts> contexts) {
contexts.forEach(this::preCalculate);
}
/**
* Pre-calculates and caches {@link PermissionData} and {@link MetaData}
* instances for a given context.
*
* <p>If the cache already contains a value for the given context,
* no action is taken.</p>
*
* <p>This method blocks until the calculation is completed.</p>
*
* @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.
*
* <p>Can be called to allow for an update in defaults.</p>
*/
void invalidatePermissionCalculators();
}

View File

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

View File

@ -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}.
*
* <p>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.</p>
* <p>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.</p>
*
* <p>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.
*
* <p>If the data is not cached, it is calculated. Therefore, this call could be costly.</p>
*
* @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.
*
* <p>If the data is not cached, it is calculated. Therefore, this call could be costly.</p>
*
* @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.
*
* <p>If the data is not cached, it is calculated. Therefore, this call could be costly.</p>
*
* @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.
*
* <p>If there is already data cached for the given contexts, and if the resultant output is different,
* the cached value is updated.</p>
*
* @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.
*
* <p>If there is already data cached for the given contexts, and if the resultant output is different,
* the cached value is updated.</p>
*
* @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.
*
* <p>If there is already data cached for the given contexts, and if the resultant output is different,
* the cached value is updated.</p>
*
* @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> contexts);
/**
* Ensures that PermissionData and MetaData is cached for a context.
*
* <p>If the cache does not contain any data for the context, it will be calculated and saved.</p>
*
* @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.
*
* <p>Can be called to allow for an update in defaults.</p>
*/
void invalidatePermissionCalculators();
public interface UserData extends CachedData {
}

View File

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

View File

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

View File

@ -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<String, Boolean> backing = user.getUserData().getPermissionData(contextManager.getApplicableContexts(player)).getImmutableBacking();
Map<String, Boolean> backing = user.getCachedData().getPermissionData(contextManager.getApplicableContexts(player)).getImmutableBacking();
boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false);
player.setOp(op);
}

View File

@ -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<PermissionProcessor> 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

View File

@ -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();
}

View File

@ -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<PermissionAttachmentInfo> getEffectivePermissions() {
Set<PermissionAttachmentInfo> 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())
);

View File

@ -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);

View File

@ -181,7 +181,7 @@ public class LPSubscriptionMap extends HashMap<String, Map<Permissible, Boolean>
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<String, Map<Permissible, Boolean>
@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

View File

@ -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

View File

@ -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 : "";
}

View File

@ -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

View File

@ -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

View File

@ -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<PermissionProcessor> 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

View File

@ -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
}

View File

@ -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<Void> refreshCachedData() {
return handle.getRefreshBuffer().request();
}
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ApiGroup)) return false;

View File

@ -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<ImmutableContextSet, Node> getNodes() {
return handle.getEnduringNodes();

View File

@ -82,7 +82,7 @@ public final class ApiUser extends ApiPermissionHolder implements User {
@Override
public UserData getCachedData() {
return handle.getUserData();
return handle.getCachedData();
}
@Override

View File

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

View File

@ -0,0 +1,295 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.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<T extends PermissionHolder> implements CachedData {
/**
* The holder whom this data instance is representing
*/
protected final T holder;
/**
* The cache used for {@link PermissionCache} instances.
*/
private final LoadingCache<Contexts, PermissionCache> permission = Caffeine.newBuilder()
.expireAfterAccess(2, TimeUnit.MINUTES)
.build(new PermissionCacheLoader());
/**
* The cache used for {@link MetaCache} instances.
*/
private final LoadingCache<MetaContexts, MetaCache> 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<PermissionCache> 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<MetaCache> 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<MetaCache> reloadMeta(@NonNull Contexts contexts) {
return reloadMeta(makeFromMetaContextsConfig(contexts, holder.getPlugin()));
}
@Override
public void recalculatePermissions() {
Set<Contexts> keys = permission.asMap().keySet();
keys.forEach(this::recalculatePermissions);
}
@Override
public void recalculateMeta() {
Set<MetaContexts> keys = meta.asMap().keySet();
keys.forEach(this::recalculateMeta);
}
@Override
public CompletableFuture<Void> reloadPermissions() {
Set<Contexts> keys = new HashSet<>(permission.asMap().keySet());
return CompletableFuture.allOf(keys.stream().map(this::reloadPermissions).toArray(CompletableFuture[]::new));
}
@Override
public CompletableFuture<Void> reloadMeta() {
Set<MetaContexts> 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<Contexts, PermissionCache> {
@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<MetaContexts, MetaCache> {
@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)
);
}
}

View File

@ -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

View File

@ -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<String, Boolean> permissions) {
private void setPermissionsInternal(Map<String, Boolean> permissions) {
this.permissions.clear();
this.permissions.putAll(permissions);
calculator.updateBacking(this.permissions);
invalidateCache();
}
public void comparePermissions(Map<String, Boolean> toApply) {
public void setPermissions(Map<String, Boolean> toApply) {
if (!permissions.equals(toApply)) {
setPermissions(toApply);
setPermissionsInternal(toApply);
}
}

View File

@ -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<User> implements UserData {
/**
* The user whom this data instance is representing
*/
private final User user;
private final LoadingCache<Contexts, PermissionCache> permission = Caffeine.newBuilder()
.expireAfterAccess(2, TimeUnit.MINUTES)
.build(new PermissionCacheLoader());
private final LoadingCache<MetaContexts, MetaCache> 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<Contexts> keys = permission.asMap().keySet();
keys.forEach(this::recalculatePermissions);
}
@Override
public void recalculateMeta() {
Set<MetaContexts> keys = meta.asMap().keySet();
keys.forEach(this::recalculateMeta);
}
@Override
public void preCalculate(@NonNull Set<Contexts> 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<Contexts, PermissionCache> {
@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<MetaContexts, MetaCache> {
@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)
);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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()) {

View File

@ -106,7 +106,7 @@ public class UserInfo extends SubCommand<User> {
.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\"";
}

View File

@ -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);

View File

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

View File

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

View File

@ -101,7 +101,7 @@ public class GenericUserManager extends AbstractManager<UserIdentifier, User> 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);
}

View File

@ -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<String> {
@Getter
private final ApiGroup delegate = new ApiGroup(this);
/**
* The groups data cache instance
*/
@Getter
private final GroupCache cachedData;
@Getter
private BufferedRequest<Void> 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<String> {
public GroupReference toReference() {
return GroupReference.of(getId());
}
private CompletableFuture<Void> reloadCachedData() {
return CompletableFuture.allOf(cachedData.reloadPermissions(), cachedData.reloadMeta()).thenAccept(n -> {
getPlugin().getApiProvider().getEventFactory().handleGroupDataRecalculate(this, cachedData);
});
}
private static final class GroupRefreshBuffer extends BufferedRequest<Void> {
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();
}
}
}

View File

@ -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.
*

View File

@ -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<UserIdentifie
private final PrimaryGroupHolder primaryGroup;
/**
* The users data cache instance, if present.
* The users data cache instance
*/
@Getter
private final UserCache userData;
private final UserCache cachedData;
@Getter
private BufferedRequest<Void> refreshBuffer;
@ -83,8 +84,8 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
this.refreshBuffer = new UserRefreshBuffer(plugin, this);
this.primaryGroup = plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION).apply(this);
this.userData = new UserCache(this);
getPlugin().getApiProvider().getEventFactory().handleUserCacheLoad(this, userData);
this.cachedData = new UserCache(this);
getPlugin().getApiProvider().getEventFactory().handleUserCacheLoad(this, cachedData);
}
public User(UUID uuid, String name, LuckPermsPlugin plugin) {
@ -95,8 +96,8 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
this.refreshBuffer = new UserRefreshBuffer(plugin, this);
this.primaryGroup = plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION).apply(this);
this.userData = new UserCache(this);
getPlugin().getApiProvider().getEventFactory().handleUserCacheLoad(this, userData);
this.cachedData = new UserCache(this);
getPlugin().getApiProvider().getEventFactory().handleUserCacheLoad(this, cachedData);
}
@Override
@ -168,24 +169,20 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
* Sets up the UserData cache
* Blocking call.
*/
public synchronized void preCalculateData() {
public void preCalculateData() {
// first try to refresh any existing permissions
refreshPermissions();
refreshBuffer.requestDirectly();
// pre-calc the allowall & global contexts
// since contexts change so frequently, it's not worth trying to calculate any more than this.
userData.preCalculate(Contexts.allowAll());
userData.preCalculate(Contexts.global());
cachedData.preCalculate(Contexts.allowAll());
cachedData.preCalculate(Contexts.global());
}
/**
* Refresh and re-assign the users permissions
* Blocking call.
*/
private synchronized void refreshPermissions() {
userData.recalculatePermissions();
userData.recalculateMeta();
getPlugin().getApiProvider().getEventFactory().handleUserDataRecalculate(this, userData);
public CompletableFuture<Void> 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<UserIdentifie
private final User user;
private UserRefreshBuffer(LuckPermsPlugin plugin, User user) {
super(250L, 50L, plugin.getScheduler().async());
super(50L, 5L, plugin.getScheduler().async());
this.user = user;
}
@Override
protected Void perform() {
user.refreshPermissions();
return null;
return user.reloadCachedData().join();
}
}

View File

@ -400,6 +400,7 @@ public abstract class ConfigurateDao extends AbstractDao {
} finally {
user.getIoLock().unlock();
}
user.getRefreshBuffer().requestDirectly();
return true;
}
@ -495,6 +496,7 @@ public abstract class ConfigurateDao extends AbstractDao {
} finally {
group.getIoLock().unlock();
}
group.getRefreshBuffer().requestDirectly();
return true;
}
@ -528,6 +530,7 @@ public abstract class ConfigurateDao extends AbstractDao {
group.getIoLock().unlock();
}
}
group.getRefreshBuffer().requestDirectly();
return true;
}

View File

@ -331,8 +331,8 @@ public class MongoDao extends AbstractDao {
return reportException(e);
} finally {
user.getIoLock().unlock();
user.getRefreshBuffer().requestDirectly();
}
user.getRefreshBuffer().requestDirectly();
return true;
}
@ -433,6 +433,7 @@ public class MongoDao extends AbstractDao {
} finally {
group.getIoLock().unlock();
}
group.getRefreshBuffer().requestDirectly();
return true;
}
@ -444,22 +445,23 @@ public class MongoDao extends AbstractDao {
MongoCollection<Document> c = database.getCollection(prefix + "groups");
try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) {
if (cursor.hasNext()) {
Document d = cursor.next();
group.setEnduringNodes(revert((Map<String, Boolean>) 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<String, Boolean>) 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

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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<SubjectReference> sortSubjects(Collection<SubjectReference> s);
Contexts calculateContexts(ImmutableContextSet contextSet);
void invalidateAllCaches(LPSubject.CacheLevel cacheLevel);
}

View File

@ -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<PermissionProcessor> 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

View File

@ -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

View File

@ -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<ImmutableContextSet, NodeTree> permissionCache = Caffeine.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(contexts -> {
// TODO move this away from NodeTree
Map<String, Boolean> 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<ImmutableContextSet, ImmutableList<SubjectReference>> parentCache = Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(contexts -> {
Set<SubjectReference> 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<SubjectReference> getParents(ImmutableContextSet contexts) {
return parentCache.get(contexts);
ImmutableSet.Builder<SubjectReference> 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<String> getOption(ImmutableContextSet contexts, String s) {
Optional<String> 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<String> 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<String> getChatMeta(ImmutableContextSet contexts, ChatMetaType type) {
MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, plugin.getService().calculateContexts(contexts));
return Optional.ofNullable(metaAccumulator.getStack(type).toFormattedString());
}
private Optional<String> getMeta(ImmutableContextSet contexts, String key) {
MetaAccumulator metaAccumulator = parent.accumulateMeta(null, null, plugin.getService().calculateContexts(contexts));
ListMultimap<String, String> meta = metaAccumulator.getMeta();
List<String> 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();
}
}
}

View File

@ -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<CommandSource> getCommandSource() {
final UUID uuid = plugin.getUuidCache().getExternalUUID(parent.getUuid());
Optional<Player> 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<SubjectReference> getParents(ImmutableContextSet contexts) {
ImmutableSet.Builder<SubjectReference> 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<String> 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();
}
}

View File

@ -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()) {

View File

@ -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());
}
}