mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-06 19:09:31 +01:00
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.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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user