Cleanup permission calculation

This commit is contained in:
Luck 2016-10-18 20:48:59 +01:00
parent 7b655d12df
commit d113a92ce5
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
12 changed files with 181 additions and 131 deletions

View File

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

View File

@ -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<Player> {
@ -39,7 +40,7 @@ public class AutoOPListener implements ContextListener<Player> {
}
Map<String, Boolean> 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);
}

View File

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

View File

@ -97,11 +97,9 @@ public class DefaultsProvider {
public Tristate hasDefault(String permission, boolean isOp) {
Map<String, Boolean> 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

View File

@ -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<Integer, String> prefixes = new TreeMap<>(Comparator.reverseOrder());
private final SortedMap<Integer, String> suffixes = new TreeMap<>(Comparator.reverseOrder());
@Getter
private String suffix = null;
private Map<String, String> meta = new ConcurrentHashMap<>();
private final Map<String, String> meta = new HashMap<>();
public void loadMeta(SortedSet<LocalizedNode> 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<Integer, String> 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<Integer, String> 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<String, String> 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() {
synchronized (meta) {
meta.clear();
prefix = null;
suffix = null;
}
synchronized (prefixes) {
prefixes.clear();
}
synchronized (suffixes) {
suffixes.clear();
}
}
public Map<String, String> getMeta() {
synchronized (meta) {
return ImmutableMap.copyOf(meta);
}
}
public SortedMap<Integer, String> getPrefixes() {
synchronized (prefixes) {
return ImmutableSortedMap.copyOfSorted(prefixes);
}
}
public SortedMap<Integer, String> 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());
}
}
}

View File

@ -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<String, Boolean> 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<String, Boolean> toApply) {
boolean different = false;
if (toApply.size() != permissions.size()) {
different = true;
} else {
for (Map.Entry<String, Boolean> 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);
}
}

View File

@ -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<Contexts, PermissionData> permission = new ConcurrentHashMap<>();
private final Map<Contexts, MetaData> 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) {
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);

View File

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

View File

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

View File

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

View File

@ -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<String, Boolean> 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;
}
}

View File

@ -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<Subject> {
@ -37,10 +37,11 @@ public class SpongeCalculatorLink extends ContextCalculator<Subject> {
@Override
public Map<String, String> giveApplicableContext(Subject subject, Map<String, String> accumulator) {
Set<Context> contexts = accumulator.entrySet().stream().map(e -> new Context(e.getKey(), e.getValue())).collect(Collectors.toSet());
Set<Context> 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;
}