Implement proper meta inheritance handling
This commit is contained in:
parent
8d5398b5a3
commit
d320679f69
@ -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.event.PostLoginEvent;
|
||||||
import net.md_5.bungee.api.plugin.Listener;
|
import net.md_5.bungee.api.plugin.Listener;
|
||||||
import net.md_5.bungee.event.EventHandler;
|
import net.md_5.bungee.event.EventHandler;
|
||||||
|
import net.md_5.bungee.event.EventPriority;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
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());
|
e.setHasPermission(user.getUserData().getPermissionData(contexts).getPermissionValue(e.getPermission()).asBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onPlayerLogin(LoginEvent e) {
|
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.
|
/* 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
|
This means that a player will have the same UUID across the network, even if parts of the network are running in
|
||||||
|
@ -24,142 +24,67 @@ package me.lucko.luckperms.common.caching;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
import com.google.common.collect.ImmutableSortedMap;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.Getter;
|
||||||
import me.lucko.luckperms.api.Contexts;
|
import lombok.NoArgsConstructor;
|
||||||
import me.lucko.luckperms.api.LocalizedNode;
|
|
||||||
import me.lucko.luckperms.api.Node;
|
|
||||||
import me.lucko.luckperms.api.caching.MetaData;
|
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
|
* Holds a user's cached meta for a given context
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor
|
@NoArgsConstructor
|
||||||
public class MetaCache implements MetaData {
|
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());
|
@Getter
|
||||||
private final SortedMap<Integer, String> suffixes = new TreeMap<>(Comparator.reverseOrder());
|
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) {
|
@Getter
|
||||||
invalidateCache();
|
private SortedMap<Integer, String> suffixes = ImmutableSortedMap.of();
|
||||||
|
|
||||||
MutableContextSet contexts = MutableContextSet.fromSet(this.contexts.getContexts());
|
public void loadMeta(MetaHolder meta) {
|
||||||
String server = contexts.getValues("server").stream().findAny().orElse(null);
|
lock.writeLock().lock();
|
||||||
String world = contexts.getValues("world").stream().findAny().orElse(null);
|
try {
|
||||||
contexts.removeAll("server");
|
this.meta = ImmutableMap.copyOf(meta.getMeta());
|
||||||
contexts.removeAll("world");
|
this.prefixes = ImmutableSortedMap.copyOfSorted(meta.getPrefixes());
|
||||||
|
this.suffixes = ImmutableSortedMap.copyOfSorted(meta.getSuffixes());
|
||||||
for (LocalizedNode ln : nodes) {
|
} finally {
|
||||||
Node n = ln.getNode();
|
lock.writeLock().unlock();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPrefix() {
|
public String getPrefix() {
|
||||||
synchronized (prefixes) {
|
lock.readLock().lock();
|
||||||
|
try {
|
||||||
if (prefixes.isEmpty()) {
|
if (prefixes.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefixes.get(prefixes.firstKey());
|
return prefixes.get(prefixes.firstKey());
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSuffix() {
|
public String getSuffix() {
|
||||||
synchronized (suffixes) {
|
lock.readLock().lock();
|
||||||
|
try {
|
||||||
if (suffixes.isEmpty()) {
|
if (suffixes.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return suffixes.get(suffixes.firstKey());
|
return suffixes.get(suffixes.firstKey());
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
}
|
@ -78,7 +78,7 @@ public class UserCache implements UserData {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<MetaCache> reload(Contexts contexts, MetaCache oldData) {
|
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);
|
return Futures.immediateFuture(oldData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -102,8 +102,8 @@ public class UserCache implements UserData {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MetaCache calculateMeta(@NonNull Contexts contexts) {
|
public MetaCache calculateMeta(@NonNull Contexts contexts) {
|
||||||
MetaCache data = new MetaCache(contexts);
|
MetaCache data = new MetaCache();
|
||||||
data.loadMeta(user.getAllNodes(null, contexts));
|
data.loadMeta(user.accumulateMeta(null, null, contexts));
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ import me.lucko.luckperms.api.event.events.*;
|
|||||||
import me.lucko.luckperms.common.LuckPermsPlugin;
|
import me.lucko.luckperms.common.LuckPermsPlugin;
|
||||||
import me.lucko.luckperms.common.api.internal.GroupLink;
|
import me.lucko.luckperms.common.api.internal.GroupLink;
|
||||||
import me.lucko.luckperms.common.api.internal.PermissionHolderLink;
|
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.commands.Util;
|
||||||
import me.lucko.luckperms.common.groups.Group;
|
import me.lucko.luckperms.common.groups.Group;
|
||||||
import me.lucko.luckperms.common.utils.Cache;
|
import me.lucko.luckperms.common.utils.Cache;
|
||||||
@ -308,6 +309,98 @@ public abstract class PermissionHolder {
|
|||||||
return all;
|
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
|
* Gets all of the nodes that this holder has (and inherits), given the context
|
||||||
* @param context the context for this request
|
* @param context the context for this request
|
||||||
|
Loading…
Reference in New Issue
Block a user