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
Unverified
parent d75b29f51d
commit a115ff8ce2
51 changed files with 1267 additions and 579 deletions
@@ -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;
@@ -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();
@@ -82,7 +82,7 @@ public final class ApiUser extends ApiPermissionHolder implements User {
@Override
public UserData getCachedData() {
return handle.getUserData();
return handle.getCachedData();
}
@Override
@@ -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();
}
}
@@ -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)
);
}
}
@@ -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
@@ -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);
}
}
@@ -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)
);
}
}
@@ -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
@@ -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;
}
@@ -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()) {
@@ -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\"";
}
@@ -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);
@@ -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;
}
@@ -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;
}
@@ -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);
}
@@ -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();
}
}
}
@@ -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.
*
@@ -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();
}
}
@@ -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;
}
@@ -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
@@ -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
@@ -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();
}
}
}
@@ -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();
}
}
}