diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java index c190a2c8..0646e204 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/ContextSet.java @@ -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> iterable) { + static ImmutableContextSet fromEntries(Iterable> iterable) { return ImmutableContextSet.fromEntries(iterable); } diff --git a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java index 8657114a..c6495b04 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/ImmutableContextSet.java @@ -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> iterable) { + public static ImmutableContextSet fromEntries(Iterable> iterable) { if (iterable == null) { throw new NullPointerException("iterable"); } diff --git a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java index a57f0359..a7a51f2c 100644 --- a/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java +++ b/api/src/main/java/me/lucko/luckperms/api/context/MutableContextSet.java @@ -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> iterable) { + public static MutableContextSet fromEntries(Iterable> 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> iterable) { + public void addAll(Iterable> iterable) { if (iterable == null) { throw new NullPointerException("contexts"); } diff --git a/common/src/main/java/me/lucko/luckperms/common/core/model/ImmutableNode.java b/common/src/main/java/me/lucko/luckperms/common/core/model/ImmutableNode.java index 3f2d333d..0a79517f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/core/model/ImmutableNode.java +++ b/common/src/main/java/me/lucko/luckperms/common/core/model/ImmutableNode.java @@ -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; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java index c7d06b14..f87be6a3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/LPSpongePlugin.java @@ -104,22 +104,30 @@ import java.util.stream.StreamSupport; public class LPSpongePlugin implements LuckPermsPlugin { private final Set 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 getExtraInfo() { LinkedHashMap 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() diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeGroupManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeGroupManager.java index de710e6a..80d9d6b8 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeGroupManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeGroupManager.java @@ -183,4 +183,9 @@ public class SpongeGroupManager implements GroupManager, LPSubjectCollection { public SubjectReference getDefaultSubject() { return SubjectReference.of("defaults", getIdentifier()); } + + @Override + public boolean getTransientHasPriority() { + return true; + } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java index b02dd5be..09d1252e 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/managers/SpongeUserManager.java @@ -242,4 +242,9 @@ public class SpongeUserManager implements UserManager, LPSubjectCollection { public SubjectReference getDefaultSubject() { return SubjectReference.of("defaults", getIdentifier()); } + + @Override + public boolean getTransientHasPriority() { + return true; + } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java index 232ddbf3..e3db2633 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsService.java @@ -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 descriptionSet; private final Set> localPermissionCaches; - private final Set, List>> localParentCaches; + private final Set>> localParentCaches; private final Set>> localOptionCaches; @Getter(value = AccessLevel.NONE) @@ -104,7 +102,7 @@ public class LuckPermsService implements PermissionService { .build(new CacheLoader() { @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 c : localPermissionCaches) { + c.invalidateAll(); + } + } + + public void invalidateParentCaches() { + for (LoadingCache> c : localParentCaches) { + c.invalidateAll(); + } + invalidateOptionCaches(); + invalidatePermissionCaches(); + } + + public void invalidateOptionCaches() { + for (LoadingCache> c : localOptionCaches) { + c.invalidateAll(); + } + } + @RequiredArgsConstructor @EqualsAndHashCode @ToString diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java index 5ea67003..7be49f98 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/LuckPermsSubjectData.java @@ -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> getPermissions() { + public Map> getPermissions() { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PERMISSIONS)) { Map> perms = new HashMap<>(); @@ -107,7 +108,7 @@ public class LuckPermsSubjectData implements LPSubjectData { perms.get(contexts).put(n.getPermission(), n.getValue()); } - ImmutableMap.Builder> map = ImmutableMap.builder(); + ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> e : perms.entrySet()) { map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue())); } @@ -215,9 +216,9 @@ public class LuckPermsSubjectData implements LPSubjectData { } @Override - public Map> getParents() { + public Map> getParents() { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_PARENTS)) { - Map> parents = new HashMap<>(); + Map> 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> map = ImmutableMap.builder(); - for (Map.Entry> e : parents.entrySet()) { - map.put(e.getKey().makeImmutable(), ImmutableSet.copyOf(e.getValue())); + ImmutableMap.Builder> map = ImmutableMap.builder(); + for (Map.Entry> 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> getOptions() { + public Map> getOptions() { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.LP_SUBJECT_GET_OPTIONS)) { Map> options = new HashMap<>(); Map minPrefixPriority = new HashMap<>(); @@ -428,7 +429,7 @@ public class LuckPermsSubjectData implements LPSubjectData { } } - ImmutableMap.Builder> map = ImmutableMap.builder(); + ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> e : options.entrySet()) { map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue())); } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubject.java index 6a97a015..02f4ef98 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubject.java @@ -83,7 +83,6 @@ public interface LPSubject extends Subject { /* Compat */ - @Deprecated @Override default LPSubjectCollection getContainingCollection() { return getParentCollection().resolve(getService()); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubjectCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubjectCollection.java index 82afdd89..52e15896 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubjectCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubjectCollection.java @@ -67,6 +67,8 @@ public interface LPSubjectCollection extends SubjectCollection { SubjectReference getDefaultSubject(); + boolean getTransientHasPriority(); + @Deprecated @Override default Subject getDefaults() { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubjectData.java index c13bce36..700e1e81 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/LPSubjectData.java @@ -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> getPermissions(); + Map> getPermissions(); default Map 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> getParents(); + Map> getParents(); default Set 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> getOptions(); + Map> getOptions(); default Map getOptions(ContextSet contexts) { return ImmutableMap.copyOf(getOptions().getOrDefault(contexts, ImmutableMap.of())); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/Util.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/Util.java index 82397936..e0eaa4ae 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/Util.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/base/Util.java @@ -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, ImmutableContextSet> SPONGE_TO_LP_CACHE = CacheBuilder.newBuilder() + .build(new CacheLoader, ImmutableContextSet>() { + @Override + public ImmutableContextSet load(Set contexts) { + return ContextSet.fromEntries(contexts); + } + }); - public static ContextSet convertContexts(Set contexts) { - return ContextSet.fromEntries(contexts.stream().map(c -> Maps.immutableEntry(c.getKey(), c.getValue())).collect(Collectors.toSet())); + private static final LoadingCache> LP_TO_SPONGE_CACHE = CacheBuilder.newBuilder() + .build(new CacheLoader>() { + @Override + public Set load(ImmutableContextSet set) { + return set.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(ImmutableCollectors.toImmutableSet()); + } + }); + + public static ContextSet convertContexts(@NonNull Set contexts) { + return SPONGE_TO_LP_CACHE.getUnchecked(contexts); } - public static Set convertContexts(ContextSet contexts) { - return contexts.toSet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(ImmutableCollectors.toImmutableSet()); + public static Set convertContexts(@NonNull ContextSet contexts) { + return LP_TO_SPONGE_CACHE.getUnchecked(contexts.makeImmutable()); } public static Tristate convertTristate(me.lucko.luckperms.api.Tristate tristate) { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java index 0de4aa5d..ff995c8e 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/CalculatedSubjectData.java @@ -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> permissions = new ConcurrentHashMap<>(); private final LoadingCache permissionCache = CacheBuilder.newBuilder() .build(new CacheLoader() { @@ -112,15 +115,9 @@ public class CalculatedSubjectData implements LPSubjectData { return holder; } }); + private final Map> parents = new ConcurrentHashMap<>(); private final Map> options = new ConcurrentHashMap<>(); - private final LoadingCache> optionCache = CacheBuilder.newBuilder() - .build(new CacheLoader>() { - @Override - public Map 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> 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> map) { @@ -148,12 +147,12 @@ public class CalculatedSubjectData implements LPSubjectData { for (Map.Entry> e : map.entrySet()) { options.put(e.getKey().makeImmutable(), new ConcurrentHashMap<>(e.getValue())); } - optionCache.invalidateAll(); + service.invalidateOptionCaches(); } @Override - public Map> getPermissions() { - ImmutableMap.Builder> map = ImmutableMap.builder(); + public Map> getPermissions() { + ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> 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> getParents() { - ImmutableMap.Builder> map = ImmutableMap.builder(); + public Map> getParents() { + ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> 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 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 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> getOptions() { - ImmutableMap.Builder> map = ImmutableMap.builder(); + public Map> getOptions() { + ImmutableMap.Builder> map = ImmutableMap.builder(); for (Map.Entry> e : options.entrySet()) { map.put(e.getKey().makeImmutable(), ImmutableMap.copyOf(e.getValue())); } @@ -273,7 +285,7 @@ public class CalculatedSubjectData implements LPSubjectData { Map 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 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 { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/OptionLookup.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/OptionLookup.java index 6817e2fe..0f69a9d3 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/OptionLookup.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/OptionLookup.java @@ -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; } \ No newline at end of file diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/PermissionLookup.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/PermissionLookup.java index 285899cf..c249bf20 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/PermissionLookup.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/calculated/PermissionLookup.java @@ -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; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedCollection.java index 2bc415b1..20e8baf0 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedCollection.java @@ -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 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; + } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java index fdb7c5d6..28b90579 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubject.java @@ -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 permissionLookupCache = CacheBuilder.newBuilder() + .build(new CacheLoader() { + @Override + public Tristate load(PermissionLookup lookup) { + return lookupPermissionValue(lookup.getContexts(), lookup.getNode()); + } + }); + private final LoadingCache> parentLookupCache = CacheBuilder.newBuilder() + .build(new CacheLoader>() { + @Override + public Set load(ImmutableContextSet contexts) { + return lookupParents(contexts); + } + }); + private final LoadingCache> optionLookupCache = CacheBuilder.newBuilder() + .build(new CacheLoader>() { + @Override + public Optional load(OptionLookup lookup) { + return lookupOptionValue(lookup.getContexts(), lookup.getKey()); + } + }); + + private final BufferedRequest saveBuffer = new BufferedRequest(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 lookupParents(ContextSet contexts) { + Set 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 lookupOptionValue(ContextSet contexts, String key) { + Optional 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 getParents(@NonNull ContextSet contexts) { - try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_PARENTS)) { - Set 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 getOption(ContextSet set, String key) { - try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_OPTION)) { - Optional 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 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 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); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/SubjectDataHolder.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/SubjectDataHolder.java index 087f1321..aa9b4bc7 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/SubjectDataHolder.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/SubjectDataHolder.java @@ -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> options; private final Map, List> parents; - public SubjectDataHolder(Map> options, Map> permissions, Map> parents) { + public SubjectDataHolder(Map> options, Map> permissions, Map> parents) { this.options = new HashMap<>(); - for (Map.Entry> e : options.entrySet()) { + for (Map.Entry> e : options.entrySet()) { this.options.put(e.getKey().toMap(), new HashMap<>(e.getValue())); } this.permissions = new HashMap<>(); - for (Map.Entry> e : permissions.entrySet()) { + for (Map.Entry> e : permissions.entrySet()) { this.permissions.put(e.getKey().toMap(), new HashMap<>(e.getValue())); } this.parents = new HashMap<>(); - for (Map.Entry> e : parents.entrySet()) { + for (Map.Entry> e : parents.entrySet()) { this.parents.put(e.getKey().toMap(), e.getValue().stream().map(SubjectReference::serialize).collect(Collectors.toList())); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/SubjectStorage.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/SubjectStorage.java index e222499a..0d9ce33c 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/SubjectStorage.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/SubjectStorage.java @@ -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 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()); diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectCollectionReference.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectCollectionReference.java index 1003d58b..8ab99992 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectCollectionReference.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectCollectionReference.java @@ -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 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; } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectReference.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectReference.java index 2c586844..3a6bb451 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectReference.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/references/SubjectReference.java @@ -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 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 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() { diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/simple/SimpleCollection.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/simple/SimpleCollection.java deleted file mode 100644 index c484ce2b..00000000 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/simple/SimpleCollection.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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 subjects = CacheBuilder.newBuilder() - .build(new CacheLoader() { - @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 getSubjects() { - return subjects.asMap().values().stream().map(s -> (LPSubject) s).collect(ImmutableCollectors.toImmutableList()); - } - - @Override - public Map getWithPermission(@NonNull ContextSet contexts, @NonNull String node) { - ImmutableMap.Builder 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); - } -} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/simple/SimpleSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/simple/SimpleSubject.java deleted file mode 100644 index fe2e6b1f..00000000 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/simple/SimpleSubject.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2016 Lucko (Luck) - * - * 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> parentLookupCache = CacheBuilder.newBuilder() - .build(new CacheLoader>() { - @Override - public Set load(ContextSet contexts) { - return lookupParents(contexts); - } - }); - private final LoadingCache permissionLookupCache = CacheBuilder.newBuilder() - .build(new CacheLoader() { - @Override - public Tristate load(PermissionLookup lookup) { - return lookupPermissionValue(lookup.getContexts(), lookup.getNode()); - } - }); - private final LoadingCache> optionLookupCache = CacheBuilder.newBuilder() - .build(new CacheLoader>() { - @Override - public Optional 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 lookupParents(ContextSet contexts) { - Set 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 lookupOptionValue(ContextSet contexts, String key) { - Optional res = Optional.ofNullable(subjectData.getOptions(contexts).get(key)); - if (res.isPresent()) { - return res; - } - - for (SubjectReference parent : getParents(getActiveContextSet())) { - Optional 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 getParents(@NonNull ContextSet contexts) { - try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_PARENTS)) { - return parentLookupCache.getUnchecked(contexts); - } - } - - @Override - public Optional 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); - } - } -} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/timings/LPTiming.java b/sponge/src/main/java/me/lucko/luckperms/sponge/timings/LPTiming.java index 9cbf3a6f..1c5c7f7e 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/timings/LPTiming.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/timings/LPTiming.java @@ -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"),