diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitCalculatorFactory.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitCalculatorFactory.java index dd6061460..361383d90 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitCalculatorFactory.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitCalculatorFactory.java @@ -30,7 +30,7 @@ import me.lucko.luckperms.bukkit.calculators.ChildProcessor; import me.lucko.luckperms.bukkit.calculators.DefaultsProcessor; import me.lucko.luckperms.bukkit.inject.Injector; import me.lucko.luckperms.bukkit.model.LPPermissible; -import me.lucko.luckperms.common.calculators.CalculatorFactory; +import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.PermissionCalculator; import me.lucko.luckperms.common.calculators.PermissionProcessor; import me.lucko.luckperms.common.calculators.processors.MapProcessor; @@ -41,7 +41,7 @@ import me.lucko.luckperms.common.core.model.User; import java.util.UUID; @AllArgsConstructor -public class BukkitCalculatorFactory implements CalculatorFactory { +public class BukkitCalculatorFactory extends AbstractCalculatorFactory { private final LPBukkitPlugin plugin; @Override @@ -63,6 +63,6 @@ public class BukkitCalculatorFactory implements CalculatorFactory { } processors.add(new DefaultsProcessor(contexts.isOp(), plugin.getDefaultsProvider())); - return new PermissionCalculator(plugin, user.getName(), processors.build()); + return registerCalculator(new PermissionCalculator(plugin, user.getName(), processors.build())); } } diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeCalculatorFactory.java b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeCalculatorFactory.java index 59629ebf5..d8ca41326 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeCalculatorFactory.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeCalculatorFactory.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.bungee; import com.google.common.collect.ImmutableList; import lombok.AllArgsConstructor; import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.common.calculators.CalculatorFactory; +import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.PermissionCalculator; import me.lucko.luckperms.common.calculators.PermissionProcessor; import me.lucko.luckperms.common.calculators.processors.MapProcessor; @@ -34,7 +34,7 @@ import me.lucko.luckperms.common.calculators.processors.WildcardProcessor; import me.lucko.luckperms.common.core.model.User; @AllArgsConstructor -public class BungeeCalculatorFactory implements CalculatorFactory { +public class BungeeCalculatorFactory extends AbstractCalculatorFactory { private final LPBungeePlugin plugin; @Override @@ -48,6 +48,6 @@ public class BungeeCalculatorFactory implements CalculatorFactory { processors.add(new RegexProcessor()); } - return new PermissionCalculator(plugin, user.getName(), processors.build()); + return registerCalculator(new PermissionCalculator(plugin, user.getName(), processors.build())); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/AbstractCalculatorFactory.java b/common/src/main/java/me/lucko/luckperms/common/calculators/AbstractCalculatorFactory.java new file mode 100644 index 000000000..d135dd850 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/calculators/AbstractCalculatorFactory.java @@ -0,0 +1,44 @@ +/* + * 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.common.calculators; + +import com.google.common.collect.MapMaker; + +import java.util.Collections; +import java.util.Set; + +public abstract class AbstractCalculatorFactory implements CalculatorFactory { + private final Set calculators = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); + + protected PermissionCalculator registerCalculator(PermissionCalculator calculator) { + calculators.add(calculator); + return calculator; + } + + @Override + public void invalidateAll() { + for (PermissionCalculator calculator : calculators) { + calculator.invalidateCache(); + } + } +} diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/CalculatorFactory.java b/common/src/main/java/me/lucko/luckperms/common/calculators/CalculatorFactory.java index 4df1f0701..95358bbd6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculators/CalculatorFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculators/CalculatorFactory.java @@ -30,6 +30,17 @@ import me.lucko.luckperms.common.core.model.User; */ public interface CalculatorFactory { + /** + * Builds a PermissionCalculator for the user in the given context + * @param contexts the contexts to build the calculator in + * @param user the user to build for + * @return a permission calculator instance + */ PermissionCalculator build(Contexts contexts, User user); + /** + * Invalidates all calculators build by this factory + */ + void invalidateAll(); + } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCalculatorFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCalculatorFactory.java index 21dfcd324..298cb9248 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCalculatorFactory.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeCalculatorFactory.java @@ -25,7 +25,7 @@ package me.lucko.luckperms.sponge; import com.google.common.collect.ImmutableList; import lombok.AllArgsConstructor; import me.lucko.luckperms.api.Contexts; -import me.lucko.luckperms.common.calculators.CalculatorFactory; +import me.lucko.luckperms.common.calculators.AbstractCalculatorFactory; import me.lucko.luckperms.common.calculators.PermissionCalculator; import me.lucko.luckperms.common.calculators.PermissionProcessor; import me.lucko.luckperms.common.calculators.processors.MapProcessor; @@ -37,7 +37,7 @@ import me.lucko.luckperms.sponge.calculators.SpongeWildcardProcessor; import me.lucko.luckperms.sponge.service.LuckPermsService; @AllArgsConstructor -public class SpongeCalculatorFactory implements CalculatorFactory { +public class SpongeCalculatorFactory extends AbstractCalculatorFactory { private final LPSpongePlugin plugin; @Override @@ -53,6 +53,6 @@ public class SpongeCalculatorFactory implements CalculatorFactory { } processors.add(new DefaultsProcessor(plugin.getService(), LuckPermsService.convertContexts(contexts.getContexts()))); - return new PermissionCalculator(plugin, user.getName(), processors.build()); + return registerCalculator(new PermissionCalculator(plugin, user.getName(), processors.build())); } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/data/CalculatedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/data/CalculatedSubjectData.java new file mode 100644 index 000000000..ca54a40b5 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/data/CalculatedSubjectData.java @@ -0,0 +1,350 @@ +/* + * 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.data; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedMap; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import me.lucko.luckperms.common.calculators.PermissionCalculator; +import me.lucko.luckperms.common.calculators.PermissionProcessor; +import me.lucko.luckperms.common.calculators.processors.MapProcessor; +import me.lucko.luckperms.common.utils.ImmutableCollectors; +import me.lucko.luckperms.sponge.calculators.SpongeWildcardProcessor; +import me.lucko.luckperms.sponge.service.LuckPermsService; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.Subject; +import org.spongepowered.api.service.permission.SubjectData; +import org.spongepowered.api.util.Tristate; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@RequiredArgsConstructor +public class CalculatedSubjectData implements SubjectData { + private static final ContextComparator CONTEXT_COMPARATOR = new ContextComparator(); + + private final LuckPermsService service; + private final String calculatorDisplayName; + + private final LoadingCache, CalculatorHolder> permissionCache = CacheBuilder.newBuilder() + .build(new CacheLoader, CalculatorHolder>() { + @Override + public CalculatorHolder load(Set contexts) { + ImmutableList.Builder processors = ImmutableList.builder(); + processors.add(new MapProcessor()); + processors.add(new SpongeWildcardProcessor()); + + CalculatorHolder holder = new CalculatorHolder(new PermissionCalculator(service.getPlugin(), calculatorDisplayName, processors.build())); + holder.setPermissions(flattenMap(contexts, permissions)); + + return holder; + } + }); + + private final LoadingCache, Map> optionCache = CacheBuilder.newBuilder() + .build(new CacheLoader, Map>() { + @Override + public Map load(Set contexts) { + return flattenMap(contexts, options); + } + }); + + private final Map, Map> permissions = new ConcurrentHashMap<>(); + private final Map, Set> parents = new ConcurrentHashMap<>(); + private final Map, Map> options = new ConcurrentHashMap<>(); + + public Tristate getPermissionValue(Set contexts, String permission) { + return LuckPermsService.convertTristate(permissionCache.getUnchecked(contexts).getCalculator().getPermissionValue(permission)); + } + + public Map, Set> getParents() { + ImmutableMap.Builder, Set> map = ImmutableMap.builder(); + for (Map.Entry, Set> e : parents.entrySet()) { + map.put(ImmutableSet.copyOf(e.getKey()), ImmutableSet.copyOf(e.getValue())); + } + return map.build(); + } + + public void replacePermissions(Map, Map> map) { + permissions.clear(); + for (Map.Entry, Map> e : map.entrySet()) { + permissions.put(ImmutableSet.copyOf(e.getKey()), new ConcurrentHashMap<>(e.getValue())); + } + permissionCache.invalidateAll(); + } + + public void replaceParents(Map, Set> map) { + parents.clear(); + for (Map.Entry, Set> e : map.entrySet()) { + Set set = ConcurrentHashMap.newKeySet(); + set.addAll(e.getValue()); + parents.put(ImmutableSet.copyOf(e.getKey()), set); + } + } + + public void replaceOptions(Map, Map> map) { + options.clear(); + for (Map.Entry, Map> e : map.entrySet()) { + options.put(ImmutableSet.copyOf(e.getKey()), new ConcurrentHashMap<>(e.getValue())); + } + optionCache.invalidateAll(); + } + + @Override + public Map, Map> getAllPermissions() { + ImmutableMap.Builder, Map> map = ImmutableMap.builder(); + for (Map.Entry, Map> e : permissions.entrySet()) { + map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue())); + } + return map.build(); + } + + @Override + public Map getPermissions(Set contexts) { + return ImmutableMap.copyOf(permissions.getOrDefault(contexts, ImmutableMap.of())); + } + + @Override + public boolean setPermission(Set contexts, String permission, Tristate value) { + boolean b; + if (value == Tristate.UNDEFINED) { + Map perms = permissions.get(contexts); + b = perms != null && perms.remove(permission.toLowerCase()) != null; + } else { + Map perms = permissions.computeIfAbsent(ImmutableSet.copyOf(contexts), c -> new ConcurrentHashMap<>()); + b = !Objects.equals(perms.put(permission.toLowerCase(), value.asBoolean()), value.asBoolean()); + } + if (b) { + permissionCache.invalidateAll(); + } + return b; + } + + @Override + public boolean clearPermissions() { + if (permissions.isEmpty()) { + return false; + } else { + permissions.clear(); + permissionCache.invalidateAll(); + return true; + } + } + + @Override + public boolean clearPermissions(Set contexts) { + Map perms = permissions.get(contexts); + if (perms == null) { + return false; + } + + permissions.remove(contexts); + if (!perms.isEmpty()) { + permissionCache.invalidateAll(); + return true; + } + return false; + } + + @Override + public Map, List> getAllParents() { + ImmutableMap.Builder, List> map = ImmutableMap.builder(); + for (Map.Entry, Set> e : parents.entrySet()) { + map.put( + ImmutableSet.copyOf(e.getKey()), + e.getValue().stream().map(s -> s.resolve(service)).collect(ImmutableCollectors.toImmutableList()) + ); + } + return map.build(); + } + + @Override + public List getParents(Set contexts) { + return parents.getOrDefault(contexts, ImmutableSet.of()).stream() + .map(s -> s.resolve(service)) + .collect(ImmutableCollectors.toImmutableList()); + } + + @Override + public boolean addParent(Set contexts, Subject parent) { + Set set = parents.computeIfAbsent(ImmutableSet.copyOf(contexts), c -> ConcurrentHashMap.newKeySet()); + return set.add(SubjectReference.of(parent)); + } + + @Override + public boolean removeParent(Set contexts, Subject parent) { + Set set = parents.get(contexts); + return set != null && set.remove(SubjectReference.of(parent)); + } + + @Override + public boolean clearParents() { + if (parents.isEmpty()) { + return false; + } else { + parents.clear(); + return true; + } + } + + @Override + public boolean clearParents(Set contexts) { + Set set = parents.get(contexts); + if (set == null) { + return false; + } + + parents.remove(contexts); + return !set.isEmpty(); + } + + @Override + public Map, Map> getAllOptions() { + ImmutableMap.Builder, Map> map = ImmutableMap.builder(); + for (Map.Entry, Map> e : options.entrySet()) { + map.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue())); + } + return map.build(); + } + + @Override + public Map getOptions(Set contexts) { + return ImmutableMap.copyOf(options.getOrDefault(contexts, ImmutableMap.of())); + } + + @Override + public boolean setOption(Set contexts, String key, @Nullable String value) { + boolean b; + if (value == null) { + Map options = this.options.get(contexts); + b = options != null && options.remove(key.toLowerCase()) != null; + } else { + Map options = this.options.computeIfAbsent(ImmutableSet.copyOf(contexts), c -> new ConcurrentHashMap<>()); + b = !stringEquals(options.put(key.toLowerCase(), value), value); + } + if (b) { + optionCache.invalidateAll(); + } + return b; + } + + @Override + public boolean clearOptions() { + if (options.isEmpty()) { + return false; + } else { + options.clear(); + optionCache.invalidateAll(); + return true; + } + } + + @Override + public boolean clearOptions(Set contexts) { + Map map = options.get(contexts); + if (map == null) { + return false; + } + + options.remove(contexts); + if (!map.isEmpty()) { + optionCache.invalidateAll(); + return true; + } + return false; + } + + private static Map flattenMap(Set contexts, Map, Map> source) { + Map map = new HashMap<>(); + + SortedMap, Map> ret = getRelevantEntries(contexts, source); + for (Map m : ret.values()) { + for (Map.Entry e : m.entrySet()) { + if (!map.containsKey(e.getKey())) { + map.put(e.getKey(), e.getValue()); + } + } + } + + return ImmutableMap.copyOf(map); + } + + private static SortedMap, Map> getRelevantEntries(Set set, Map, Map> map) { + ImmutableSortedMap.Builder, Map> perms = ImmutableSortedMap.orderedBy(CONTEXT_COMPARATOR); + + loop: + for (Map.Entry, Map> e : map.entrySet()) { + for (Context c : e.getKey()) { + if (!set.contains(c)) { + continue loop; + } + } + + perms.put(ImmutableSet.copyOf(e.getKey()), ImmutableMap.copyOf(e.getValue())); + } + + return perms.build(); + } + + private static boolean stringEquals(String a, String b) { + return a == null && b == null || a != null && b != null && a.equalsIgnoreCase(b); + } + + private static class ContextComparator implements Comparator> { + + @Override + public int compare(Set o1, Set o2) { + int i = Integer.compare(o1.size(), o2.size()); + return i == 0 ? 1 : i; + } + } + + private static class CalculatorHolder { + + @Getter + private final PermissionCalculator calculator; + + @Getter + private final Map permissions; + + public CalculatorHolder(PermissionCalculator calculator) { + this.calculator = calculator; + this.permissions = new ConcurrentHashMap<>(); + this.calculator.updateBacking(permissions); + } + + public void setPermissions(Map permissions) { + this.permissions.clear(); + this.permissions.putAll(permissions); + calculator.updateBacking(this.permissions); + calculator.invalidateCache(); + } + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/data/SubjectReference.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/data/SubjectReference.java new file mode 100644 index 000000000..b834587ca --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/data/SubjectReference.java @@ -0,0 +1,60 @@ +/* + * 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.data; + +import com.google.common.base.Splitter; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import org.spongepowered.api.service.permission.PermissionService; +import org.spongepowered.api.service.permission.Subject; + +import java.util.List; + +@Getter +@ToString +@EqualsAndHashCode +@AllArgsConstructor(staticName = "of") +public class SubjectReference { + public static SubjectReference deserialize(String s) { + List parts = Splitter.on('/').limit(2).splitToList(s); + return of(parts.get(0), parts.get(1)); + } + + public static SubjectReference of(Subject subject) { + return of(subject.getContainingCollection().getIdentifier(), subject.getIdentifier()); + } + + private final String collection; + private final String identifier; + + public Subject resolve(PermissionService service) { + return service.getSubjects(collection).get(identifier); + } + + public String serialize() { + return collection + "/" + identifier; + } + +} 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 e0d320b12..64a64e67b 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 @@ -29,10 +29,10 @@ import lombok.Getter; import lombok.NonNull; import me.lucko.luckperms.common.utils.BufferedRequest; import me.lucko.luckperms.sponge.service.LuckPermsService; +import me.lucko.luckperms.sponge.service.data.CalculatedSubjectData; import me.lucko.luckperms.sponge.timings.LPTiming; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.service.context.Context; -import org.spongepowered.api.service.permission.MemorySubjectData; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.util.Tristate; @@ -52,7 +52,7 @@ public class PersistedSubject implements Subject { private final LuckPermsService service; private final PersistedCollection containingCollection; private final PersistedSubjectData subjectData; - private final MemorySubjectData transientSubjectData; + private final CalculatedSubjectData transientSubjectData; private final BufferedRequest saveBuffer = new BufferedRequest(1000L, r -> PersistedSubject.this.service.getPlugin().doAsync(r)) { @Override protected Void perform() { @@ -71,13 +71,13 @@ public class PersistedSubject implements Subject { this.identifier = identifier; this.service = service; this.containingCollection = containingCollection; - this.subjectData = new PersistedSubjectData(service, this); - this.transientSubjectData = new MemorySubjectData(service); + this.subjectData = new PersistedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)", this); + this.transientSubjectData = new CalculatedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)"); } public void loadData(SubjectDataHolder dataHolder) { subjectData.setSave(false); - dataHolder.copyTo(subjectData, service); + dataHolder.copyTo(subjectData); subjectData.setSave(true); } @@ -98,12 +98,12 @@ public class PersistedSubject implements Subject { @Override public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String node) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.PERSISTED_SUBJECT_GET_PERMISSION_VALUE)) { - Tristate res = subjectData.getNodeTree(contexts).get(node); + Tristate res = subjectData.getPermissionValue(contexts, node); if (res != Tristate.UNDEFINED) { return res; } - res = transientSubjectData.getNodeTree(contexts).get(node); + res = transientSubjectData.getPermissionValue(contexts, node); if (res != Tristate.UNDEFINED) { return res; } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubjectData.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubjectData.java index e2390471e..fb7bda009 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubjectData.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/persisted/PersistedSubjectData.java @@ -24,9 +24,9 @@ package me.lucko.luckperms.sponge.service.persisted; import lombok.Getter; import lombok.Setter; +import me.lucko.luckperms.sponge.service.LuckPermsService; +import me.lucko.luckperms.sponge.service.data.CalculatedSubjectData; import org.spongepowered.api.service.context.Context; -import org.spongepowered.api.service.permission.MemorySubjectData; -import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.Subject; import org.spongepowered.api.util.Tristate; @@ -36,15 +36,15 @@ import java.util.Set; /** * Extension of MemorySubjectData which persists data when modified */ -public class PersistedSubjectData extends MemorySubjectData { +public class PersistedSubjectData extends CalculatedSubjectData { private final PersistedSubject subject; @Getter @Setter private boolean save = true; - public PersistedSubjectData(PermissionService service, PersistedSubject subject) { - super(service); + public PersistedSubjectData(LuckPermsService service, String calculatorDisplayName, PersistedSubject subject) { + super(service, calculatorDisplayName); this.subject = subject; } 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 23b80414b..4d8e15cb1 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 @@ -22,15 +22,16 @@ package me.lucko.luckperms.sponge.service.persisted; -import com.google.common.base.Splitter; import lombok.ToString; import me.lucko.luckperms.api.context.ContextSet; +import me.lucko.luckperms.sponge.service.data.CalculatedSubjectData; +import me.lucko.luckperms.sponge.service.data.SubjectReference; import org.spongepowered.api.service.context.Context; -import org.spongepowered.api.service.permission.MemorySubjectData; -import org.spongepowered.api.service.permission.PermissionService; -import org.spongepowered.api.util.Tristate; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static me.lucko.luckperms.sponge.service.LuckPermsService.convertContexts; @@ -44,7 +45,7 @@ public class SubjectDataHolder { private final Map, Map> options; private final Map, List> parents; - public SubjectDataHolder(Map, Map> options, Map, Map> permissions, Map, List>> parents) { + public SubjectDataHolder(Map, Map> options, Map, Map> permissions, Map, Set> parents) { this.options = new HashMap<>(); for (Map.Entry, Map> e : options.entrySet()) { this.options.put(convertContexts(e.getKey()).toMap(), new HashMap<>(e.getValue())); @@ -56,50 +57,35 @@ public class SubjectDataHolder { } this.parents = new HashMap<>(); - for (Map.Entry, List>> e : parents.entrySet()) { - this.parents.put(convertContexts(e.getKey()).toMap(), e.getValue().stream().map(p -> p.getKey() + "/" + p.getValue()).collect(Collectors.toList())); + for (Map.Entry, Set> e : parents.entrySet()) { + this.parents.put(convertContexts(e.getKey()).toMap(), e.getValue().stream().map(SubjectReference::serialize).collect(Collectors.toList())); } } - public SubjectDataHolder(MemorySubjectData data) { - this( - data.getAllOptions(), - data.getAllPermissions(), - data.getAllParents().entrySet().stream() - .collect(Collectors.toMap( - Map.Entry::getKey, - e -> e.getValue().stream() - .map(s -> new AbstractMap.SimpleEntry<>( - s.getContainingCollection().getIdentifier(), - s.getIdentifier()) - ) - .collect(Collectors.toList()) - ) - ) + public SubjectDataHolder(CalculatedSubjectData data) { + this(data.getAllOptions(), data.getAllPermissions(), data.getParents()); + } + + public void copyTo(CalculatedSubjectData subjectData) { + subjectData.replacePermissions(permissions.entrySet().stream() + .collect(Collectors.toMap( + k -> convertContexts(ContextSet.fromMap(k.getKey())), + Map.Entry::getValue + )) + ); + + subjectData.replaceOptions(options.entrySet().stream() + .collect(Collectors.toMap( + k -> convertContexts(ContextSet.fromMap(k.getKey())), + Map.Entry::getValue + )) + ); + + subjectData.replaceParents(parents.entrySet().stream() + .collect(Collectors.toMap( + k -> convertContexts(ContextSet.fromMap(k.getKey())), + v -> v.getValue().stream().map(SubjectReference::deserialize).collect(Collectors.toSet()) + )) ); } - - public void copyTo(MemorySubjectData subjectData, PermissionService service) { - for (Map.Entry, Map> e : permissions.entrySet()) { - Set contexts = convertContexts(ContextSet.fromMap(e.getKey())); - for (Map.Entry perm : e.getValue().entrySet()) { - subjectData.setPermission(contexts, perm.getKey(), Tristate.fromBoolean(perm.getValue())); - } - } - - for (Map.Entry, Map> e : options.entrySet()) { - Set contexts = convertContexts(ContextSet.fromMap(e.getKey())); - for (Map.Entry option : e.getValue().entrySet()) { - subjectData.setOption(contexts, option.getKey(), option.getValue()); - } - } - - for (Map.Entry, List> e : parents.entrySet()) { - Set contexts = convertContexts(ContextSet.fromMap(e.getKey())); - for (String parent : e.getValue()) { - List parts = Splitter.on('/').limit(2).splitToList(parent); - subjectData.addParent(contexts, service.getSubjects(parts.get(0)).get(parts.get(1))); - } - } - } } 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 index 4b90da8d1..524c30179 100644 --- 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 @@ -28,12 +28,11 @@ import com.google.common.collect.ImmutableSet; import lombok.Getter; import lombok.NonNull; import me.lucko.luckperms.sponge.service.LuckPermsService; +import me.lucko.luckperms.sponge.service.data.CalculatedSubjectData; import me.lucko.luckperms.sponge.timings.LPTiming; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.service.context.Context; -import org.spongepowered.api.service.permission.MemorySubjectData; import org.spongepowered.api.service.permission.Subject; -import org.spongepowered.api.service.permission.SubjectData; import org.spongepowered.api.util.Tristate; import java.util.ArrayList; @@ -50,13 +49,15 @@ public class SimpleSubject implements Subject { private final LuckPermsService service; private final SimpleCollection containingCollection; - private final MemorySubjectData subjectData; + private final CalculatedSubjectData subjectData; + private final CalculatedSubjectData transientSubjectData; public SimpleSubject(String identifier, LuckPermsService service, SimpleCollection containingCollection) { this.identifier = identifier; this.service = service; this.containingCollection = containingCollection; - this.subjectData = new MemorySubjectData(service); + this.subjectData = new CalculatedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(p)"); + this.transientSubjectData = new CalculatedSubjectData(service, "local:" + containingCollection.getIdentifier() + "/" + identifier + "(t)"); } @Override @@ -64,11 +65,6 @@ public class SimpleSubject implements Subject { return Optional.empty(); } - @Override - public SubjectData getTransientSubjectData() { - return getSubjectData(); - } - @Override public boolean hasPermission(@NonNull Set contexts, @NonNull String node) { return getPermissionValue(contexts, node).asBoolean(); @@ -77,15 +73,20 @@ public class SimpleSubject implements Subject { @Override public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String node) { try (Timing ignored = service.getPlugin().getTimings().time(LPTiming.SIMPLE_SUBJECT_GET_PERMISSION_VALUE)) { - Tristate res = subjectData.getNodeTree(contexts).get(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 (Subject parent : getParents(contexts)) { - Tristate tempRes = parent.getPermissionValue(contexts, node); - if (tempRes != Tristate.UNDEFINED) { - return tempRes; + res = parent.getPermissionValue(contexts, node); + if (res != Tristate.UNDEFINED) { + return res; } } @@ -93,7 +94,7 @@ public class SimpleSubject implements Subject { return Tristate.UNDEFINED; } - res = service.getGroupSubjects().getDefaults().getPermissionValue(contexts, node); + res = getContainingCollection().getDefaults().getPermissionValue(contexts, node); if (res != Tristate.UNDEFINED) { return res; }