extract WeightCache out of PermissionHolder

This commit is contained in:
Luck 2018-02-10 21:27:02 +00:00
parent 7e7268bb5a
commit 63f8e8849f
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
3 changed files with 130 additions and 56 deletions

View File

@ -57,7 +57,11 @@ import javax.annotation.Nullable;
/** /**
* A map of nodes held by a {@link PermissionHolder}. * A map of nodes held by a {@link PermissionHolder}.
* *
* Each holder has two of these maps, one for enduring and transient nodes. * <p>Permissions are stored in Multimaps, with the context of the node being the key, and the actual Node object being
* the value. The keys (context sets) are ordered according to their weight {@link ContextSetComparator}, and the values
* are ordered according to the priority of the node, according to {@link NodeComparator}.</p>
*
* <p>Each holder has two of these maps, one for enduring and transient nodes.</p>
*/ */
public final class NodeMap { public final class NodeMap {

View File

@ -44,11 +44,9 @@ import me.lucko.luckperms.common.caching.HolderCachedData;
import me.lucko.luckperms.common.caching.handlers.StateListener; import me.lucko.luckperms.common.caching.handlers.StateListener;
import me.lucko.luckperms.common.caching.type.MetaAccumulator; import me.lucko.luckperms.common.caching.type.MetaAccumulator;
import me.lucko.luckperms.common.config.ConfigKeys; import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.contexts.ContextSetComparator;
import me.lucko.luckperms.common.node.ImmutableLocalizedNode; import me.lucko.luckperms.common.node.ImmutableLocalizedNode;
import me.lucko.luckperms.common.node.InheritanceInfo; import me.lucko.luckperms.common.node.InheritanceInfo;
import me.lucko.luckperms.common.node.MetaType; import me.lucko.luckperms.common.node.MetaType;
import me.lucko.luckperms.common.node.NodeComparator;
import me.lucko.luckperms.common.node.NodeFactory; import me.lucko.luckperms.common.node.NodeFactory;
import me.lucko.luckperms.common.node.NodeTools; import me.lucko.luckperms.common.node.NodeTools;
import me.lucko.luckperms.common.node.NodeWithContextComparator; import me.lucko.luckperms.common.node.NodeWithContextComparator;
@ -79,63 +77,74 @@ import java.util.stream.Collectors;
/** /**
* Represents an object that can hold permissions, (a user or group) * Represents an object that can hold permissions, (a user or group)
* *
* <p>Permissions are stored in Multimaps, with the context of the node being the key, and the actual Node object being * <p>Data is stored in {@link NodeMap}s. A holder has two of these, one for
* the value. The keys (context sets) are ordered according to their weight {@link ContextSetComparator}, and the values * enduring nodes and one for transient nodes.</p>
* are ordered according to the priority of the node, according to {@link NodeComparator}.</p>
* *
* <p>This class also provides a number of methods to perform inheritance lookups. These lookup methods initially use * <p>This class provides a number of methods to perform inheritance lookups.
* Lists of nodes populated with the inheritance tree. Nodes at the start of this list have priority over nodes at the * These lookup methods initially use Lists of nodes populated with the
* end. Nodes higher up the tree appear at the end of these lists. In order to remove duplicate elements, the lists are * inheritance tree. Nodes at the start of this list have priority over nodes at
* flattened using the methods in {@link NodeTools}. This is significantly faster than trying to prevent duplicates * the end. Nodes higher up the tree appear at the end of these lists. In order
* throughout the process of accumulation, and reduces the need for too much caching.</p> * to remove duplicate elements, the lists are flattened using the methods in
* {@link NodeTools}. This is significantly faster than trying to prevent
* duplicates throughout the process of accumulation, and reduces the need for
* too much caching.</p>
* *
* <p>Cached state is avoided in these instances to cut down on memory footprint. The nodes are stored indexed to the * <p>Cached state is avoided in these instances to cut down on memory
* contexts they apply in, so doing context specific querying should be fast. Caching would be ineffective here, due to * footprint. The nodes are stored indexed to the contexts they apply in, so
* the potentially vast amount of contexts being used by nodes, and the potential for very large inheritance trees.</p> * doing context specific querying should be fast. Caching would be ineffective
* here, due to the potentially vast amount of contexts being used by nodes,
* and the potential for very large inheritance trees.</p>
*/ */
public abstract class PermissionHolder { public abstract class PermissionHolder {
/** /**
* The name of this PermissionHolder object. * The name of this object.
* *
* <p>Used to prevent circular inheritance issues.</p> * <p>Used as a base for identifying permission holding objects. Also acts
* as a method for preventing circular inheritance issues.</p>
* *
* <p>For users, this value is a String representation of their {@link User#getUuid()}. For groups, it's just the * @see User#getUuid()
* {@link Group#getName()}.</p> * @see Group#getName()
* @see #getObjectName()
*/ */
private final String objectName; private final String objectName;
/** /**
* Reference to the main plugin instance * Reference to the main plugin instance
* @see #getPlugin()
*/ */
private final LuckPermsPlugin plugin; private final LuckPermsPlugin plugin;
/** /**
* The holders persistent nodes. * The holders persistent nodes.
*
* <p>These (unlike transient nodes) are saved to the storage backing.</p>
*
* @see #getEnduringData()
*/ */
private final NodeMap enduringNodes = new NodeMap(this); private final NodeMap enduringNodes = new NodeMap(this);
/** /**
* The holders transient nodes. * The holders transient nodes.
* *
* <p>These are nodes which are never stored or persisted to a file, and only * <p>These are nodes which are never stored or persisted to a file, and
* last until the end of the objects lifetime. (for a group, that's when the server stops, and for a user, it's when * only last until the end of the objects lifetime. (for a group, that's
* they log out, or get unloaded.)</p> * when the server stops, and for a user, it's when they log out, or get
* unloaded.)</p>
*
* @see #getTransientData()
*/ */
private final NodeMap transientNodes = new NodeMap(this); private final NodeMap transientNodes = new NodeMap(this);
/** /**
* Caches the holders weight lookup * Caches the holders weight
* @see #getWeight()
*/ */
private final Cache<OptionalInt> weightCache = new Cache<OptionalInt>() { private final Cache<OptionalInt> weightCache = WeightCache.getFor(this);
@Override
protected OptionalInt supply() {
return calculateWeight();
}
};
/** /**
* Lock used by Storage implementations to prevent concurrent read/writes * Lock used by Storage implementations to prevent concurrent read/writes
* @see #getIoLock()
*/ */
private final Lock ioLock = new ReentrantLock(); private final Lock ioLock = new ReentrantLock();
@ -1003,34 +1012,6 @@ public abstract class PermissionHolder {
return this.weightCache.get(); return this.weightCache.get();
} }
private OptionalInt calculateWeight() {
if (this.getType().isUser()) return OptionalInt.empty();
boolean seen = false;
int best = 0;
for (Node n : getOwnNodes(ImmutableContextSet.empty())) {
Integer weight = NodeFactory.parseWeightNode(n.getPermission());
if (weight == null) {
continue;
}
if (!seen || weight > best) {
seen = true;
best = weight;
}
}
OptionalInt weight = seen ? OptionalInt.of(best) : OptionalInt.empty();
if (!weight.isPresent()) {
Integer w = this.plugin.getConfiguration().get(ConfigKeys.GROUP_WEIGHTS).get(getObjectName().toLowerCase());
if (w != null) {
weight = OptionalInt.of(w);
}
}
return weight;
}
public Set<HolderReference> getGroupReferences() { public Set<HolderReference> getGroupReferences() {
return getOwnNodes().stream() return getOwnNodes().stream()
.filter(Node::isGroupNode) .filter(Node::isGroupNode)

View File

@ -0,0 +1,89 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.model;
import me.lucko.luckperms.api.Node;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import me.lucko.luckperms.common.buffers.Cache;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.node.NodeFactory;
import java.util.Map;
import java.util.OptionalInt;
/**
* Cache instance to supply the weight of a {@link PermissionHolder}.
*/
public class WeightCache extends Cache<OptionalInt> {
private static final Cache<OptionalInt> NULL = new Cache<OptionalInt>() {
@Override
protected OptionalInt supply() {
return OptionalInt.empty();
}
};
public static Cache<OptionalInt> getFor(PermissionHolder holder) {
if (holder.getType().isUser()) {
return NULL;
}
return new WeightCache(((Group) holder));
}
private final Group group;
private WeightCache(Group group) {
this.group = group;
}
@Override
protected OptionalInt supply() {
boolean seen = false;
int best = 0;
for (Node n : this.group.getOwnNodes(ImmutableContextSet.empty())) {
Integer weight = NodeFactory.parseWeightNode(n.getPermission());
if (weight == null) {
continue;
}
if (!seen || weight > best) {
seen = true;
best = weight;
}
}
OptionalInt weight = seen ? OptionalInt.of(best) : OptionalInt.empty();
if (!weight.isPresent()) {
Map<String, Integer> configWeights = this.group.getPlugin().getConfiguration().get(ConfigKeys.GROUP_WEIGHTS);
Integer w = configWeights.get(this.group.getObjectName().toLowerCase());
if (w != null) {
weight = OptionalInt.of(w);
}
}
return weight;
}
}