Implement proper meta inheritance handling

This commit is contained in:
Luck 2016-11-07 17:32:17 +00:00
parent 8d5398b5a3
commit d320679f69
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
5 changed files with 169 additions and 109 deletions

View File

@ -37,6 +37,7 @@ import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@ -83,7 +84,7 @@ public class BungeeListener extends AbstractListener implements Listener {
e.setHasPermission(user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission()).asBoolean());
}
@EventHandler
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(LoginEvent e) {
/* Delay the login here, as we want to cache UUID data before the player is connected to a backend bukkit server.
This means that a player will have the same UUID across the network, even if parts of the network are running in

View File

@ -24,142 +24,67 @@ package me.lucko.luckperms.common.caching;
import com.google.common.collect.ImmutableMap;
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 lombok.Getter;
import lombok.NoArgsConstructor;
import me.lucko.luckperms.api.caching.MetaData;
import me.lucko.luckperms.api.context.MutableContextSet;
import java.util.*;
import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Holds a user's cached meta for a given context
*/
@RequiredArgsConstructor
@NoArgsConstructor
public class MetaCache implements MetaData {
private final Contexts contexts;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final SortedMap<Integer, String> prefixes = new TreeMap<>(Comparator.reverseOrder());
private final SortedMap<Integer, String> suffixes = new TreeMap<>(Comparator.reverseOrder());
@Getter
private Map<String, String> meta = ImmutableMap.of();
private final Map<String, String> meta = new HashMap<>();
@Getter
private SortedMap<Integer, String> prefixes = ImmutableSortedMap.of();
public void loadMeta(SortedSet<LocalizedNode> nodes) {
invalidateCache();
@Getter
private SortedMap<Integer, String> suffixes = ImmutableSortedMap.of();
MutableContextSet contexts = MutableContextSet.fromSet(this.contexts.getContexts());
String server = contexts.getValues("server").stream().findAny().orElse(null);
String world = contexts.getValues("world").stream().findAny().orElse(null);
contexts.removeAll("server");
contexts.removeAll("world");
for (LocalizedNode ln : nodes) {
Node n = ln.getNode();
if (!n.getValue()) {
continue;
}
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) {
continue;
}
if (!n.shouldApplyOnServer(server, this.contexts.isIncludeGlobal(), false)) {
continue;
}
if (!n.shouldApplyOnWorld(world, this.contexts.isIncludeGlobalWorld(), false)) {
continue;
}
if (!n.shouldApplyWithContext(contexts, false)) {
continue;
}
if (n.isPrefix()) {
Map.Entry<Integer, String> value = n.getPrefix();
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();
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());
}
}
}
}
}
private void invalidateCache() {
synchronized (meta) {
meta.clear();
}
synchronized (prefixes) {
prefixes.clear();
}
synchronized (suffixes) {
suffixes.clear();
}
}
@Override
public Map<String, String> getMeta() {
synchronized (meta) {
return ImmutableMap.copyOf(meta);
}
}
@Override
public SortedMap<Integer, String> getPrefixes() {
synchronized (prefixes) {
return ImmutableSortedMap.copyOfSorted(prefixes);
}
}
@Override
public SortedMap<Integer, String> getSuffixes() {
synchronized (suffixes) {
return ImmutableSortedMap.copyOfSorted(suffixes);
public void loadMeta(MetaHolder meta) {
lock.writeLock().lock();
try {
this.meta = ImmutableMap.copyOf(meta.getMeta());
this.prefixes = ImmutableSortedMap.copyOfSorted(meta.getPrefixes());
this.suffixes = ImmutableSortedMap.copyOfSorted(meta.getSuffixes());
} finally {
lock.writeLock().unlock();
}
}
@Override
public String getPrefix() {
synchronized (prefixes) {
lock.readLock().lock();
try {
if (prefixes.isEmpty()) {
return null;
}
return prefixes.get(prefixes.firstKey());
} finally {
lock.readLock().unlock();
}
}
@Override
public String getSuffix() {
synchronized (suffixes) {
lock.readLock().lock();
try {
if (suffixes.isEmpty()) {
return null;
}
return suffixes.get(suffixes.firstKey());
} finally {
lock.readLock().unlock();
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.caching;
import lombok.Getter;
import lombok.ToString;
import java.util.*;
/**
* Holds temporary mutable meta whilst this object is passed up the inheritance tree to accumulate meta from parents
*/
@Getter
@ToString
public class MetaHolder {
private final Map<String, String> meta = new HashMap<>();
private final SortedMap<Integer, String> prefixes = new TreeMap<>(Comparator.reverseOrder());
private final SortedMap<Integer, String> suffixes = new TreeMap<>(Comparator.reverseOrder());
}

View File

@ -78,7 +78,7 @@ public class UserCache implements UserData {
@Override
public ListenableFuture<MetaCache> reload(Contexts contexts, MetaCache oldData) {
oldData.loadMeta(user.getAllNodes(null, contexts));
oldData.loadMeta(user.accumulateMeta(null, null, contexts));
return Futures.immediateFuture(oldData);
}
});
@ -102,8 +102,8 @@ public class UserCache implements UserData {
@Override
public MetaCache calculateMeta(@NonNull Contexts contexts) {
MetaCache data = new MetaCache(contexts);
data.loadMeta(user.getAllNodes(null, contexts));
MetaCache data = new MetaCache();
data.loadMeta(user.accumulateMeta(null, null, contexts));
return data;
}

View File

@ -38,6 +38,7 @@ import me.lucko.luckperms.api.event.events.*;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.api.internal.GroupLink;
import me.lucko.luckperms.common.api.internal.PermissionHolderLink;
import me.lucko.luckperms.common.caching.MetaHolder;
import me.lucko.luckperms.common.commands.Util;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.utils.Cache;
@ -308,6 +309,98 @@ public abstract class PermissionHolder {
return all;
}
private static void accumulateMetaNode(Node n, MetaHolder holder) {
if (n.isPrefix()) {
Map.Entry<Integer, String> value = n.getPrefix();
if (!holder.getPrefixes().containsKey(value.getKey())) {
holder.getPrefixes().put(value.getKey(), value.getValue());
}
}
if (n.isSuffix()) {
Map.Entry<Integer, String> value = n.getSuffix();
if (!holder.getSuffixes().containsKey(value.getKey())) {
holder.getSuffixes().put(value.getKey(), value.getValue());
}
}
if (n.isMeta()) {
Map.Entry<String, String> meta = n.getMeta();
if (!holder.getMeta().containsKey(meta.getKey())) {
holder.getMeta().put(meta.getKey(), meta.getValue());
}
}
}
public MetaHolder accumulateMeta(MetaHolder holder, List<String> excludedGroups, Contexts context) {
if (holder == null) {
holder = new MetaHolder();
}
if (excludedGroups == null) {
excludedGroups = new ArrayList<>();
}
excludedGroups.add(getObjectName().toLowerCase());
MutableContextSet contexts = MutableContextSet.fromSet(context.getContexts());
String server = contexts.getValues("server").stream().findAny().orElse(null);
String world = contexts.getValues("world").stream().findAny().orElse(null);
contexts.removeAll("server");
contexts.removeAll("world");
SortedSet<LocalizedNode> all = new TreeSet<>((SortedSet<LocalizedNode>) getPermissions(true));
for (LocalizedNode ln : all) {
Node n = ln.getNode();
if (!n.getValue()) continue;
if (!n.isMeta() && !n.isPrefix() && !n.isSuffix()) continue;
if (!n.shouldApplyOnServer(server, context.isIncludeGlobal(), false)) continue;
if (!n.shouldApplyOnWorld(world, context.isIncludeGlobalWorld(), false)) continue;
if (!n.shouldApplyWithContext(contexts, false)) continue;
accumulateMetaNode(n, holder);
}
Set<Node> parents = all.stream()
.map(LocalizedNode::getNode)
.filter(Node::getValue)
.filter(Node::isGroupNode)
.collect(Collectors.toSet());
parents.removeIf(node ->
!node.shouldApplyOnServer(server, context.isApplyGlobalGroups(), plugin.getConfiguration().isApplyingRegex()) ||
!node.shouldApplyOnWorld(world, context.isApplyGlobalWorldGroups(), plugin.getConfiguration().isApplyingRegex()) ||
!node.shouldApplyWithContext(contexts, false)
);
TreeSet<Map.Entry<Integer, Node>> sortedParents = new TreeSet<>(Util.getMetaComparator().reversed());
Map<String, Integer> weights = plugin.getConfiguration().getGroupWeights();
for (Node node : parents) {
if (weights.containsKey(node.getGroupName().toLowerCase())) {
sortedParents.add(Maps.immutableEntry(weights.get(node.getGroupName().toLowerCase()), node));
} else {
sortedParents.add(Maps.immutableEntry(0, node));
}
}
for (Map.Entry<Integer, Node> e : sortedParents) {
Node parent = e.getValue();
Group group = plugin.getGroupManager().get(parent.getGroupName());
if (group == null) {
continue;
}
if (excludedGroups.contains(group.getObjectName().toLowerCase())) {
continue;
}
group.accumulateMeta(holder, excludedGroups, context);
}
return holder;
}
/**
* Gets all of the nodes that this holder has (and inherits), given the context
* @param context the context for this request