Implement full caching for non user/group subjects
This commit is contained in:
parent
dd3a78ada8
commit
83b0f62d59
@ -65,7 +65,7 @@ public interface ContextSet {
|
||||
* @return a new ImmutableContextSet representing the pairs in the iterable
|
||||
* @throws NullPointerException if the iterable is null
|
||||
*/
|
||||
static ImmutableContextSet fromEntries(Iterable<Map.Entry<String, String>> iterable) {
|
||||
static ImmutableContextSet fromEntries(Iterable<? extends Map.Entry<String, String>> iterable) {
|
||||
return ImmutableContextSet.fromEntries(iterable);
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ public final class ImmutableContextSet implements ContextSet {
|
||||
* @return a new ImmutableContextSet representing the pairs in the iterable
|
||||
* @throws NullPointerException if the iterable is null
|
||||
*/
|
||||
public static ImmutableContextSet fromEntries(Iterable<Map.Entry<String, String>> iterable) {
|
||||
public static ImmutableContextSet fromEntries(Iterable<? extends Map.Entry<String, String>> iterable) {
|
||||
if (iterable == null) {
|
||||
throw new NullPointerException("iterable");
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public final class MutableContextSet implements ContextSet {
|
||||
* @return a new MutableContextSet representing the pairs in the iterable
|
||||
* @throws NullPointerException if the iterable is null
|
||||
*/
|
||||
public static MutableContextSet fromEntries(Iterable<Map.Entry<String, String>> iterable) {
|
||||
public static MutableContextSet fromEntries(Iterable<? extends Map.Entry<String, String>> iterable) {
|
||||
if (iterable == null) {
|
||||
throw new NullPointerException("iterable");
|
||||
}
|
||||
@ -277,7 +277,7 @@ public final class MutableContextSet implements ContextSet {
|
||||
* @param iterable an iterable of key value context pairs
|
||||
* @throws NullPointerException if iterable is null
|
||||
*/
|
||||
public void addAll(Iterable<Map.Entry<String, String>> iterable) {
|
||||
public void addAll(Iterable<? extends Map.Entry<String, String>> iterable) {
|
||||
if (iterable == null) {
|
||||
throw new NullPointerException("contexts");
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import me.lucko.luckperms.api.MetaUtils;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
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.constants.Patterns;
|
||||
import me.lucko.luckperms.common.utils.ShorthandParser;
|
||||
|
||||
@ -122,7 +123,7 @@ public class ImmutableNode implements Node {
|
||||
private long expireAt = 0L;
|
||||
|
||||
@Getter
|
||||
private final ContextSet contexts;
|
||||
private final ImmutableContextSet contexts;
|
||||
|
||||
// Cached state
|
||||
private final boolean isGroup;
|
||||
|
@ -104,22 +104,30 @@ import java.util.stream.StreamSupport;
|
||||
public class LPSpongePlugin implements LuckPermsPlugin {
|
||||
|
||||
private final Set<UUID> ignoringLogs = ConcurrentHashMap.newKeySet();
|
||||
|
||||
@Inject
|
||||
private Logger logger;
|
||||
|
||||
@Inject
|
||||
private Game game;
|
||||
|
||||
@Inject
|
||||
@ConfigDir(sharedRoot = false)
|
||||
private Path configDir;
|
||||
|
||||
private Scheduler scheduler = Sponge.getScheduler();
|
||||
|
||||
@Inject
|
||||
@SynchronousExecutor
|
||||
private SpongeExecutorService syncExecutor;
|
||||
|
||||
@Inject
|
||||
@AsynchronousExecutor
|
||||
private SpongeExecutorService asyncExecutor;
|
||||
|
||||
private LPTimings timings;
|
||||
private boolean lateLoad = false;
|
||||
|
||||
private LPConfiguration configuration;
|
||||
private SpongeUserManager userManager;
|
||||
private SpongeGroupManager groupManager;
|
||||
@ -419,9 +427,9 @@ public class LPSpongePlugin implements LuckPermsPlugin {
|
||||
@Override
|
||||
public LinkedHashMap<String, Object> getExtraInfo() {
|
||||
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("SubjectCollection count", service.getKnownSubjects().size());
|
||||
map.put("SubjectCollection count", service.getCollections().size());
|
||||
map.put("Subject count",
|
||||
service.getKnownSubjects().values().stream()
|
||||
service.getCollections().values().stream()
|
||||
.map(SubjectCollection::getAllSubjects)
|
||||
.flatMap(subjects -> StreamSupport.stream(subjects.spliterator(), false))
|
||||
.count()
|
||||
|
@ -183,4 +183,9 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
|
||||
public SubjectReference getDefaultSubject() {
|
||||
return SubjectReference.of("defaults", getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTransientHasPriority() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -242,4 +242,9 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
|
||||
public SubjectReference getDefaultSubject() {
|
||||
return SubjectReference.of("defaults", getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTransientHasPriority() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
import me.lucko.luckperms.api.Contexts;
|
||||
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.utils.ImmutableCollectors;
|
||||
import me.lucko.luckperms.sponge.LPSpongePlugin;
|
||||
import me.lucko.luckperms.sponge.contexts.SpongeCalculatorLink;
|
||||
@ -55,11 +56,9 @@ import me.lucko.luckperms.sponge.service.calculated.PermissionLookup;
|
||||
import me.lucko.luckperms.sponge.service.persisted.PersistedCollection;
|
||||
import me.lucko.luckperms.sponge.service.persisted.SubjectStorage;
|
||||
import me.lucko.luckperms.sponge.service.references.SubjectReference;
|
||||
import me.lucko.luckperms.sponge.service.simple.SimpleCollection;
|
||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
||||
|
||||
import org.spongepowered.api.plugin.PluginContainer;
|
||||
import org.spongepowered.api.service.context.Context;
|
||||
import org.spongepowered.api.service.context.ContextCalculator;
|
||||
import org.spongepowered.api.service.permission.PermissionDescription;
|
||||
import org.spongepowered.api.service.permission.PermissionService;
|
||||
@ -71,7 +70,6 @@ import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@ -89,14 +87,14 @@ public class LuckPermsService implements PermissionService {
|
||||
private final LPSpongePlugin plugin;
|
||||
private final SubjectStorage storage;
|
||||
private final SpongeUserManager userSubjects;
|
||||
private final SimpleCollection fallbackUserSubjects;
|
||||
private final PersistedCollection fallbackUserSubjects;
|
||||
private final SpongeGroupManager groupSubjects;
|
||||
private final SimpleCollection fallbackGroupSubjects;
|
||||
private final PersistedCollection fallbackGroupSubjects;
|
||||
private final PersistedCollection defaultSubjects;
|
||||
private final Set<PermissionDescription> descriptionSet;
|
||||
|
||||
private final Set<LoadingCache<PermissionLookup, Tristate>> localPermissionCaches;
|
||||
private final Set<LoadingCache<Set<Context>, List<SubjectReference>>> localParentCaches;
|
||||
private final Set<LoadingCache<ImmutableContextSet, Set<SubjectReference>>> localParentCaches;
|
||||
private final Set<LoadingCache<OptionLookup, Optional<String>>> localOptionCaches;
|
||||
|
||||
@Getter(value = AccessLevel.NONE)
|
||||
@ -104,7 +102,7 @@ public class LuckPermsService implements PermissionService {
|
||||
.build(new CacheLoader<String, LPSubjectCollection>() {
|
||||
@Override
|
||||
public LPSubjectCollection load(String s) {
|
||||
return new SimpleCollection(LuckPermsService.this, s);
|
||||
return new PersistedCollection(LuckPermsService.this, s, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,10 +121,10 @@ public class LuckPermsService implements PermissionService {
|
||||
storage = new SubjectStorage(new File(plugin.getDataFolder(), "local"));
|
||||
|
||||
userSubjects = plugin.getUserManager();
|
||||
fallbackUserSubjects = new SimpleCollection(this, "fallback-users");
|
||||
fallbackUserSubjects = new PersistedCollection(this, "fallback-users", true);
|
||||
groupSubjects = plugin.getGroupManager();
|
||||
fallbackGroupSubjects = new SimpleCollection(this, "fallback-groups");
|
||||
defaultSubjects = new PersistedCollection(this, "defaults");
|
||||
fallbackGroupSubjects = new PersistedCollection(this, "fallback-groups", true);
|
||||
defaultSubjects = new PersistedCollection(this, "defaults", false);
|
||||
defaultSubjects.loadAll();
|
||||
|
||||
collections.put(PermissionService.SUBJECTS_USER, userSubjects);
|
||||
@ -135,6 +133,16 @@ public class LuckPermsService implements PermissionService {
|
||||
collections.put("fallback-groups", fallbackGroupSubjects);
|
||||
collections.put("defaults", defaultSubjects);
|
||||
|
||||
for (String collection : storage.getSavedCollections()) {
|
||||
if (collections.asMap().containsKey(collection.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PersistedCollection c = new PersistedCollection(this, collection.toLowerCase(), true);
|
||||
c.loadAll();
|
||||
collections.put(c.getIdentifier(), c);
|
||||
}
|
||||
|
||||
descriptionSet = ConcurrentHashMap.newKeySet();
|
||||
}
|
||||
|
||||
@ -207,6 +215,26 @@ public class LuckPermsService implements PermissionService {
|
||||
);
|
||||
}
|
||||
|
||||
public void invalidatePermissionCaches() {
|
||||
for (LoadingCache<PermissionLookup, Tristate> c : localPermissionCaches) {
|
||||
c.invalidateAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateParentCaches() {
|
||||
for (LoadingCache<ImmutableContextSet, Set<SubjectReference>> c : localParentCaches) {
|
||||
c.invalidateAll();
|
||||
}
|
||||
invalidateOptionCaches();
|
||||
invalidatePermissionCaches();
|
||||
}
|
||||
|
||||
public void invalidateOptionCaches() {
|
||||
for (LoadingCache<OptionLookup, Optional<String>> c : localOptionCaches) {
|
||||
c.invalidateAll();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
|
@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.api.context.MutableContextSet;
|
||||
import me.lucko.luckperms.common.core.NodeBuilder;
|
||||
import me.lucko.luckperms.common.core.NodeFactory;
|
||||
@ -85,7 +86,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ContextSet, Map<String, Boolean>> getPermissions() {
|
||||
public Map<ImmutableContextSet, Map<String, Boolean>> getPermissions() {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) {
|
||||
Map<ContextSet, Map<String, Boolean>> perms = new HashMap<>();
|
||||
|
||||
@ -107,7 +108,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
|
||||
perms.get(contexts).put(n.getPermission(), n.getValue());
|
||||
}
|
||||
|
||||
ImmutableMap.Builder<ContextSet, Map<String, Boolean>> map = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<ImmutableContextSet, Map<String, Boolean>> map = ImmutableMap.builder();
|
||||
for (Map.Entry<ContextSet, Map<String, Boolean>> e : perms.entrySet()) {
|
||||
map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
|
||||
}
|
||||
@ -215,9 +216,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ContextSet, Set<SubjectReference>> getParents() {
|
||||
public Map<ImmutableContextSet, Set<SubjectReference>> getParents() {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PARENTS)) {
|
||||
Map<ContextSet, Set<SubjectReference>> parents = new HashMap<>();
|
||||
Map<ImmutableContextSet, Set<SubjectReference>> parents = new HashMap<>();
|
||||
|
||||
for (Node n : enduring ? holder.getNodes() : holder.getTransientNodes()) {
|
||||
if (!n.isGroupNode()) {
|
||||
@ -241,9 +242,9 @@ public class LuckPermsSubjectData implements LPSubjectData {
|
||||
parents.get(contexts).add(service.getGroupSubjects().get(n.getGroupName()).toReference());
|
||||
}
|
||||
|
||||
ImmutableMap.Builder<ContextSet, Set<SubjectReference>> map = ImmutableMap.builder();
|
||||
for (Map.Entry<ContextSet, Set<SubjectReference>> e : parents.entrySet()) {
|
||||
map.put(e.getKey().makeImmutable(), ImmutableSet.copyOf(e.getValue()));
|
||||
ImmutableMap.Builder<ImmutableContextSet, Set<SubjectReference>> map = ImmutableMap.builder();
|
||||
for (Map.Entry<ImmutableContextSet, Set<SubjectReference>> e : parents.entrySet()) {
|
||||
map.put(e.getKey(), ImmutableSet.copyOf(e.getValue()));
|
||||
}
|
||||
return map.build();
|
||||
}
|
||||
@ -373,7 +374,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ContextSet, Map<String, String>> getOptions() {
|
||||
public Map<ImmutableContextSet, Map<String, String>> getOptions() {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_OPTIONS)) {
|
||||
Map<ContextSet, Map<String, String>> options = new HashMap<>();
|
||||
Map<ContextSet, Integer> minPrefixPriority = new HashMap<>();
|
||||
@ -428,7 +429,7 @@ public class LuckPermsSubjectData implements LPSubjectData {
|
||||
}
|
||||
}
|
||||
|
||||
ImmutableMap.Builder<ContextSet, Map<String, String>> map = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<ImmutableContextSet, Map<String, String>> map = ImmutableMap.builder();
|
||||
for (Map.Entry<ContextSet, Map<String, String>> e : options.entrySet()) {
|
||||
map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
|
||||
}
|
||||
|
@ -83,7 +83,6 @@ public interface LPSubject extends Subject {
|
||||
|
||||
/* Compat */
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
default LPSubjectCollection getContainingCollection() {
|
||||
return getParentCollection().resolve(getService());
|
||||
|
@ -67,6 +67,8 @@ public interface LPSubjectCollection extends SubjectCollection {
|
||||
|
||||
SubjectReference getDefaultSubject();
|
||||
|
||||
boolean getTransientHasPriority();
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
default Subject getDefaults() {
|
||||
|
@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||
import me.lucko.luckperms.sponge.service.references.SubjectReference;
|
||||
|
||||
@ -47,7 +48,7 @@ public interface LPSubjectData extends SubjectData {
|
||||
|
||||
LPSubject getParentSubject();
|
||||
|
||||
Map<ContextSet, Map<String, Boolean>> getPermissions();
|
||||
Map<ImmutableContextSet, Map<String, Boolean>> getPermissions();
|
||||
|
||||
default Map<String, Boolean> getPermissions(ContextSet contexts) {
|
||||
return ImmutableMap.copyOf(getPermissions().getOrDefault(contexts, ImmutableMap.of()));
|
||||
@ -60,7 +61,7 @@ public interface LPSubjectData extends SubjectData {
|
||||
|
||||
boolean clearPermissions(ContextSet contexts);
|
||||
|
||||
Map<ContextSet, Set<SubjectReference>> getParents();
|
||||
Map<ImmutableContextSet, Set<SubjectReference>> getParents();
|
||||
|
||||
default Set<SubjectReference> getParents(ContextSet contexts) {
|
||||
return ImmutableSet.copyOf(getParents().getOrDefault(contexts, ImmutableSet.of()));
|
||||
@ -75,7 +76,7 @@ public interface LPSubjectData extends SubjectData {
|
||||
|
||||
boolean clearParents(ContextSet contexts);
|
||||
|
||||
Map<ContextSet, Map<String, String>> getOptions();
|
||||
Map<ImmutableContextSet, Map<String, String>> getOptions();
|
||||
|
||||
default Map<String, String> getOptions(ContextSet contexts) {
|
||||
return ImmutableMap.copyOf(getOptions().getOrDefault(contexts, ImmutableMap.of()));
|
||||
|
@ -22,28 +22,46 @@
|
||||
|
||||
package me.lucko.luckperms.sponge.service.base;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||
|
||||
import org.spongepowered.api.service.context.Context;
|
||||
import org.spongepowered.api.util.Tristate;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@UtilityClass
|
||||
public class Util {
|
||||
private static final LoadingCache<Set<Context>, ImmutableContextSet> SPONGE_TO_LP_CACHE = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<Set<Context>, ImmutableContextSet>() {
|
||||
@Override
|
||||
public ImmutableContextSet load(Set<Context> contexts) {
|
||||
return ContextSet.fromEntries(contexts);
|
||||
}
|
||||
});
|
||||
|
||||
public static ContextSet convertContexts(Set<Context> contexts) {
|
||||
return ContextSet.fromEntries(contexts.stream().map(c -> Maps.immutableEntry(c.getKey(), c.getValue())).collect(Collectors.toSet()));
|
||||
private static final LoadingCache<ImmutableContextSet, Set<Context>> LP_TO_SPONGE_CACHE = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<ImmutableContextSet, Set<Context>>() {
|
||||
@Override
|
||||
public Set<Context> load(ImmutableContextSet set) {
|
||||
return set.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(ImmutableCollectors.toImmutableSet());
|
||||
}
|
||||
});
|
||||
|
||||
public static ContextSet convertContexts(@NonNull Set<Context> contexts) {
|
||||
return SPONGE_TO_LP_CACHE.getUnchecked(contexts);
|
||||
}
|
||||
|
||||
public static Set<Context> convertContexts(ContextSet contexts) {
|
||||
return contexts.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(ImmutableCollectors.toImmutableSet());
|
||||
public static Set<Context> convertContexts(@NonNull ContextSet contexts) {
|
||||
return LP_TO_SPONGE_CACHE.getUnchecked(contexts.makeImmutable());
|
||||
}
|
||||
|
||||
public static Tristate convertTristate(me.lucko.luckperms.api.Tristate tristate) {
|
||||
|
@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableSortedMap;
|
||||
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.calculators.PermissionCalculator;
|
||||
import me.lucko.luckperms.common.calculators.PermissionProcessor;
|
||||
import me.lucko.luckperms.common.calculators.processors.MapProcessor;
|
||||
@ -95,8 +96,10 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
|
||||
@Getter
|
||||
private final LPSubject parentSubject;
|
||||
|
||||
private final LuckPermsService service;
|
||||
private final String calculatorDisplayName;
|
||||
|
||||
private final Map<ContextSet, Map<String, Boolean>> permissions = new ConcurrentHashMap<>();
|
||||
private final LoadingCache<ContextSet, CalculatorHolder> permissionCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<ContextSet, CalculatorHolder>() {
|
||||
@ -112,15 +115,9 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
return holder;
|
||||
}
|
||||
});
|
||||
|
||||
private final Map<ContextSet, Set<SubjectReference>> parents = new ConcurrentHashMap<>();
|
||||
private final Map<ContextSet, Map<String, String>> options = new ConcurrentHashMap<>();
|
||||
private final LoadingCache<ContextSet, Map<String, String>> optionCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<ContextSet, Map<String, String>>() {
|
||||
@Override
|
||||
public Map<String, String> load(ContextSet contexts) {
|
||||
return flattenMap(contexts, options);
|
||||
}
|
||||
});
|
||||
|
||||
public Tristate getPermissionValue(ContextSet contexts, String permission) {
|
||||
return permissionCache.getUnchecked(contexts).getCalculator().getPermissionValue(permission);
|
||||
@ -132,6 +129,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
permissions.put(e.getKey().makeImmutable(), new ConcurrentHashMap<>(e.getValue()));
|
||||
}
|
||||
permissionCache.invalidateAll();
|
||||
service.invalidatePermissionCaches();
|
||||
}
|
||||
|
||||
public void replaceParents(Map<ContextSet, Set<SubjectReference>> map) {
|
||||
@ -141,6 +139,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
set.addAll(e.getValue());
|
||||
parents.put(e.getKey().makeImmutable(), set);
|
||||
}
|
||||
service.invalidateParentCaches();
|
||||
}
|
||||
|
||||
public void replaceOptions(Map<ContextSet, Map<String, String>> map) {
|
||||
@ -148,12 +147,12 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
for (Map.Entry<ContextSet, Map<String, String>> e : map.entrySet()) {
|
||||
options.put(e.getKey().makeImmutable(), new ConcurrentHashMap<>(e.getValue()));
|
||||
}
|
||||
optionCache.invalidateAll();
|
||||
service.invalidateOptionCaches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ContextSet, Map<String, Boolean>> getPermissions() {
|
||||
ImmutableMap.Builder<ContextSet, Map<String, Boolean>> map = ImmutableMap.builder();
|
||||
public Map<ImmutableContextSet, Map<String, Boolean>> getPermissions() {
|
||||
ImmutableMap.Builder<ImmutableContextSet, Map<String, Boolean>> map = ImmutableMap.builder();
|
||||
for (Map.Entry<ContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
|
||||
map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
|
||||
}
|
||||
@ -177,6 +176,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
}
|
||||
if (b) {
|
||||
permissionCache.invalidateAll();
|
||||
service.invalidatePermissionCaches();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
@ -188,6 +188,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
} else {
|
||||
permissions.clear();
|
||||
permissionCache.invalidateAll();
|
||||
service.invalidatePermissionCaches();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -202,14 +203,15 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
permissions.remove(contexts);
|
||||
if (!perms.isEmpty()) {
|
||||
permissionCache.invalidateAll();
|
||||
service.invalidatePermissionCaches();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ContextSet, Set<SubjectReference>> getParents() {
|
||||
ImmutableMap.Builder<ContextSet, Set<SubjectReference>> map = ImmutableMap.builder();
|
||||
public Map<ImmutableContextSet, Set<SubjectReference>> getParents() {
|
||||
ImmutableMap.Builder<ImmutableContextSet, Set<SubjectReference>> map = ImmutableMap.builder();
|
||||
for (Map.Entry<ContextSet, Set<SubjectReference>> e : parents.entrySet()) {
|
||||
map.put(e.getKey().makeImmutable(), ImmutableSet.copyOf(e.getValue()));
|
||||
}
|
||||
@ -224,13 +226,21 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
@Override
|
||||
public boolean addParent(ContextSet contexts, SubjectReference parent) {
|
||||
Set<SubjectReference> set = parents.computeIfAbsent(contexts.makeImmutable(), c -> ConcurrentHashMap.newKeySet());
|
||||
return set.add(parent);
|
||||
boolean b = set.add(parent);
|
||||
if (b) {
|
||||
service.invalidateParentCaches();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeParent(ContextSet contexts, SubjectReference parent) {
|
||||
Set<SubjectReference> set = parents.get(contexts);
|
||||
return set != null && set.remove(parent);
|
||||
boolean b = set != null && set.remove(parent);
|
||||
if (b) {
|
||||
service.invalidateParentCaches();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -239,6 +249,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
return false;
|
||||
} else {
|
||||
parents.clear();
|
||||
service.invalidateOptionCaches();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -251,12 +262,13 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
}
|
||||
|
||||
parents.remove(contexts);
|
||||
service.invalidateParentCaches();
|
||||
return !set.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ContextSet, Map<String, String>> getOptions() {
|
||||
ImmutableMap.Builder<ContextSet, Map<String, String>> map = ImmutableMap.builder();
|
||||
public Map<ImmutableContextSet, Map<String, String>> getOptions() {
|
||||
ImmutableMap.Builder<ImmutableContextSet, Map<String, String>> map = ImmutableMap.builder();
|
||||
for (Map.Entry<ContextSet, Map<String, String>> e : options.entrySet()) {
|
||||
map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue()));
|
||||
}
|
||||
@ -273,7 +285,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
Map<String, String> options = this.options.computeIfAbsent(contexts.makeImmutable(), c -> new ConcurrentHashMap<>());
|
||||
boolean b = !stringEquals(options.put(key.toLowerCase(), value), value);
|
||||
if (b) {
|
||||
optionCache.invalidateAll();
|
||||
service.invalidateOptionCaches();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
@ -283,7 +295,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
Map<String, String> options = this.options.get(contexts);
|
||||
boolean b = options != null && options.remove(key.toLowerCase()) != null;
|
||||
if (b) {
|
||||
optionCache.invalidateAll();
|
||||
service.invalidateOptionCaches();
|
||||
}
|
||||
return b;
|
||||
}
|
||||
@ -294,7 +306,7 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
return false;
|
||||
} else {
|
||||
options.clear();
|
||||
optionCache.invalidateAll();
|
||||
service.invalidateOptionCaches();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -307,11 +319,8 @@ public class CalculatedSubjectData implements LPSubjectData {
|
||||
}
|
||||
|
||||
options.remove(contexts);
|
||||
if (!map.isEmpty()) {
|
||||
optionCache.invalidateAll();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
service.invalidateOptionCaches();
|
||||
return !map.isEmpty();
|
||||
}
|
||||
|
||||
private static class ContextComparator implements Comparator<ContextSet> {
|
||||
|
@ -27,7 +27,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@ -36,6 +36,6 @@ import me.lucko.luckperms.api.context.ContextSet;
|
||||
public class OptionLookup {
|
||||
|
||||
private final String key;
|
||||
private final ContextSet contexts;
|
||||
private final ImmutableContextSet contexts;
|
||||
|
||||
}
|
@ -27,7 +27,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@ -36,6 +36,6 @@ import me.lucko.luckperms.api.context.ContextSet;
|
||||
public class PermissionLookup {
|
||||
|
||||
private final String node;
|
||||
private final ContextSet contexts;
|
||||
private final ImmutableContextSet contexts;
|
||||
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ import java.util.Map;
|
||||
public class PersistedCollection implements LPSubjectCollection {
|
||||
private final LuckPermsService service;
|
||||
private final String identifier;
|
||||
private final boolean transientHasPriority;
|
||||
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final LoadingCache<String, PersistedSubject> subjects = CacheBuilder.newBuilder()
|
||||
@ -101,4 +102,9 @@ public class PersistedCollection implements LPSubjectCollection {
|
||||
public SubjectReference getDefaultSubject() {
|
||||
return SubjectReference.of("defaults", identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getTransientHasPriority() {
|
||||
return transientHasPriority;
|
||||
}
|
||||
}
|
||||
|
@ -25,14 +25,20 @@ package me.lucko.luckperms.sponge.service.persisted;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
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.utils.BufferedRequest;
|
||||
import me.lucko.luckperms.sponge.service.LuckPermsService;
|
||||
import me.lucko.luckperms.sponge.service.base.LPSubject;
|
||||
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
|
||||
import me.lucko.luckperms.sponge.service.calculated.OptionLookup;
|
||||
import me.lucko.luckperms.sponge.service.calculated.PermissionLookup;
|
||||
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
|
||||
import me.lucko.luckperms.sponge.service.references.SubjectReference;
|
||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
||||
@ -58,6 +64,30 @@ public class PersistedSubject implements LPSubject {
|
||||
private final SubjectCollectionReference parentCollection;
|
||||
private final PersistedSubjectData subjectData;
|
||||
private final CalculatedSubjectData transientSubjectData;
|
||||
|
||||
private final LoadingCache<PermissionLookup, Tristate> permissionLookupCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<PermissionLookup, Tristate>() {
|
||||
@Override
|
||||
public Tristate load(PermissionLookup lookup) {
|
||||
return lookupPermissionValue(lookup.getContexts(), lookup.getNode());
|
||||
}
|
||||
});
|
||||
private final LoadingCache<ImmutableContextSet, Set<SubjectReference>> parentLookupCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<ImmutableContextSet, Set<SubjectReference>>() {
|
||||
@Override
|
||||
public Set<SubjectReference> load(ImmutableContextSet contexts) {
|
||||
return lookupParents(contexts);
|
||||
}
|
||||
});
|
||||
private final LoadingCache<OptionLookup, Optional<String>> optionLookupCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<OptionLookup, Optional<String>>() {
|
||||
@Override
|
||||
public Optional<String> load(OptionLookup lookup) {
|
||||
return lookupOptionValue(lookup.getContexts(), lookup.getKey());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
private final BufferedRequest<Void> saveBuffer = new BufferedRequest<Void>(1000L, r -> PersistedSubject.this.service.getPlugin().doAsync(r)) {
|
||||
@Override
|
||||
protected Void perform() {
|
||||
@ -76,8 +106,13 @@ public class PersistedSubject implements LPSubject {
|
||||
this.identifier = identifier;
|
||||
this.service = service;
|
||||
this.parentCollection = containingCollection.toReference();
|
||||
|
||||
this.subjectData = new PersistedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)", this);
|
||||
this.transientSubjectData = new CalculatedSubjectData(this, service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)");
|
||||
|
||||
service.getLocalPermissionCaches().add(permissionLookupCache);
|
||||
service.getLocalParentCaches().add(parentLookupCache);
|
||||
service.getLocalOptionCaches().add(optionLookupCache);
|
||||
}
|
||||
|
||||
public void loadData(SubjectDataHolder dataHolder) {
|
||||
@ -95,10 +130,21 @@ public class PersistedSubject implements LPSubject {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tristate getPermissionValue(@NonNull ContextSet contexts, @NonNull String node) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_PERMISSION_VALUE)) {
|
||||
Tristate res = subjectData.getPermissionValue(contexts, node);
|
||||
private Tristate lookupPermissionValue(ContextSet contexts, String node) {
|
||||
Tristate res;
|
||||
|
||||
if (parentCollection.resolve(service).getTransientHasPriority()) {
|
||||
res = transientSubjectData.getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = subjectData.getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
res = subjectData.getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
@ -107,31 +153,95 @@ public class PersistedSubject implements LPSubject {
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
for (SubjectReference parent : getParents(contexts)) {
|
||||
Tristate tempRes = parent.resolve(service).getPermissionValue(contexts, node);
|
||||
if (tempRes != Tristate.UNDEFINED) {
|
||||
return tempRes;
|
||||
}
|
||||
}
|
||||
|
||||
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
return Tristate.UNDEFINED;
|
||||
}
|
||||
|
||||
res = service.getGroupSubjects().getDefaultSubject().resolve(service).getPermissionValue(contexts, node);
|
||||
for (SubjectReference parent : getParents(contexts)) {
|
||||
res = parent.resolve(service).getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
res = service.getDefaults().getPermissionValue(contexts, node);
|
||||
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
return Tristate.UNDEFINED;
|
||||
}
|
||||
|
||||
res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = service.getDefaults().getPermissionValue(contexts, node);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Set<SubjectReference> lookupParents(ContextSet contexts) {
|
||||
Set<SubjectReference> s = new HashSet<>();
|
||||
s.addAll(subjectData.getParents(contexts));
|
||||
s.addAll(transientSubjectData.getParents(contexts));
|
||||
|
||||
if (!getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
s.addAll(getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts));
|
||||
s.addAll(service.getDefaults().getParents(contexts));
|
||||
}
|
||||
|
||||
return ImmutableSet.copyOf(s);
|
||||
}
|
||||
|
||||
private Optional<String> lookupOptionValue(ContextSet contexts, String key) {
|
||||
Optional<String> res;
|
||||
|
||||
if (getParentCollection().resolve(service).getTransientHasPriority()) {
|
||||
res = Optional.ofNullable(transientSubjectData.getOptions(contexts).get(key));
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = Optional.ofNullable(subjectData.getOptions(contexts).get(key));
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
res = Optional.ofNullable(subjectData.getOptions(contexts).get(key));
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = Optional.ofNullable(transientSubjectData.getOptions(contexts).get(key));
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
for (SubjectReference parent : getParents(contexts)) {
|
||||
res = parent.resolve(service).getOption(contexts, key);
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getOption(contexts, key);
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return service.getDefaults().getOption(contexts, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tristate getPermissionValue(@NonNull ContextSet contexts, @NonNull String node) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PERMISSION_VALUE)) {
|
||||
return permissionLookupCache.getUnchecked(PermissionLookup.of(node, contexts.makeImmutable()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildOf(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_IS_CHILD_OF)) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_IS_CHILD_OF)) {
|
||||
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
return subjectData.getParents(contexts).contains(subject) ||
|
||||
transientSubjectData.getParents(contexts).contains(subject);
|
||||
@ -146,56 +256,21 @@ public class PersistedSubject implements LPSubject {
|
||||
|
||||
@Override
|
||||
public Set<SubjectReference> getParents(@NonNull ContextSet contexts) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_PARENTS)) {
|
||||
Set<SubjectReference> s = new HashSet<>();
|
||||
s.addAll(subjectData.getParents(contexts));
|
||||
s.addAll(transientSubjectData.getParents(contexts));
|
||||
|
||||
if (!getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
s.addAll(getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts));
|
||||
s.addAll(service.getDefaults().getParents(contexts));
|
||||
}
|
||||
|
||||
return ImmutableSet.copyOf(s);
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_PARENTS)) {
|
||||
return parentLookupCache.getUnchecked(contexts.makeImmutable());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getOption(ContextSet set, String key) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_OPTION)) {
|
||||
Optional<String> res = Optional.ofNullable(subjectData.getOptions(getActiveContextSet()).get(key));
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = Optional.ofNullable(transientSubjectData.getOptions(getActiveContextSet()).get(key));
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (SubjectReference parent : getParents(getActiveContextSet())) {
|
||||
Optional<String> tempRes = parent.resolve(service).getOption(getActiveContextSet(), key);
|
||||
if (tempRes.isPresent()) {
|
||||
return tempRes;
|
||||
}
|
||||
}
|
||||
|
||||
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getOption(set, key);
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return service.getDefaults().getOption(set, key);
|
||||
public Optional<String> getOption(ContextSet contexts, String key) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_OPTION)) {
|
||||
return optionLookupCache.getUnchecked(OptionLookup.of(key, contexts.makeImmutable()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextSet getActiveContextSet() {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_ACTIVE_CONTEXTS)) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.INTERNAL_SUBJECT_GET_ACTIVE_CONTEXTS)) {
|
||||
return service.getPlugin().getContextManager().getApplicableContext(this);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ package me.lucko.luckperms.sponge.service.persisted;
|
||||
import lombok.ToString;
|
||||
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
|
||||
import me.lucko.luckperms.sponge.service.references.SubjectReference;
|
||||
|
||||
@ -43,19 +44,19 @@ public class SubjectDataHolder {
|
||||
private final Map<Map<String, String>, Map<String, String>> options;
|
||||
private final Map<Map<String, String>, List<String>> parents;
|
||||
|
||||
public SubjectDataHolder(Map<ContextSet, Map<String, String>> options, Map<ContextSet, Map<String, Boolean>> permissions, Map<ContextSet, Set<SubjectReference>> parents) {
|
||||
public SubjectDataHolder(Map<ImmutableContextSet, Map<String, String>> options, Map<ImmutableContextSet, Map<String, Boolean>> permissions, Map<ImmutableContextSet, Set<SubjectReference>> parents) {
|
||||
this.options = new HashMap<>();
|
||||
for (Map.Entry<ContextSet, Map<String, String>> e : options.entrySet()) {
|
||||
for (Map.Entry<ImmutableContextSet, Map<String, String>> e : options.entrySet()) {
|
||||
this.options.put(e.getKey().toMap(), new HashMap<>(e.getValue()));
|
||||
}
|
||||
|
||||
this.permissions = new HashMap<>();
|
||||
for (Map.Entry<ContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
|
||||
for (Map.Entry<ImmutableContextSet, Map<String, Boolean>> e : permissions.entrySet()) {
|
||||
this.permissions.put(e.getKey().toMap(), new HashMap<>(e.getValue()));
|
||||
}
|
||||
|
||||
this.parents = new HashMap<>();
|
||||
for (Map.Entry<ContextSet, Set<SubjectReference>> e : parents.entrySet()) {
|
||||
for (Map.Entry<ImmutableContextSet, Set<SubjectReference>> e : parents.entrySet()) {
|
||||
this.parents.put(e.getKey().toMap(), e.getValue().stream().map(SubjectReference::serialize).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
package me.lucko.luckperms.sponge.service.persisted;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@ -33,6 +34,8 @@ import java.util.AbstractMap;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Handles persisted Subject I/O and (de)serialization
|
||||
@ -51,6 +54,17 @@ public class SubjectStorage {
|
||||
this.container.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
public Set<String> getSavedCollections() {
|
||||
checkContainer();
|
||||
|
||||
File[] dirs = container.listFiles(File::isDirectory);
|
||||
if (dirs == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return ImmutableSet.copyOf(dirs).stream().map(File::getName).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public void saveToFile(PersistedSubject subject) throws IOException {
|
||||
checkContainer();
|
||||
File collection = new File(container, subject.getContainingCollection().getIdentifier());
|
||||
|
@ -22,23 +22,35 @@
|
||||
|
||||
package me.lucko.luckperms.sponge.service.references;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import me.lucko.luckperms.sponge.service.LuckPermsService;
|
||||
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor(staticName = "of")
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
public class SubjectCollectionReference {
|
||||
|
||||
private final String collection;
|
||||
private WeakReference<LPSubjectCollection> ref = null;
|
||||
|
||||
public LPSubjectCollection resolve(LuckPermsService service) {
|
||||
return service.getSubjects(collection);
|
||||
public synchronized LPSubjectCollection resolve(LuckPermsService service) {
|
||||
if (ref != null) {
|
||||
LPSubjectCollection sc = ref.get();
|
||||
if (sc != null) {
|
||||
return sc;
|
||||
}
|
||||
}
|
||||
|
||||
LPSubjectCollection sc = service.getSubjects(collection);
|
||||
ref = new WeakReference<>(sc);
|
||||
return sc;
|
||||
}
|
||||
}
|
||||
|
@ -22,25 +22,25 @@
|
||||
|
||||
package me.lucko.luckperms.sponge.service.references;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
import me.lucko.luckperms.sponge.service.LuckPermsService;
|
||||
import me.lucko.luckperms.sponge.service.base.LPSubject;
|
||||
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
|
||||
|
||||
import org.spongepowered.api.service.permission.Subject;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor(staticName = "of")
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
public class SubjectReference {
|
||||
public static SubjectReference deserialize(String s) {
|
||||
List<String> parts = Splitter.on('/').limit(2).splitToList(s);
|
||||
@ -54,12 +54,19 @@ public class SubjectReference {
|
||||
private final String collection;
|
||||
private final String identifier;
|
||||
|
||||
public LPSubject resolve(LuckPermsService service) {
|
||||
return service.getSubjects(collection).get(identifier);
|
||||
}
|
||||
private WeakReference<LPSubject> ref = null;
|
||||
|
||||
public LPSubjectCollection resolveCollection(LuckPermsService service) {
|
||||
return service.getSubjects(collection);
|
||||
public synchronized LPSubject resolve(LuckPermsService service) {
|
||||
if (ref != null) {
|
||||
LPSubject s = ref.get();
|
||||
if (s != null) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
LPSubject s = service.getSubjects(collection).get(identifier);
|
||||
ref = new WeakReference<>(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public String serialize() {
|
||||
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.sponge.service.simple;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||
import me.lucko.luckperms.sponge.service.LuckPermsService;
|
||||
import me.lucko.luckperms.sponge.service.base.LPSubject;
|
||||
import me.lucko.luckperms.sponge.service.base.LPSubjectCollection;
|
||||
import me.lucko.luckperms.sponge.service.references.SubjectReference;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Super simple SubjectCollection implementation
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class SimpleCollection implements LPSubjectCollection {
|
||||
private final LuckPermsService service;
|
||||
private final String identifier;
|
||||
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final LoadingCache<String, SimpleSubject> subjects = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<String, SimpleSubject>() {
|
||||
@Override
|
||||
public SimpleSubject load(String s) {
|
||||
return new SimpleSubject(s, service, SimpleCollection.this);
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public LPSubject get(@NonNull String id) {
|
||||
return subjects.getUnchecked(id.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRegistered(@NonNull String id) {
|
||||
return subjects.asMap().containsKey(id.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LPSubject> getSubjects() {
|
||||
return subjects.asMap().values().stream().map(s -> (LPSubject) s).collect(ImmutableCollectors.toImmutableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<LPSubject, Boolean> getWithPermission(@NonNull ContextSet contexts, @NonNull String node) {
|
||||
ImmutableMap.Builder<LPSubject, Boolean> m = ImmutableMap.builder();
|
||||
for (LPSubject subject : subjects.asMap().values()) {
|
||||
Tristate ts = subject.getPermissionValue(contexts, node);
|
||||
if (ts != Tristate.UNDEFINED) {
|
||||
m.put(subject, ts.asBoolean());
|
||||
}
|
||||
|
||||
}
|
||||
return m.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubjectReference getDefaultSubject() {
|
||||
return SubjectReference.of("defaults", identifier);
|
||||
}
|
||||
}
|
@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
|
||||
*
|
||||
* 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.sponge.service.simple;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import me.lucko.luckperms.api.Tristate;
|
||||
import me.lucko.luckperms.api.context.ContextSet;
|
||||
import me.lucko.luckperms.sponge.service.LuckPermsService;
|
||||
import me.lucko.luckperms.sponge.service.base.LPSubject;
|
||||
import me.lucko.luckperms.sponge.service.calculated.CalculatedSubjectData;
|
||||
import me.lucko.luckperms.sponge.service.calculated.OptionLookup;
|
||||
import me.lucko.luckperms.sponge.service.calculated.PermissionLookup;
|
||||
import me.lucko.luckperms.sponge.service.references.SubjectCollectionReference;
|
||||
import me.lucko.luckperms.sponge.service.references.SubjectReference;
|
||||
import me.lucko.luckperms.sponge.timings.LPTiming;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import co.aikar.timings.Timing;
|
||||
|
||||
/**
|
||||
* Super simple Subject implementation.
|
||||
*/
|
||||
@Getter
|
||||
public class SimpleSubject implements LPSubject {
|
||||
private final String identifier;
|
||||
|
||||
private final LuckPermsService service;
|
||||
private final SubjectCollectionReference parentCollection;
|
||||
private final CalculatedSubjectData subjectData;
|
||||
private final CalculatedSubjectData transientSubjectData;
|
||||
private final LoadingCache<ContextSet, Set<SubjectReference>> parentLookupCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<ContextSet, Set<SubjectReference>>() {
|
||||
@Override
|
||||
public Set<SubjectReference> load(ContextSet contexts) {
|
||||
return lookupParents(contexts);
|
||||
}
|
||||
});
|
||||
private final LoadingCache<PermissionLookup, Tristate> permissionLookupCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<PermissionLookup, Tristate>() {
|
||||
@Override
|
||||
public Tristate load(PermissionLookup lookup) {
|
||||
return lookupPermissionValue(lookup.getContexts(), lookup.getNode());
|
||||
}
|
||||
});
|
||||
private final LoadingCache<OptionLookup, Optional<String>> optionLookupCache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<OptionLookup, Optional<String>>() {
|
||||
@Override
|
||||
public Optional<String> load(OptionLookup lookup) {
|
||||
return lookupOptionValue(lookup.getContexts(), lookup.getKey());
|
||||
}
|
||||
});
|
||||
|
||||
public SimpleSubject(String identifier, LuckPermsService service, SimpleCollection containingCollection) {
|
||||
this.identifier = identifier;
|
||||
this.service = service;
|
||||
this.parentCollection = containingCollection.toReference();
|
||||
this.subjectData = new CalculatedSubjectData(this, service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)");
|
||||
this.transientSubjectData = new CalculatedSubjectData(this, service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)");
|
||||
service.getLocalPermissionCaches().add(permissionLookupCache);
|
||||
}
|
||||
|
||||
private Tristate lookupPermissionValue(ContextSet contexts, String node) {
|
||||
Tristate res = transientSubjectData.getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = subjectData.getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (SubjectReference parent : getParents(contexts)) {
|
||||
res = parent.resolve(service).getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
return Tristate.UNDEFINED;
|
||||
}
|
||||
|
||||
res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getPermissionValue(contexts, node);
|
||||
if (res != Tristate.UNDEFINED) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = service.getDefaults().getPermissionValue(contexts, node);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Set<SubjectReference> lookupParents(ContextSet contexts) {
|
||||
Set<SubjectReference> s = new HashSet<>();
|
||||
s.addAll(subjectData.getParents(contexts));
|
||||
|
||||
if (!getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
s.addAll(getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts));
|
||||
s.addAll(service.getDefaults().getParents(contexts));
|
||||
}
|
||||
|
||||
return ImmutableSet.copyOf(s);
|
||||
}
|
||||
|
||||
private Optional<String> lookupOptionValue(ContextSet contexts, String key) {
|
||||
Optional<String> res = Optional.ofNullable(subjectData.getOptions(contexts).get(key));
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (SubjectReference parent : getParents(getActiveContextSet())) {
|
||||
Optional<String> tempRes = parent.resolve(service).getOption(contexts, key);
|
||||
if (tempRes.isPresent()) {
|
||||
return tempRes;
|
||||
}
|
||||
}
|
||||
|
||||
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
res = getParentCollection().resolve(service).getDefaultSubject().resolve(service).getOption(contexts, key);
|
||||
if (res.isPresent()) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return service.getDefaults().getOption(contexts, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tristate getPermissionValue(@NonNull ContextSet contexts, @NonNull String node) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_PERMISSION_VALUE)) {
|
||||
return permissionLookupCache.getUnchecked(PermissionLookup.of(node, contexts));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildOf(@NonNull ContextSet contexts, @NonNull SubjectReference subject) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_IS_CHILD_OF)) {
|
||||
if (getParentCollection().resolve(service).getIdentifier().equalsIgnoreCase("defaults")) {
|
||||
return subjectData.getParents(contexts).contains(subject);
|
||||
} else {
|
||||
return subjectData.getParents(contexts).contains(subject) ||
|
||||
getParentCollection().resolve(service).getDefaultSubject().resolve(service).getParents(contexts).contains(subject) ||
|
||||
service.getDefaults().getParents(contexts).contains(subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SubjectReference> getParents(@NonNull ContextSet contexts) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_PARENTS)) {
|
||||
return parentLookupCache.getUnchecked(contexts);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getOption(ContextSet contexts, String key) {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_OPTION)) {
|
||||
return optionLookupCache.getUnchecked(OptionLookup.of(key, contexts));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextSet getActiveContextSet() {
|
||||
try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_ACTIVE_CONTEXTS)) {
|
||||
return service.getPlugin().getContextManager().getApplicableContext(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -57,17 +57,11 @@ public enum LPTiming {
|
||||
LP_SUBJECT_SET_OPTION("lpSubjectSetOption"),
|
||||
LP_SUBJECT_CLEAR_OPTIONS("lpSubjectClearOptions"),
|
||||
|
||||
SIMPLE_SUBJECT_GET_PERMISSION_VALUE("simpleSubjectGetPermissionValue"),
|
||||
SIMPLE_SUBJECT_IS_CHILD_OF("simpleSubjectIsChildOf"),
|
||||
SIMPLE_SUBJECT_GET_PARENTS("simpleSubjectGetParents"),
|
||||
SIMPLE_SUBJECT_GET_OPTION("simpleSubjectGetOption"),
|
||||
SIMPLE_SUBJECT_GET_ACTIVE_CONTEXTS("simpleSubjectGetActiveContexts"),
|
||||
|
||||
PERSISTED_SUBJECT_GET_PERMISSION_VALUE("persistedSubjectGetPermissionValue"),
|
||||
PERSISTED_SUBJECT_IS_CHILD_OF("persistedSubjectIsChildOf"),
|
||||
PERSISTED_SUBJECT_GET_PARENTS("persistedSubjectGetParents"),
|
||||
PERSISTED_SUBJECT_GET_OPTION("persistedSubjectGetOption"),
|
||||
PERSISTED_SUBJECT_GET_ACTIVE_CONTEXTS("persistedSubjectGetActiveContexts"),
|
||||
INTERNAL_SUBJECT_GET_PERMISSION_VALUE("internalSubjectGetPermissionValue"),
|
||||
INTERNAL_SUBJECT_IS_CHILD_OF("internalSubjectIsChildOf"),
|
||||
INTERNAL_SUBJECT_GET_PARENTS("internalSubjectGetParents"),
|
||||
INTERNAL_SUBJECT_GET_OPTION("internalSubjectGetOption"),
|
||||
INTERNAL_SUBJECT_GET_ACTIVE_CONTEXTS("internalSubjectGetActiveContexts"),
|
||||
|
||||
ON_COMMAND("onCommand"),
|
||||
COMMAND_TAB_COMPLETE("commandTabComplete"),
|
||||
|
Loading…
Reference in New Issue
Block a user