Implement full caching for non user/group subjects

This commit is contained in:
Luck 2016-12-03 13:26:17 +00:00
parent dd3a78ada8
commit 83b0f62d59
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
25 changed files with 336 additions and 446 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -183,4 +183,9 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection {
public SubjectReference getDefaultSubject() {
return SubjectReference.of("defaults", getIdentifier());
}
@Override
public boolean getTransientHasPriority() {
return true;
}
}

View File

@ -242,4 +242,9 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection {
public SubjectReference getDefaultSubject() {
return SubjectReference.of("defaults", getIdentifier());
}
@Override
public boolean getTransientHasPriority() {
return true;
}
}

View File

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

View File

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

View File

@ -83,7 +83,6 @@ public interface LPSubject extends Subject {
/* Compat */
@Deprecated
@Override
default LPSubjectCollection getContainingCollection() {
return getParentCollection().resolve(getService());

View File

@ -67,6 +67,8 @@ public interface LPSubjectCollection extends SubjectCollection {
SubjectReference getDefaultSubject();
boolean getTransientHasPriority();
@Deprecated
@Override
default Subject getDefaults() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"),