Refactor the way holder caches are invalidated in order to improve consistency (#734)
This commit is contained in:
parent
b8a1871cf1
commit
a6facf7492
@ -55,11 +55,6 @@ public class BukkitConfigAdapter extends AbstractConfigurationAdapter implements
|
||||
this.configuration = YamlConfiguration.loadConfiguration(this.file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String path) {
|
||||
return this.configuration.contains(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String path, String def) {
|
||||
return this.configuration.getString(path, def);
|
||||
|
@ -403,7 +403,7 @@ public class VaultPermissionHook extends AbstractVaultPermission {
|
||||
void holderSave(PermissionHolder holder) {
|
||||
if (holder.getType().isUser()) {
|
||||
User u = (User) holder;
|
||||
this.plugin.getStorage().saveUser(u).thenRunAsync(() -> u.getRefreshBuffer().request(), this.plugin.getBootstrap().getScheduler().async());
|
||||
this.plugin.getStorage().saveUser(u).thenRunAsync(() -> u.reloadCachedData(), this.plugin.getBootstrap().getScheduler().async());
|
||||
}
|
||||
if (holder.getType().isGroup()) {
|
||||
Group g = (Group) holder;
|
||||
|
@ -61,11 +61,6 @@ public class BungeeConfigAdapter extends AbstractConfigurationAdapter implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String path) {
|
||||
return this.configuration.contains(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String path, String def) {
|
||||
return this.configuration.getString(path, def);
|
||||
|
@ -98,7 +98,7 @@ public class ApiPermissionHolder implements me.lucko.luckperms.api.PermissionHol
|
||||
@Nonnull
|
||||
@Override
|
||||
public CompletableFuture<Void> refreshCachedData() {
|
||||
return this.handle.getRefreshBuffer().request();
|
||||
return this.handle.reloadCachedData();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -99,7 +99,7 @@ public final class ApiUser extends ApiPermissionHolder implements me.lucko.luckp
|
||||
@Override
|
||||
@Deprecated
|
||||
public void refreshPermissions() {
|
||||
this.handle.getRefreshBuffer().requestDirectly();
|
||||
this.handle.reloadCachedData().join();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,9 +25,9 @@
|
||||
|
||||
package me.lucko.luckperms.common.caching;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
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;
|
||||
@ -43,7 +43,6 @@ import me.lucko.luckperms.common.calculators.PermissionCalculatorMetadata;
|
||||
import me.lucko.luckperms.common.metastacking.SimpleMetaStack;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -51,6 +50,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link CachedData}.
|
||||
@ -65,16 +65,16 @@ public abstract class AbstractCachedData implements CachedData {
|
||||
/**
|
||||
* The cache used for {@link PermissionCache} instances.
|
||||
*/
|
||||
private final LoadingCache<Contexts, PermissionCache> permission = Caffeine.newBuilder()
|
||||
private final AsyncLoadingCache<Contexts, PermissionCache> permission = Caffeine.newBuilder()
|
||||
.expireAfterAccess(2, TimeUnit.MINUTES)
|
||||
.build(new PermissionCacheLoader());
|
||||
.buildAsync(new PermissionCacheLoader());
|
||||
|
||||
/**
|
||||
* The cache used for {@link MetaCache} instances.
|
||||
*/
|
||||
private final LoadingCache<MetaContexts, MetaCache> meta = Caffeine.newBuilder()
|
||||
private final AsyncLoadingCache<MetaContexts, MetaCache> meta = Caffeine.newBuilder()
|
||||
.expireAfterAccess(2, TimeUnit.MINUTES)
|
||||
.build(new MetaCacheLoader());
|
||||
.buildAsync(new MetaCacheLoader());
|
||||
|
||||
public AbstractCachedData(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
@ -190,7 +190,7 @@ public abstract class AbstractCachedData implements CachedData {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
|
||||
//noinspection ConstantConditions
|
||||
return this.permission.get(contexts);
|
||||
return this.permission.get(contexts).join();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@ -199,7 +199,7 @@ public abstract class AbstractCachedData implements CachedData {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
|
||||
//noinspection ConstantConditions
|
||||
return this.meta.get(contexts);
|
||||
return this.meta.get(contexts).join();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@ -233,13 +233,13 @@ public abstract class AbstractCachedData implements CachedData {
|
||||
@Override
|
||||
public void recalculatePermissions(@Nonnull Contexts contexts) {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
this.permission.refresh(contexts);
|
||||
this.permission.synchronous().refresh(contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculateMeta(@Nonnull MetaContexts contexts) {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
this.meta.refresh(contexts);
|
||||
this.meta.synchronous().refresh(contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -254,13 +254,19 @@ public abstract class AbstractCachedData implements CachedData {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
|
||||
// get the previous value - to use when recalculating
|
||||
PermissionCache previous = this.permission.getIfPresent(contexts);
|
||||
CompletableFuture<PermissionCache> previous = this.permission.getIfPresent(contexts);
|
||||
|
||||
// invalidate the entry
|
||||
this.permission.invalidate(contexts);
|
||||
// invalidate any previous setting
|
||||
this.permission.synchronous().invalidate(contexts);
|
||||
|
||||
// repopulate the cache
|
||||
return CompletableFuture.supplyAsync(() -> this.permission.get(contexts, c -> calculatePermissions(c, previous)));
|
||||
// if the previous value is already calculated, use it when recalculating.
|
||||
PermissionCache value = getIfReady(previous);
|
||||
if (value != null) {
|
||||
return this.permission.get(contexts, c -> calculatePermissions(c, value));
|
||||
}
|
||||
|
||||
// otherwise, just calculate a new value
|
||||
return this.permission.get(contexts);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@ -269,13 +275,19 @@ public abstract class AbstractCachedData implements CachedData {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
|
||||
// get the previous value - to use when recalculating
|
||||
MetaCache previous = this.meta.getIfPresent(contexts);
|
||||
CompletableFuture<MetaCache> previous = this.meta.getIfPresent(contexts);
|
||||
|
||||
// invalidate the entry
|
||||
this.meta.invalidate(contexts);
|
||||
// invalidate any previous setting
|
||||
this.meta.synchronous().invalidate(contexts);
|
||||
|
||||
// repopulate the cache
|
||||
return CompletableFuture.supplyAsync(() -> this.meta.get(contexts, c -> calculateMeta(c, previous)));
|
||||
// if the previous value is already calculated, use it when recalculating.
|
||||
MetaCache value = getIfReady(previous);
|
||||
if (value != null) {
|
||||
return this.meta.get(contexts, c -> calculateMeta(c, value));
|
||||
}
|
||||
|
||||
// otherwise, just calculate a new value
|
||||
return this.meta.get(contexts);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@ -287,30 +299,34 @@ public abstract class AbstractCachedData implements CachedData {
|
||||
|
||||
@Override
|
||||
public void recalculatePermissions() {
|
||||
Set<Contexts> keys = this.permission.asMap().keySet();
|
||||
Set<Contexts> keys = this.permission.synchronous().asMap().keySet();
|
||||
keys.forEach(this::recalculatePermissions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recalculateMeta() {
|
||||
Set<MetaContexts> keys = this.meta.asMap().keySet();
|
||||
Set<MetaContexts> keys = this.meta.synchronous().asMap().keySet();
|
||||
keys.forEach(this::recalculateMeta);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CompletableFuture<Void> reloadPermissions() {
|
||||
Set<Contexts> keys = new HashSet<>(this.permission.asMap().keySet());
|
||||
Set<Contexts> keys = this.permission.synchronous().asMap().keySet();
|
||||
return CompletableFuture.allOf(keys.stream().map(this::reloadPermissions).toArray(CompletableFuture[]::new));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CompletableFuture<Void> reloadMeta() {
|
||||
Set<MetaContexts> keys = new HashSet<>(this.meta.asMap().keySet());
|
||||
Set<MetaContexts> keys = this.meta.synchronous().asMap().keySet();
|
||||
return CompletableFuture.allOf(keys.stream().map(this::reloadMeta).toArray(CompletableFuture[]::new));
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> reloadAll() {
|
||||
return CompletableFuture.allOf(reloadPermissions(), reloadMeta());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preCalculate(@Nonnull Contexts contexts) {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
@ -324,34 +340,45 @@ public abstract class AbstractCachedData implements CachedData {
|
||||
@Override
|
||||
public void invalidatePermissions(@Nonnull Contexts contexts) {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
this.permission.invalidate(contexts);
|
||||
this.permission.synchronous().invalidate(contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateMeta(@Nonnull MetaContexts contexts) {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
this.meta.invalidate(contexts);
|
||||
this.meta.synchronous().invalidate(contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateMeta(@Nonnull Contexts contexts) {
|
||||
Objects.requireNonNull(contexts, "contexts");
|
||||
this.meta.invalidate(getDefaultMetaContexts(contexts));
|
||||
this.meta.synchronous().invalidate(getDefaultMetaContexts(contexts));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidatePermissionCalculators() {
|
||||
this.permission.asMap().values().forEach(PermissionCache::invalidateCache);
|
||||
this.permission.synchronous().asMap().values().forEach(PermissionCache::invalidateCache);
|
||||
}
|
||||
|
||||
public void invalidateCaches() {
|
||||
this.permission.invalidateAll();
|
||||
this.meta.invalidateAll();
|
||||
this.permission.synchronous().invalidateAll();
|
||||
this.meta.synchronous().invalidateAll();
|
||||
}
|
||||
|
||||
public void doCacheCleanup() {
|
||||
this.permission.cleanUp();
|
||||
this.meta.cleanUp();
|
||||
this.permission.synchronous().cleanUp();
|
||||
this.meta.synchronous().cleanUp();
|
||||
}
|
||||
|
||||
private static boolean isReady(@Nullable CompletableFuture<?> future) {
|
||||
return (future != null) && future.isDone()
|
||||
&& !future.isCompletedExceptionally()
|
||||
&& (future.join() != null);
|
||||
}
|
||||
|
||||
/** Returns the current value or null if either not done or failed. */
|
||||
private static <V> V getIfReady(@Nullable CompletableFuture<V> future) {
|
||||
return isReady(future) ? future.join() : null;
|
||||
}
|
||||
|
||||
private final class PermissionCacheLoader implements CacheLoader<Contexts, PermissionCache> {
|
||||
|
@ -108,7 +108,7 @@ public final class StorageAssistant {
|
||||
return;
|
||||
}
|
||||
|
||||
user.getRefreshBuffer().requestDirectly();
|
||||
user.reloadCachedData().join();
|
||||
|
||||
Optional<InternalMessagingService> messagingService = plugin.getMessagingService();
|
||||
if (messagingService.isPresent() && plugin.getConfiguration().get(ConfigKeys.AUTO_PUSH_UPDATES)) {
|
||||
|
@ -36,8 +36,6 @@ public interface ConfigurationAdapter {
|
||||
|
||||
void reload();
|
||||
|
||||
boolean contains(String path);
|
||||
|
||||
String getString(String path, String def);
|
||||
|
||||
int getInt(String path, int def);
|
||||
|
@ -29,7 +29,6 @@ import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.nodetype.types.DisplayNameType;
|
||||
import me.lucko.luckperms.common.api.delegates.model.ApiGroup;
|
||||
import me.lucko.luckperms.common.buffers.BufferedRequest;
|
||||
import me.lucko.luckperms.common.buffers.Cache;
|
||||
import me.lucko.luckperms.common.caching.GroupCachedData;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
@ -61,34 +60,66 @@ public class Group extends PermissionHolder implements Identifiable<String> {
|
||||
*/
|
||||
private final GroupCachedData cachedData;
|
||||
|
||||
/**
|
||||
* The group's cached data refresh buffer
|
||||
*/
|
||||
private final GroupRefreshBuffer refreshBuffer;
|
||||
|
||||
public Group(String name, LuckPermsPlugin plugin) {
|
||||
super(name, plugin);
|
||||
super(plugin);
|
||||
this.name = name.toLowerCase();
|
||||
|
||||
this.refreshBuffer = new GroupRefreshBuffer(plugin, this);
|
||||
this.cachedData = new GroupCachedData(this);
|
||||
getPlugin().getEventFactory().handleGroupCacheLoad(this, this.cachedData);
|
||||
|
||||
// invalidate our caches when data is updated
|
||||
getStateListeners().add(this.refreshBuffer::request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidateCache() {
|
||||
super.invalidateCache();
|
||||
|
||||
// invalidate our caches
|
||||
this.weightCache.invalidate();
|
||||
this.displayNameCache.invalidate();
|
||||
super.invalidateCache();
|
||||
}
|
||||
|
||||
// name getters
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getObjectName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFriendlyName() {
|
||||
Optional<String> dn = getDisplayName();
|
||||
return dn.map(s -> this.name + " (" + s + ")").orElse(this.name);
|
||||
}
|
||||
|
||||
public Optional<String> getDisplayName() {
|
||||
return this.displayNameCache.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a display name value exactly matching a specific context.
|
||||
*
|
||||
* <p>Note that the behaviour of {@link #getDisplayName()} is not the same as this.</p>
|
||||
*
|
||||
* @param contextSet the contexts to lookup in
|
||||
* @return the display name
|
||||
*/
|
||||
public Optional<String> getDisplayName(ContextSet contextSet) {
|
||||
for (Node n : getData(NodeMapType.ENDURING).immutable().get(contextSet.makeImmutable())) {
|
||||
Optional<DisplayNameType> displayName = n.getTypeData(DisplayNameType.KEY);
|
||||
if (displayName.isPresent()) {
|
||||
return Optional.of(displayName.get().getDisplayName());
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public ApiGroup getApiDelegate() {
|
||||
return this.apiDelegate;
|
||||
}
|
||||
@ -98,36 +129,6 @@ public class Group extends PermissionHolder implements Identifiable<String> {
|
||||
return this.cachedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedRequest<Void> getRefreshBuffer() {
|
||||
return this.refreshBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Optional<String> getDisplayName() {
|
||||
return this.displayNameCache.get();
|
||||
}
|
||||
|
||||
public Optional<String> getDisplayName(ContextSet contextSet) {
|
||||
for (Node n : getData(NodeMapType.ENDURING).immutable().get(contextSet.makeImmutable())) {
|
||||
Optional<DisplayNameType> displayName = n.getTypeData(DisplayNameType.KEY);
|
||||
if (displayName.isPresent()) {
|
||||
return Optional.of(displayName.get().getDisplayName());
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFriendlyName() {
|
||||
Optional<String> dn = getDisplayName();
|
||||
return dn.map(s -> this.name + " (" + s + ")").orElse(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt getWeight() {
|
||||
return this.weightCache.get();
|
||||
@ -138,11 +139,10 @@ public class Group extends PermissionHolder implements Identifiable<String> {
|
||||
return HolderType.GROUP;
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> reloadCachedData() {
|
||||
return CompletableFuture.allOf(
|
||||
this.cachedData.reloadPermissions(),
|
||||
this.cachedData.reloadMeta()
|
||||
).thenAccept(n -> getPlugin().getEventFactory().handleGroupDataRecalculate(this, this.cachedData));
|
||||
@Override
|
||||
public CompletableFuture<Void> reloadCachedData() {
|
||||
return this.cachedData.reloadAll()
|
||||
.thenAccept(n -> getPlugin().getEventFactory().handleGroupDataRecalculate(this, this.cachedData));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -163,18 +163,4 @@ public class Group extends PermissionHolder implements Identifiable<String> {
|
||||
return "Group(name=" + this.name + ")";
|
||||
}
|
||||
|
||||
private static final class GroupRefreshBuffer extends BufferedRequest<Void> {
|
||||
private final Group group;
|
||||
|
||||
private GroupRefreshBuffer(LuckPermsPlugin plugin, Group group) {
|
||||
super(50L, 5L, plugin.getBootstrap().getScheduler().async());
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void perform() {
|
||||
return this.group.reloadCachedData().join();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,9 +40,7 @@ import me.lucko.luckperms.api.StandardNodeEquality;
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.buffers.BufferedRequest;
|
||||
import me.lucko.luckperms.common.caching.HolderCachedData;
|
||||
import me.lucko.luckperms.common.caching.handlers.StateListener;
|
||||
import me.lucko.luckperms.common.caching.type.MetaAccumulator;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.inheritance.InheritanceComparator;
|
||||
@ -67,7 +65,7 @@ import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Predicate;
|
||||
@ -95,18 +93,6 @@ import java.util.function.Predicate;
|
||||
*/
|
||||
public abstract class PermissionHolder {
|
||||
|
||||
/**
|
||||
* The name of this object.
|
||||
*
|
||||
* <p>Used as a base for identifying permission holding objects. Also acts
|
||||
* as a method for preventing circular inheritance issues.</p>
|
||||
*
|
||||
* @see User#getUuid()
|
||||
* @see Group#getName()
|
||||
* @see #getObjectName()
|
||||
*/
|
||||
private final String objectName;
|
||||
|
||||
/**
|
||||
* Reference to the main plugin instance
|
||||
* @see #getPlugin()
|
||||
@ -146,18 +132,15 @@ public abstract class PermissionHolder {
|
||||
private final Comparator<Group> inheritanceComparator = InheritanceComparator.getFor(this);
|
||||
|
||||
/**
|
||||
* A set of runnables which are called when this objects state changes.
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param plugin the plugin instance
|
||||
*/
|
||||
private final Set<StateListener> stateListeners = ConcurrentHashMap.newKeySet();
|
||||
|
||||
protected PermissionHolder(String objectName, LuckPermsPlugin plugin) {
|
||||
this.objectName = objectName;
|
||||
protected PermissionHolder(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public String getObjectName() {
|
||||
return this.objectName;
|
||||
}
|
||||
// getters
|
||||
|
||||
public LuckPermsPlugin getPlugin() {
|
||||
return this.plugin;
|
||||
@ -167,52 +150,6 @@ public abstract class PermissionHolder {
|
||||
return this.ioLock;
|
||||
}
|
||||
|
||||
public Set<StateListener> getStateListeners() {
|
||||
return this.stateListeners;
|
||||
}
|
||||
|
||||
protected void invalidateCache() {
|
||||
this.enduringNodes.invalidate();
|
||||
this.transientNodes.invalidate();
|
||||
|
||||
// Invalidate listeners
|
||||
for (StateListener listener : this.stateListeners) {
|
||||
try {
|
||||
listener.onStateChange();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the friendly name of this permission holder (for use in commands, etc)
|
||||
*
|
||||
* @return the holders "friendly" name
|
||||
*/
|
||||
public abstract String getFriendlyName();
|
||||
|
||||
/**
|
||||
* Gets the holders cached data
|
||||
*
|
||||
* @return the holders cached data
|
||||
*/
|
||||
public abstract HolderCachedData<?> getCachedData();
|
||||
|
||||
/**
|
||||
* Gets the holders refresh buffer
|
||||
*
|
||||
* @return the holders refresh buffer
|
||||
*/
|
||||
public abstract BufferedRequest<Void> getRefreshBuffer();
|
||||
|
||||
/**
|
||||
* Returns the type of this PermissionHolder.
|
||||
*
|
||||
* @return this holders type
|
||||
*/
|
||||
public abstract HolderType getType();
|
||||
|
||||
public Comparator<Group> getInheritanceComparator() {
|
||||
return this.inheritanceComparator;
|
||||
}
|
||||
@ -236,6 +173,51 @@ public abstract class PermissionHolder {
|
||||
return this.transientNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique name of this holder object.
|
||||
*
|
||||
* <p>Used as a base for identifying permission holding objects. Also acts
|
||||
* as a method for preventing circular inheritance issues.</p>
|
||||
*
|
||||
* @return the object name
|
||||
*/
|
||||
public abstract String getObjectName();
|
||||
|
||||
/**
|
||||
* Gets the friendly name of this permission holder (for use in commands, etc)
|
||||
*
|
||||
* @return the holders "friendly" name
|
||||
*/
|
||||
public abstract String getFriendlyName();
|
||||
|
||||
/**
|
||||
* Gets the holders cached data
|
||||
*
|
||||
* @return the holders cached data
|
||||
*/
|
||||
public abstract HolderCachedData<?> getCachedData();
|
||||
|
||||
/**
|
||||
* Returns the type of this PermissionHolder.
|
||||
*
|
||||
* @return this holders type
|
||||
*/
|
||||
public abstract HolderType getType();
|
||||
|
||||
/**
|
||||
* Reloads the holder's cached data.
|
||||
*
|
||||
* @return a future encapsulating the result
|
||||
*/
|
||||
public abstract CompletableFuture<Void> reloadCachedData();
|
||||
|
||||
protected void invalidateCache() {
|
||||
this.enduringNodes.invalidate();
|
||||
this.transientNodes.invalidate();
|
||||
|
||||
reloadCachedData();
|
||||
}
|
||||
|
||||
public void setNodes(NodeMapType type, Set<Node> set) {
|
||||
getData(type).setContent(set);
|
||||
invalidateCache();
|
||||
|
@ -26,16 +26,18 @@
|
||||
package me.lucko.luckperms.common.model;
|
||||
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
import me.lucko.luckperms.common.buffers.BufferedRequest;
|
||||
import me.lucko.luckperms.common.caching.UserCachedData;
|
||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.primarygroup.CachedPrimaryGroupHolder;
|
||||
import me.lucko.luckperms.common.primarygroup.PrimaryGroupHolder;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class User extends PermissionHolder implements Identifiable<UserIdentifier> {
|
||||
|
||||
/**
|
||||
@ -46,6 +48,7 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
/**
|
||||
* The last known username of a player
|
||||
*/
|
||||
@Nullable
|
||||
private String name = null;
|
||||
|
||||
/**
|
||||
@ -58,14 +61,11 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
*/
|
||||
private final UserCachedData cachedData;
|
||||
|
||||
private final UserRefreshBuffer refreshBuffer;
|
||||
|
||||
public User(UUID uuid, String name, LuckPermsPlugin plugin) {
|
||||
super(uuid.toString(), plugin);
|
||||
super(plugin);
|
||||
this.uuid = uuid;
|
||||
setName(name, false);
|
||||
|
||||
this.refreshBuffer = new UserRefreshBuffer(plugin, this);
|
||||
this.primaryGroup = plugin.getConfiguration().get(ConfigKeys.PRIMARY_GROUP_CALCULATION).apply(this);
|
||||
|
||||
this.cachedData = new UserCachedData(this);
|
||||
@ -76,22 +76,27 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
this(uuid, null, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidateCache() {
|
||||
super.invalidateCache();
|
||||
|
||||
// invalidate our caches
|
||||
if (this.primaryGroup instanceof CachedPrimaryGroupHolder) {
|
||||
((CachedPrimaryGroupHolder) this.primaryGroup).invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
public PrimaryGroupHolder getPrimaryGroup() {
|
||||
return this.primaryGroup;
|
||||
public Optional<String> getName() {
|
||||
return Optional.ofNullable(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserCachedData getCachedData() {
|
||||
return this.cachedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedRequest<Void> getRefreshBuffer() {
|
||||
return this.refreshBuffer;
|
||||
public String getObjectName() {
|
||||
return this.uuid.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,8 +104,18 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
return UserIdentifier.of(this.uuid, this.name);
|
||||
}
|
||||
|
||||
public Optional<String> getName() {
|
||||
return Optional.ofNullable(this.name);
|
||||
@Override
|
||||
public String getFriendlyName() {
|
||||
return this.name != null ? this.name : this.uuid.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserCachedData getCachedData() {
|
||||
return this.cachedData;
|
||||
}
|
||||
|
||||
public PrimaryGroupHolder getPrimaryGroup() {
|
||||
return this.primaryGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,11 +167,6 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFriendlyName() {
|
||||
return this.name != null ? this.name : this.uuid.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HolderType getType() {
|
||||
return HolderType.USER;
|
||||
@ -168,7 +178,7 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
*/
|
||||
public void preCalculateData() {
|
||||
// first try to refresh any existing permissions
|
||||
this.refreshBuffer.requestDirectly();
|
||||
reloadCachedData().join();
|
||||
|
||||
// pre-calc the allowall & global contexts
|
||||
// since contexts change so frequently, it's not worth trying to calculate any more than this.
|
||||
@ -176,11 +186,10 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
this.cachedData.preCalculate(Contexts.global());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> reloadCachedData() {
|
||||
return CompletableFuture.allOf(
|
||||
this.cachedData.reloadPermissions(),
|
||||
this.cachedData.reloadMeta()
|
||||
).thenAccept(n -> getPlugin().getEventFactory().handleUserDataRecalculate(this, this.cachedData));
|
||||
return this.cachedData.reloadAll()
|
||||
.thenAccept(n -> getPlugin().getEventFactory().handleUserDataRecalculate(this, this.cachedData));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -197,13 +206,6 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clearNodes(boolean giveDefault) {
|
||||
super.clearNodes();
|
||||
if (giveDefault) {
|
||||
getPlugin().getUserManager().giveDefaultIfNeeded(this, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
@ -222,18 +224,4 @@ public class User extends PermissionHolder implements Identifiable<UserIdentifie
|
||||
return "User(uuid=" + this.uuid + ")";
|
||||
}
|
||||
|
||||
private static final class UserRefreshBuffer extends BufferedRequest<Void> {
|
||||
private final User user;
|
||||
|
||||
private UserRefreshBuffer(LuckPermsPlugin plugin, User user) {
|
||||
super(50L, 5L, plugin.getBootstrap().getScheduler().async());
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void perform() {
|
||||
return this.user.reloadCachedData().join();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,16 +26,15 @@
|
||||
package me.lucko.luckperms.common.primarygroup;
|
||||
|
||||
import me.lucko.luckperms.common.buffers.Cache;
|
||||
import me.lucko.luckperms.common.caching.handlers.StateListener;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.factory.NodeFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link StateListener} which caches all lookups.
|
||||
* Abstract implementation of {@link PrimaryGroupHolder} which caches all lookups.
|
||||
*/
|
||||
public abstract class CachedPrimaryGroupHolder extends StoredHolder implements StateListener {
|
||||
public abstract class CachedPrimaryGroupHolder extends StoredHolder {
|
||||
|
||||
// cache lookups
|
||||
private final Cache<String> cache = new Cache<String>() {
|
||||
@ -48,20 +47,18 @@ public abstract class CachedPrimaryGroupHolder extends StoredHolder implements S
|
||||
|
||||
public CachedPrimaryGroupHolder(User user) {
|
||||
super(user);
|
||||
user.getStateListeners().add(this);
|
||||
}
|
||||
|
||||
protected abstract String calculateValue();
|
||||
|
||||
public void invalidate() {
|
||||
this.cache.invalidate();;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getValue() {
|
||||
String s = this.cache.get();
|
||||
return s != null ? s : getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChange() {
|
||||
this.cache.invalidate();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ public abstract class AbstractConfigurateDao extends AbstractDao {
|
||||
} finally {
|
||||
user.getIoLock().unlock();
|
||||
}
|
||||
user.getRefreshBuffer().requestDirectly();
|
||||
user.reloadCachedData().join();
|
||||
return user;
|
||||
}
|
||||
|
||||
@ -261,7 +261,7 @@ public abstract class AbstractConfigurateDao extends AbstractDao {
|
||||
} finally {
|
||||
group.getIoLock().unlock();
|
||||
}
|
||||
group.getRefreshBuffer().requestDirectly();
|
||||
group.reloadCachedData().join();
|
||||
return group;
|
||||
}
|
||||
|
||||
@ -295,7 +295,7 @@ public abstract class AbstractConfigurateDao extends AbstractDao {
|
||||
group.getIoLock().unlock();
|
||||
}
|
||||
}
|
||||
group.getRefreshBuffer().requestDirectly();
|
||||
group.reloadCachedData().join();
|
||||
return Optional.of(group);
|
||||
}
|
||||
|
||||
|
@ -286,7 +286,7 @@ public class MongoDao extends AbstractDao {
|
||||
} finally {
|
||||
user.getIoLock().unlock();
|
||||
}
|
||||
user.getRefreshBuffer().requestDirectly();
|
||||
user.reloadCachedData().join();
|
||||
return user;
|
||||
}
|
||||
|
||||
@ -357,7 +357,7 @@ public class MongoDao extends AbstractDao {
|
||||
} finally {
|
||||
group.getIoLock().unlock();
|
||||
}
|
||||
group.getRefreshBuffer().requestDirectly();
|
||||
group.reloadCachedData().join();
|
||||
return group;
|
||||
}
|
||||
|
||||
@ -388,7 +388,7 @@ public class MongoDao extends AbstractDao {
|
||||
group.getIoLock().unlock();
|
||||
}
|
||||
}
|
||||
group.getRefreshBuffer().requestDirectly();
|
||||
group.reloadCachedData().join();
|
||||
return Optional.of(group);
|
||||
}
|
||||
|
||||
|
@ -356,7 +356,7 @@ public class SqlDao extends AbstractDao {
|
||||
} finally {
|
||||
user.getIoLock().unlock();
|
||||
}
|
||||
user.getRefreshBuffer().requestDirectly();
|
||||
user.reloadCachedData().join();
|
||||
return user;
|
||||
}
|
||||
|
||||
@ -595,7 +595,7 @@ public class SqlDao extends AbstractDao {
|
||||
} finally {
|
||||
group.getIoLock().unlock();
|
||||
}
|
||||
group.getRefreshBuffer().requestDirectly();
|
||||
group.reloadCachedData().join();
|
||||
return Optional.of(group);
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ public class ExpireTemporaryTask implements Runnable {
|
||||
if (user.auditTemporaryPermissions()) {
|
||||
this.plugin.getStorage().saveUser(user);
|
||||
if (!groupChanges) {
|
||||
user.getRefreshBuffer().request();
|
||||
user.reloadCachedData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,11 +55,6 @@ public class NukkitConfigAdapter extends AbstractConfigurationAdapter implements
|
||||
this.configuration = new Config(this.file, Config.YAML);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String path) {
|
||||
return this.configuration.exists(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String path, String def) {
|
||||
return this.configuration.getString(path, def);
|
||||
|
@ -85,11 +85,6 @@ public class SpongeConfigAdapter extends AbstractConfigurationAdapter implements
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String path) {
|
||||
return !resolvePath(path).isVirtual();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String path, String def) {
|
||||
return resolvePath(path).getString(def);
|
||||
|
@ -37,6 +37,13 @@ public class SpongeGroup extends Group implements SpongePermissionHolder {
|
||||
this.spongeData = new GroupSubject(plugin, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidateCache() {
|
||||
super.invalidateCache();
|
||||
|
||||
this.spongeData.fireUpdateEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupSubject sponge() {
|
||||
return this.spongeData;
|
||||
|
@ -48,6 +48,13 @@ public class SpongeUser extends User implements SpongePermissionHolder {
|
||||
this.spongeData = new UserSubject(plugin, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invalidateCache() {
|
||||
super.invalidateCache();
|
||||
|
||||
this.spongeData.fireUpdateEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSubject sponge() {
|
||||
return this.spongeData;
|
||||
|
@ -66,12 +66,11 @@ public abstract class HolderSubject<T extends PermissionHolder> implements LPSub
|
||||
this.plugin = plugin;
|
||||
this.subjectData = new HolderSubjectData(plugin.getService(), NodeMapType.ENDURING, parent, this);
|
||||
this.transientSubjectData = new HolderSubjectData(plugin.getService(), NodeMapType.TRANSIENT, parent, this);
|
||||
}
|
||||
|
||||
// fire update event
|
||||
parent.getStateListeners().add(() -> {
|
||||
plugin.getUpdateEventHandler().fireUpdateEvent(this.subjectData);
|
||||
plugin.getUpdateEventHandler().fireUpdateEvent(this.transientSubjectData);
|
||||
});
|
||||
public void fireUpdateEvent() {
|
||||
this.plugin.getUpdateEventHandler().fireUpdateEvent(this.subjectData);
|
||||
this.plugin.getUpdateEventHandler().fireUpdateEvent(this.transientSubjectData);
|
||||
}
|
||||
|
||||
public T getParent() {
|
||||
|
@ -450,7 +450,7 @@ public class HolderSubjectData implements LPSubjectData {
|
||||
// don't bother saving to primary storage. just refresh
|
||||
if (t.getType().isUser()) {
|
||||
User user = ((User) t);
|
||||
return user.getRefreshBuffer().request();
|
||||
return user.reloadCachedData();
|
||||
} else {
|
||||
return this.service.getPlugin().getUpdateTaskBuffer().request();
|
||||
}
|
||||
@ -465,7 +465,7 @@ public class HolderSubjectData implements LPSubjectData {
|
||||
fut.complete(null);
|
||||
}
|
||||
|
||||
user.getRefreshBuffer().request().thenAccept(fut::complete);
|
||||
user.reloadCachedData().thenAccept(fut::complete);
|
||||
}, this.service.getPlugin().getBootstrap().getScheduler().async());
|
||||
return fut;
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user