diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/AttachmentProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/AttachmentProcessor.java index 281b0ebd..4ef1b760 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/AttachmentProcessor.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/AttachmentProcessor.java @@ -44,10 +44,8 @@ public class AttachmentProcessor implements PermissionProcessor { return Tristate.UNDEFINED; } - if (m.containsKey(permission)) { - return Tristate.fromBoolean(m.get(permission).getValue()); - } - return Tristate.UNDEFINED; + PermissionAttachmentInfo pai = m.get(permission); + return pai == null ? Tristate.UNDEFINED : Tristate.fromBoolean(pai.getValue()); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/AutoOPListener.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/AutoOPListener.java index b01008e6..40a8fb1d 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/AutoOPListener.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/AutoOPListener.java @@ -28,6 +28,7 @@ import me.lucko.luckperms.bukkit.inject.LPPermissible; import org.bukkit.entity.Player; import java.util.Map; +import java.util.Optional; public class AutoOPListener implements ContextListener { @@ -39,7 +40,7 @@ public class AutoOPListener implements ContextListener { } Map backing = permissible.getUser().getUserData().getPermissionData(permissible.calculateContexts()).getImmutableBacking(); - boolean op = backing.containsKey("luckperms.autoop") && backing.get("luckperms.autoop"); + boolean op = Optional.ofNullable(backing.get("luckperms.autoop")).orElse(false); subject.setOp(op); } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/DefaultsProcessor.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/DefaultsProcessor.java index ef4f2e72..46923733 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/DefaultsProcessor.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/DefaultsProcessor.java @@ -41,10 +41,6 @@ public class DefaultsProcessor implements PermissionProcessor { } Permission defPerm = Bukkit.getServer().getPluginManager().getPermission(permission); - if (defPerm != null) { - return Tristate.fromBoolean(defPerm.getDefault().getValue(isOp)); - } else { - return Tristate.UNDEFINED; - } + return defPerm == null ? Tristate.UNDEFINED : Tristate.fromBoolean(defPerm.getDefault().getValue(isOp)); } } diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/DefaultsProvider.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/DefaultsProvider.java index 18822270..6e409d7a 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/DefaultsProvider.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/calculators/DefaultsProvider.java @@ -97,11 +97,9 @@ public class DefaultsProvider { public Tristate hasDefault(String permission, boolean isOp) { Map map = isOp ? op : nonOp; - if (!map.containsKey(permission)) { - return Tristate.UNDEFINED; - } - return Tristate.fromBoolean(map.get(permission)); + Boolean b = map.get(permission); + return b == null ? Tristate.UNDEFINED : Tristate.fromBoolean(b); } @AllArgsConstructor diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/MetaData.java b/common/src/main/java/me/lucko/luckperms/common/caching/MetaData.java index d6fb870b..d6612514 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/MetaData.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/MetaData.java @@ -23,27 +23,25 @@ package me.lucko.luckperms.common.caching; import com.google.common.collect.ImmutableMap; -import lombok.Getter; +import com.google.common.collect.ImmutableSortedMap; import lombok.RequiredArgsConstructor; import me.lucko.luckperms.api.Contexts; import me.lucko.luckperms.api.LocalizedNode; import me.lucko.luckperms.api.Node; -import java.util.HashMap; -import java.util.Map; -import java.util.SortedSet; -import java.util.concurrent.ConcurrentHashMap; +import java.util.*; +/** + * Holds a user's cached meta for a given context + */ @RequiredArgsConstructor public class MetaData { private final Contexts contexts; - @Getter - private String prefix = null; + private final SortedMap prefixes = new TreeMap<>(Comparator.reverseOrder()); + private final SortedMap suffixes = new TreeMap<>(Comparator.reverseOrder()); - @Getter - private String suffix = null; - private Map meta = new ConcurrentHashMap<>(); + private final Map meta = new HashMap<>(); public void loadMeta(SortedSet nodes) { invalidateCache(); @@ -52,9 +50,6 @@ public class MetaData { String server = contexts.remove("server"); String world = contexts.remove("world"); - int prefixPriority = Integer.MIN_VALUE; - int suffixPriority = Integer.MIN_VALUE; - for (LocalizedNode ln : nodes) { Node n = ln.getNode(); @@ -80,37 +75,84 @@ public class MetaData { if (n.isPrefix()) { Map.Entry value = n.getPrefix(); - if (value.getKey() > prefixPriority) { - this.prefix = value.getValue(); - prefixPriority = value.getKey(); + synchronized (this.prefixes) { + if (!this.prefixes.containsKey(value.getKey())) { + this.prefixes.put(value.getKey(), value.getValue()); + } } continue; } if (n.isSuffix()) { Map.Entry value = n.getSuffix(); - if (value.getKey() > suffixPriority) { - this.suffix = value.getValue(); - suffixPriority = value.getKey(); + synchronized (this.suffixes) { + if (!this.suffixes.containsKey(value.getKey())) { + this.suffixes.put(value.getKey(), value.getValue()); + } } continue; } if (n.isMeta()) { Map.Entry meta = n.getMeta(); + synchronized (this.meta) { + if (!this.meta.containsKey(meta.getKey())) { + this.meta.put(meta.getKey(), meta.getValue()); + } + } this.meta.put(meta.getKey(), meta.getValue()); } } } public void invalidateCache() { - meta.clear(); - prefix = null; - suffix = null; + synchronized (meta) { + meta.clear(); + } + synchronized (prefixes) { + prefixes.clear(); + } + synchronized (suffixes) { + suffixes.clear(); + } } public Map getMeta() { - return ImmutableMap.copyOf(meta); + synchronized (meta) { + return ImmutableMap.copyOf(meta); + } + } + + public SortedMap getPrefixes() { + synchronized (prefixes) { + return ImmutableSortedMap.copyOfSorted(prefixes); + } + } + + public SortedMap getSuffixes() { + synchronized (suffixes) { + return ImmutableSortedMap.copyOfSorted(suffixes); + } + } + + public String getPrefix() { + synchronized (prefixes) { + if (prefixes.isEmpty()) { + return null; + } + + return prefixes.get(prefixes.firstKey()); + } + } + + public String getSuffix() { + synchronized (suffixes) { + if (suffixes.isEmpty()) { + return null; + } + + return suffixes.get(suffixes.firstKey()); + } } } diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/PermissionData.java b/common/src/main/java/me/lucko/luckperms/common/caching/PermissionData.java index 7c5e6db5..bc96e0e7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/PermissionData.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/PermissionData.java @@ -33,14 +33,24 @@ import me.lucko.luckperms.common.users.User; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +/** + * Holds a user's cached permissions for a given context + */ public class PermissionData { - private final Contexts contexts; + + /** + * The raw set of permission strings. + */ private final Map permissions; + /** + * The calculator instance responsible for resolving the raw permission strings in the permission map. + * This calculator will attempt to resolve all regex/wildcard permissions, as well as account for + * defaults & attachment permissions (if applicable.) + */ private final PermissionCalculator calculator; public PermissionData(Contexts contexts, User user, CalculatorFactory calculatorFactory) { - this.contexts = contexts; permissions = new ConcurrentHashMap<>(); calculator = calculatorFactory.build(contexts, user, permissions); } @@ -56,20 +66,7 @@ public class PermissionData { } public void comparePermissions(Map toApply) { - boolean different = false; - if (toApply.size() != permissions.size()) { - different = true; - } else { - for (Map.Entry e : permissions.entrySet()) { - if (toApply.containsKey(e.getKey()) && toApply.get(e.getKey()) == e.getValue()) { - continue; - } - different = true; - break; - } - } - - if (different) { + if (!permissions.equals(toApply)) { setPermissions(toApply); } } @@ -79,11 +76,6 @@ public class PermissionData { } public Tristate getPermissionValue(@NonNull String permission) { - Tristate t = calculator.getPermissionValue(permission); - if (t != Tristate.UNDEFINED) { - return Tristate.fromBoolean(t.asBoolean()); - } else { - return Tristate.UNDEFINED; - } + return calculator.getPermissionValue(permission); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/caching/UserData.java b/common/src/main/java/me/lucko/luckperms/common/caching/UserData.java index 0fd7380f..a95ec2c7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/caching/UserData.java +++ b/common/src/main/java/me/lucko/luckperms/common/caching/UserData.java @@ -31,28 +31,72 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +/** + * Holds an easily accessible cache of a user's data in a number of contexts + */ @RequiredArgsConstructor public class UserData { + + /** + * The user whom this data instance is representing + */ private final User user; + + /** + * A provider of {@link me.lucko.luckperms.common.calculators.PermissionCalculator}s for the instance + */ private final CalculatorFactory calculatorFactory; private final Map permission = new ConcurrentHashMap<>(); private final Map meta = new ConcurrentHashMap<>(); + /** + * Gets PermissionData from the cache, given a specified context. + * If the data is not cached, it is calculated. Therefore, this call could be costly. + * @param contexts the contexts to get the permission data in + * @return a permission data instance + */ public PermissionData getPermissionData(Contexts contexts) { return permission.computeIfAbsent(contexts, this::calculatePermissions); } + /** + * Gets MetaData from the cache, given a specified context. + * If the data is not cached, it is calculated. Therefore, this call could be costly. + * @param contexts the contexts to get the permission data in + * @return a meta data instance + */ public MetaData getMetaData(Contexts contexts) { return meta.computeIfAbsent(contexts, this::calculateMeta); } + /** + * Calculates permission data, bypassing the cache. + * @param contexts the contexts to get permission data in + * @return a permission data instance + */ public PermissionData calculatePermissions(Contexts contexts) { PermissionData data = new PermissionData(contexts, user, calculatorFactory); data.setPermissions(user.exportNodes(contexts, true)); return data; } + /** + * Calculates meta data, bypassing the cache. + * @param contexts the contexts to get meta data in + * @return a meta data instance + */ + public MetaData calculateMeta(Contexts contexts) { + MetaData data = new MetaData(contexts); + data.loadMeta(user.getAllNodes(null, contexts)); + return data; + } + + /** + * Calculates permission data and stores it in the cache. If there is already data cached for the given contexts, + * and if the resultant output is different, the cached value is updated. + * @param contexts the contexts to recalculate in. + */ public void recalculatePermissions(Contexts contexts) { permission.compute(contexts, (c, data) -> { if (data == null) { @@ -64,16 +108,11 @@ public class UserData { }); } - public void recalculatePermissions() { - permission.keySet().forEach(this::recalculatePermissions); - } - - public MetaData calculateMeta(Contexts contexts) { - MetaData data = new MetaData(contexts); - data.loadMeta(user.getAllNodes(null, contexts)); - return data; - } - + /** + * Calculates meta data and stores it in the cache. If there is already data cached for the given contexts, + * and if the resultant output is different, the cached value is updated. + * @param contexts the contexts to recalculate in. + */ public void recalculateMeta(Contexts contexts) { meta.compute(contexts, (c, data) -> { if (data == null) { @@ -85,14 +124,33 @@ public class UserData { }); } + /** + * Calls {@link #recalculatePermissions(Contexts)} for all current loaded contexts + */ + public void recalculatePermissions() { + permission.keySet().forEach(this::recalculatePermissions); + } + + /** + * Calls {@link #recalculateMeta(Contexts)} for all current loaded contexts + */ public void recalculateMeta() { meta.keySet().forEach(this::recalculateMeta); } + /** + * Calls {@link #preCalculate(Contexts)} for the given contexts + * @param contexts a set of contexts + */ public void preCalculate(Set contexts) { contexts.forEach(this::preCalculate); } + /** + * Ensures that PermissionData and MetaData is cached for a context. If the cache does not contain any data for the + * context, it will be calculated and saved. + * @param contexts the contexts to pre-calculate for + */ public void preCalculate(Contexts contexts) { getPermissionData(contexts); getMetaData(contexts); diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/MapProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculators/MapProcessor.java index 35cf624c..085cd172 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculators/MapProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculators/MapProcessor.java @@ -36,10 +36,7 @@ public class MapProcessor implements PermissionProcessor { @Override public Tristate hasPermission(String permission) { - if (map.containsKey(permission)) { - return Tristate.fromBoolean(map.get(permission)); - } - - return Tristate.UNDEFINED; + Boolean b = map.get(permission); + return b == null ? Tristate.UNDEFINED : Tristate.fromBoolean(b); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/calculators/WildcardProcessor.java b/common/src/main/java/me/lucko/luckperms/common/calculators/WildcardProcessor.java index ab2ad0de..12736ba1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/calculators/WildcardProcessor.java +++ b/common/src/main/java/me/lucko/luckperms/common/calculators/WildcardProcessor.java @@ -38,43 +38,31 @@ public class WildcardProcessor implements PermissionProcessor { public Tristate hasPermission(String permission) { String node = permission; - while (node.contains(".")) { + while (true) { int endIndex = node.lastIndexOf('.'); if (endIndex == -1) { break; } node = node.substring(0, endIndex); - if (!isEmpty(node)) { - if (map.containsKey(node + ".*")) { - return Tristate.fromBoolean(map.get(node + ".*")); + if (!node.isEmpty()) { + Boolean b = map.get(node + ".*"); + if (b != null) { + return Tristate.fromBoolean(b); } } } - if (map.containsKey("'*'")) { - return Tristate.fromBoolean(map.get("'*'")); + Boolean b = map.get("'*'"); + if (b != null) { + return Tristate.fromBoolean(b); } - if (map.containsKey("*")) { - return Tristate.fromBoolean(map.get("*")); + b = map.get("*"); + if (b != null) { + return Tristate.fromBoolean(b); } return Tristate.UNDEFINED; } - - private static boolean isEmpty(String s) { - if (s.equals("")) { - return true; - } - - char[] chars = s.toCharArray(); - for (char c : chars) { - if (c != '.') { - return false; - } - } - - return true; - } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/DefaultsProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/DefaultsProcessor.java index 12fc7e98..deb60ce9 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/DefaultsProcessor.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/DefaultsProcessor.java @@ -41,12 +41,12 @@ public class DefaultsProcessor implements PermissionProcessor { public me.lucko.luckperms.api.Tristate hasPermission(String permission) { Tristate t = service.getUserSubjects().getDefaults().getPermissionValue(contexts, permission); if (t != Tristate.UNDEFINED) { - return convertTristate(Tristate.fromBoolean(t.asBoolean())); + return convertTristate(t); } Tristate t2 = service.getDefaults().getPermissionValue(contexts, permission); if (t2 != Tristate.UNDEFINED) { - return convertTristate(Tristate.fromBoolean(t.asBoolean())); + return convertTristate(t); } return me.lucko.luckperms.api.Tristate.UNDEFINED; diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeWildcardProcessor.java b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeWildcardProcessor.java index dec08ae2..5213b3f0 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeWildcardProcessor.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/calculators/SpongeWildcardProcessor.java @@ -24,6 +24,7 @@ package me.lucko.luckperms.sponge.calculators; import lombok.AllArgsConstructor; import lombok.Getter; +import me.lucko.luckperms.api.Tristate; import me.lucko.luckperms.common.calculators.PermissionProcessor; import java.util.Map; @@ -35,46 +36,24 @@ public class SpongeWildcardProcessor implements PermissionProcessor { private final Map map; @Override - public me.lucko.luckperms.api.Tristate hasPermission(String permission) { + public Tristate hasPermission(String permission) { String node = permission; - while (node.contains(".")) { + while (true) { int endIndex = node.lastIndexOf('.'); if (endIndex == -1) { break; } node = node.substring(0, endIndex); - if (!isEmpty(node)) { - if (map.containsKey(node)) { - return me.lucko.luckperms.api.Tristate.fromBoolean(map.get(node)); + if (!node.isEmpty()) { + Boolean b = map.get(node); + if (b != null) { + return Tristate.fromBoolean(b); } } } - if (map.containsKey("'*'")) { - return me.lucko.luckperms.api.Tristate.fromBoolean(map.get("'*'")); - } - - if (map.containsKey("*")) { - return me.lucko.luckperms.api.Tristate.fromBoolean(map.get("*")); - } - - return me.lucko.luckperms.api.Tristate.UNDEFINED; - } - - private static boolean isEmpty(String s) { - if (s.equals("")) { - return true; - } - - char[] chars = s.toCharArray(); - for (char c : chars) { - if (c != '.') { - return false; - } - } - - return true; + return Tristate.UNDEFINED; } } diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/contexts/SpongeCalculatorLink.java b/sponge/src/main/java/me/lucko/luckperms/sponge/contexts/SpongeCalculatorLink.java index 84e17d54..34a9084c 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/contexts/SpongeCalculatorLink.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/contexts/SpongeCalculatorLink.java @@ -24,12 +24,12 @@ package me.lucko.luckperms.sponge.contexts; import lombok.AllArgsConstructor; import me.lucko.luckperms.api.context.ContextCalculator; +import me.lucko.luckperms.sponge.service.LuckPermsService; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.permission.Subject; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; @AllArgsConstructor public class SpongeCalculatorLink extends ContextCalculator { @@ -37,10 +37,11 @@ public class SpongeCalculatorLink extends ContextCalculator { @Override public Map giveApplicableContext(Subject subject, Map accumulator) { - Set contexts = accumulator.entrySet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet()); + Set contexts = LuckPermsService.convertContexts(accumulator); calculator.accumulateContexts(subject, contexts); - contexts.forEach(c -> accumulator.put(c.getKey(), c.getValue())); + accumulator.clear(); + accumulator.putAll(LuckPermsService.convertContexts(contexts)); return accumulator; }